特征組合之 XGBoost + LR


一、特征組合

廣告點擊率預估、推薦系統等業務場景涉及到的特征通常都是高維、稀疏的,並且樣本量巨大,模型通常采用速度較快的LR,然而LR算法學習能力有限,因此要想得到好的預測結果,需要前期做大量的特征工程,工程師通常需要花費大量精力去篩選特征、做特征與處理,即便這樣,最終的效果提升可能非常有限。

樹模型算法天然具有特征篩選的功能,其通過熵、信息增益、基尼指數等方法,在每次分裂時選取最優的分裂節點。因此,當樹模型訓練完畢后,從樹的根節點到葉子節點都是篩選出來的局部最優特征。

於是很自然的想法就是,通過樹模型的特征篩選功能篩選一些局部最優的特征組合,然后將組合特征輸入到LR算法,這樣就可以提升LR算法的擬合能力。

二、樹模型+LR

2014年Facebook 發表論文 Practical Lessons from Predicting Clicks on Ads at Facebook,這篇文章介紹了通過GBDT構造組合特征,然后通過LR對組合特征進行訓練,從而達到對預測樣本進行分類的目的。隨后在Kaggle競賽中該方法得到驗證 ,GBDT與LR也引起了人們的廣泛關注。 

 

圖1 GBDT+LR組合方法圖

圖1所示為FaceBook的paper中GBDT與LR融合的方法,圖中x為輸入特征,經過兩棵樹后走到葉子節點。圖中左邊的樹有三個葉子節點,右邊的樹有兩個葉子節點,因此對於輸入x,假設其在左子樹落在第一個節點,在右子樹落在第二個節點,那么在左子樹的one-hot編碼為[1,0,0],在右子樹的one-hot編碼為[0,1],最終的特征為兩個one-hot編碼的組合[1,0,0,0,1]。最后將轉化后的特征輸入到線性分類器,即可訓練的到一個基於組合特征的線性模型。

在進行特征轉化的時候,GBDT模型中所包含的樹的棵樹即為后面組合特征的數量,每一個組合特征的向量長度不等,該長度取決於所在樹的葉子節點數量。舉例來說,假設訓練得到100棵樹之后,就可以得到100個組合特征。

三、XGBoost+LR

XGBoost是一個高效的梯度提升樹的實現框架,並且廣泛用於工業界及各種比賽。XGBoost提供了一個借口,如圖2所示,其中輸入X為樣本特征,ntree_limit為用於預測的樹的棵樹。其返回值為[n_sample, n_trees]的矩陣,每一行代表一個樣本,每一列代表一棵樹,矩陣的值代表相應樣本在相應樹的所在節點的編號,舉例來說,假設模型共有四棵樹,返回值第一列為[1,0,3,2],則“1”代表該樣本在第一顆樹的第一個葉子節點,在第三棵樹的的第三個葉子節點,在第四棵樹的第二個葉子節點。

 圖2 XGBoost apply函數借口

代碼:

import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn import metrics
import logging
import xgboost as xgb
import time
from sklearn.datasets import load_iris

logging.basicConfig(format='%(asctime)s : %(levelname)s: %(message)s', level=logging.INFO)


def XGBoost_LR(df_train):
    X_train = df_train.values[:, :-1]
    y_train = df_train.values[:, -1]
    X_train_xgb, X_train_lr, y_train_xgb, y_train_lr = train_test_split(X_train, 
                                                y_train, test_size=0.75)
    XGB = xgb.XGBClassifier(n_estimators = 6)

    XGB.fit(X_train_xgb, y_train_xgb)
    logging.info("訓練集特征數據為: \n%s" % X_train_xgb)
    logging.info("訓練集標簽數據為: \n%s" % y_train_xgb)
    logging.info("轉化為葉子節點后的特征為:\n%s" % XGB.apply(X_train_xgb, ntree_limit=0))

    XGB_enc = OneHotEncoder()
    XGB_enc.fit(XGB.apply(X_train_xgb, ntree_limit=0)) # ntree_limit 預測時使用的樹的數量
    XGB_LR = LogisticRegression()
    XGB_LR.fit(XGB_enc.transform(XGB.apply(X_train_lr)), y_train_lr.astype('int'))
    X_predict = XGB_LR.predict_proba(XGB_enc.transform(XGB.apply(X_train)))[:, 1]
    AUC_train = metrics.roc_auc_score(y_train.astype('int'), X_predict)
    logging.info("AUC of train data is %f" % AUC_train)


if __name__ == "__main__":
    start = time.clock()
    #加載數據集
    iris=load_iris()
    df_data = pd.concat([pd.DataFrame(iris.data), pd.DataFrame(iris.target)], axis=1)
    df_data.columns = ["x1","x2", "x3","x4", "y"]
    df_new = df_data[df_data["y"]<2]
    logging.info("Train model begine...")
    XGBoost_LR(df_new)
    end = time.clock()
    logging.info("Program over, time cost is %s" % (end-start))

 

這里使用iris數據集,特征及標簽如下圖所示,這里選取兩類數據用於訓練模型,因此標簽只有0和1。 

圖3 訓練樣本示例

調用apply函數后,打印出轉化為葉子節點的特征:

 圖4 apply函數結果示例

 四、項目案例

這里是我在一個營銷項目中的案例,背景是重疾險潛客識別,利用客戶的多維度特征預測客戶是否會購買重疾險。項目中嘗試了多種算法,包括XGBoost、XGBoost+LR、GBDT、RF+LR,不同算法在數據的表現如下圖所示。圖中橫坐標代表樣本的得分分布,共有10個分段區間,縱坐標代表正樣本(已購重疾險的客戶)在相應區間的數量。該項目本身對樣本的區分度並不高,但是從圖中可以看出,使用XGBoost+LR模型的結果表現明顯優於其他三種方法,其在得分較高的區間的樣本數量較其他方法多,例如[0.9,1.0]這個區間;而在得分較低的區間的樣本數量較少,例如[0.0,0.1]這個區間。直觀上看,XGBoost+LR具有將正樣本向得分高的方向“移動”的作用。

 圖4 不同算法的表現

五、寫在最后

我個人在工作中的感受是樹模型+LR的方法最適用於高緯稀疏數據。樹模型對這樣的稀疏數據容易導致過擬合,舉例來說,假設預測用戶是否會購買蘋果手機,那么“近一周瀏覽蘋果手機的次數”這個特征對於模型的貢獻度極高,大量的正樣本都有瀏覽或者瀏覽次數很多,而未購買的用戶在這個特征上的表現則可能數值極小甚至為0。此時,樹模型只需要將該特征作為一個分叉就可以得到很好的效果,然而當上線使用時發現可能效果並不是那么好,因為有些客戶可能在這個特征表現不明顯,但依然購買了商品。

原因是什么呢?因為樹模型的正則項是樹的深度和葉子節點的數量,上述情況樹模型的深度非常淺,葉子節點相應少,因此樹模型認為自身很完美,但其實已經過擬合了。對於這樣的特征,如果輸入到LR這樣的線性模型,正則項會對貢獻度高的特征的權重進行懲罰,以此來避免過擬合,但是LR的學習能力較差,可能難以達到期望的效果。

綜上,可以結合樹模型的特征組合能力和LR的正則項來處理高維稀疏數據,既可以提高模型的擬合能力,又可以防止過擬合。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM