轉(zhuǎn)載:https://blog.csdn.net/anshuai_aw1/article/details/82983997
關(guān)于Xgboost的知識點很多,本篇博客介紹如何利用Xgboost構(gòu)造新特征社付,且在此基礎(chǔ)上承疲,介紹與LR模型融合的相關(guān)知識點邻耕。
母
一 目錄
二 實踐
2.1 如何獲得樣本落在哪個葉子節(jié)點
2.2 舉例
2.2.1 訓練集準備
2.2.2 RF+LR
2.2.3 GBDT+LR
2.2.4 Xgboost+LR
2.2.5 單獨使用RF,GBDT和Xgboost
2.2.6 結(jié)果對比
三 為什么Xgboost+LR 的融合效果沒有想象的那么好
四 參考文獻
一 原理
為什么要使用LR模型進行融合呢? 這是因為LR(邏輯回歸)算法簡單有效燕鸽,成為工業(yè)界最常用的算法之一兄世。但LR算法是線性模型,不能捕捉到非線性西悉尼啊研,需要大量的特征工程找到特征組合御滩。為了發(fā)現(xiàn)有效的特征組合,F(xiàn)acebook在2014年論文ractical Lessons from Predicting Clicks on Ads at Facebook 介紹通過GBDT (Gradient Boost Decision Tree)+ LR 的方案 (XGBoost 是 GBDT 的后續(xù)發(fā)展)党远。在這篇論文中他們提出一中將Xgboost作為feature transform 的方法削解。隨后在多個Kaggle比賽中,均證明此思路的有效性沟娱。
大概的思想可以描述為如下:先用已有特征訓練Xgboost模型氛驮,然后利用Xgboost模型學習到的樹來構(gòu)造新特征,最后把這些新特征加入原有特征一起訓練模型济似。構(gòu)造的新特征向量是取值0/1的矫废,向量的每個元素對應(yīng)于Xgboost模型中樹的葉子結(jié)點。當一個樣本點通過某棵樹最終落在這棵樹的一個葉子結(jié)點上砰蠢,那么在新特征向量中這個葉子結(jié)點對應(yīng)的元素值為1磷脯,而這棵樹的其他葉子結(jié)點對應(yīng)的元素值為0。新特征向量的長度等于XGBoost模型里所有樹包含的葉子結(jié)點數(shù)之和娩脾。最后將新的特征扔到LR模型進行訓練。
舉例說明打毛。下面的圖中的兩棵樹是GBDT(Xgboost一樣)學習到的柿赊,第一棵樹有3個葉子結(jié)點,而第二棵樹有2個葉子節(jié)點幻枉。對于一個輸入樣本點x碰声,如果它在第一棵樹最后落在其中的第二個葉子結(jié)點,而在第二棵樹里最后落在其中的第一個葉子結(jié)點熬甫。那么通過GBDT獲得的新特征向量為[0, 1, 0, 1, 0]胰挑,其中向量中的前三位對應(yīng)第一棵樹的3個葉子結(jié)點,后兩位對應(yīng)第二棵樹的2個葉子結(jié)點椿肩。
二實踐
2.1 如何獲得樣本落在哪個葉子節(jié)點
在實踐中的關(guān)鍵點是如何獲得每個樣本落在訓練后的每棵樹的哪個葉子結(jié)點上瞻颂。
A、對于Xgboost來說郑象,因為其有sklearn接口和自帶接口贡这,因此有兩種方法可以獲得:
①、sklearn接口厂榛「墙茫可以設(shè)置pre_leaf=True獲得每個樣本在每顆樹上的leaf_Index丽惭。XGBoost官方文檔
②、自帶接口辈双。利用apply()方法可以獲得leaf indices责掏。SKlearn GBDT API
!E韧换衬!此過程需注意: 無論是設(shè)置pre_leaf=True還是利用apply()方法,獲得的都是葉子節(jié)點的 index喜爷,也就是說落在了具體哪顆樹的哪個葉子節(jié)點上冗疮,并非是0/1變量,因此需要自己動手去做 onehot 編碼檩帐。onehot 可以在 sklearn 的預(yù)處理包中調(diào)用即可术幔。
B、對于其它的樹模型湃密,如隨機森林和GBDT诅挑,我們只能使用apply()方法獲得leaf indices。
2.2 舉例
接下來泛源,我們舉例來說明如何利用樹模型拔妥,尤其是Xgboost來構(gòu)建新特征,并且是如何與LR模型進行融合达箍。
本例子對Feature transformations with ensembles of trees進行了改進没龙,使得更加健壯易讀。
2.2.1 訓練集準備
從代碼中缎玫,可以看到硬纤,我們對訓練集X_train又進行了一次切分,生成了訓練集X_train, X_train_lr和測試集y_train, y_train_lr赃磨。特別要注意數(shù)據(jù)集的大小筝家,后續(xù)我們會進行分析。
注意:我們設(shè)定了n_estimator = 10邻辉,這意味著樹模型中只有10顆樹.
import numpy as np
np.random.seed(10)
import matplotlib.pyplot as plt
import xgboost as xgb
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import ( RandomForestClassifier,
GradientBoostingClassifier)
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, roc_auc_score
from scipy.sparse import hstack
X, y = make_classification(n_samples=80000)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
# It is important to train the ensemble of trees on a different subset
# of the training data than the linear regression model to avoid
# overfitting, in particular if the total number of leaves is
# similar to the number of training samples
X_train, X_train_lr, y_train, y_train_lr = train_test_split(
X_train, y_train, test_size=0.5)
n_estimator = 10
'''
X_train為20000*20
X_train_lr為20000*20
y_train為20000*1
y_train_lr為20000*1
y_test 為40000*1
'''
2.2.2 RF+LR
我們首先使用隨機森林進行實驗溪王。這里我們需要對代碼進行一個解讀。
# Supervised transformation based on gradient boosted trees
grd = GradientBoostingClassifier(n_estimators=n_estimator)
grd_enc = OneHotEncoder()
grd_lm = LogisticRegression(solver='lbfgs', max_iter=1000)
grd.fit(X_train, y_train)
grd_enc.fit(grd.apply(X_train)[:, :, 0])
grd_lm.fit(grd_enc.transform(grd.apply(X_train_lr)[:, :, 0]), y_train_lr)
y_pred_grd_lm = grd_lm.predict_proba(
grd_enc.transform(grd.apply(X_test)[:, :, 0]))[:, 1]
fpr_grd_lm, tpr_grd_lm, _ = roc_curve(y_test, y_pred_grd_lm)
print("GBT+LR的AUC為:", roc_auc_score(y_test, y_pred_grd_lm))
2.2.3 GBDT+LR
我們其次使用隨機森林進行實驗值骇。其代碼與隨機森林幾乎一樣莹菱。不再進行代碼解讀。
# Supervised transformation based on gradient boosted trees
grd = GradientBoostingClassifier(n_estimators=n_estimator)
grd_enc = OneHotEncoder()
grd_lm = LogisticRegression(solver='lbfgs', max_iter=1000)
grd.fit(X_train, y_train)
grd_enc.fit(grd.apply(X_train)[:, :, 0])
grd_lm.fit(grd_enc.transform(grd.apply(X_train_lr)[:, :, 0]), y_train_lr)
y_pred_grd_lm = grd_lm.predict_proba(
grd_enc.transform(grd.apply(X_test)[:, :, 0]))[:, 1]
fpr_grd_lm, tpr_grd_lm, _ = roc_curve(y_test, y_pred_grd_lm)
print("GBT+LR的AUC為:", roc_auc_score(y_test, y_pred_grd_lm))
2.2.4 Xgboost+LR
最后吱瘩,我們使用Xgboost進行實驗芒珠。
# Supervised transformation based on xgboost
xgb = xgb.XGBClassifier(nthread=4, #含義:nthread=-1時,使用全部CPU進行并行運算(默認), nthread=1時搅裙,使用1個CPU進行運算皱卓。
learning_rate=0.08, #含義:學習率裹芝,控制每次迭代更新權(quán)重時的步長,默認0.3娜汁。調(diào)參:值越小嫂易,訓練越慢。典型值為0.01-0.2掐禁。
n_estimators=50, #含義:總共迭代的次數(shù)怜械,即決策樹的個數(shù)
max_depth=5, #含義:樹的深度,默認值為6傅事,典型值3-10缕允。調(diào)參:值越大,越容易過擬合蹭越;值越小障本,越容易欠擬合
gamma=0, #含義:懲罰項系數(shù),指定節(jié)點分裂所需的最小損失函數(shù)下降值响鹃。
subsample=0.9, #含義:訓練每棵樹時驾霜,使用的數(shù)據(jù)占全部訓練集的比例。默認值為1买置,典型值為0.5-1粪糙。調(diào)參:防止overfitting。
colsample_bytree=0.5) #訓練每棵樹時忿项,使用的特征占全部特征的比例蓉冈。默認值為1,典型值為0.5-1轩触。調(diào)參:防止overfitting洒擦。
xgb_enc = OneHotEncoder()
xgb_lm = LogisticRegression(solver='lbfgs', max_iter=1000)
xgb.fit(X_train, y_train)
xgb_enc.fit(xgb.apply(X_train))
xgb_lm.fit(xgb_enc.transform(xgb.apply(X_train_lr)), y_train_lr)
y_pred_xgb_lm = xgb_lm.predict_proba(
xgb_enc.transform(xgb.apply(X_test)))[:, 1]
fpr_xgb_lm, tpr_xgb_lm, _ = roc_curve(y_test, y_pred_xgb_lm)
print("xgboost+LR的AUC為:", roc_auc_score(y_test, y_pred_xgb_lm))
在之前的代碼中,我們只是用樹模型構(gòu)造的新特征來訓練LR怕膛。
接下來,我們更近一步秦踪,將新特征與原始的20個特征進行拼接形成新的數(shù)據(jù)集來訓練LR褐捻。
X_train_ext = hstack([xgb_enc.transform(xgb.apply(X_train_lr)), X_train_lr])
X_test_ext = hstack([xgb_enc.transform(xgb.apply(X_test)), X_test])
xgb_lm.fit(X_train_ext, y_train_lr)
y_pred_xgb_originalfeature_lm = xgb_lm.predict_proba(X_test_ext)[:, 1]
fpr_xgb_originalfeature_lm, tpr_xgb_originalfeature_lm, _ = roc_curve(y_test, y_pred_xgb_originalfeature_lm)
print("xgboost新特征與原始特征+LR的AUC為:", roc_auc_score(y_test, y_pred_xgb_originalfeature_lm))
2.2.5 單獨使用RF, GBDT和Xgboost
為了進行對比,我們也輸出單獨使用RF,GBDT和Xgboost的結(jié)果椅邓。
# The gradient boosted model by itself
y_pred_grd = grd.predict_proba(X_test)[:, 1]
fpr_grd, tpr_grd, _ = roc_curve(y_test, y_pred_grd)
print("GBT的AUC為:", roc_auc_score(y_test, y_pred_grd))
# The random forest model by itself
y_pred_rf = rf.predict_proba(X_test)[:, 1]
fpr_rf, tpr_rf, _ = roc_curve(y_test, y_pred_rf)
print("RF的AUC為:", roc_auc_score(y_test, y_pred_rf))
# The xgboost model by itself
xgb.fit(X_train, y_train)
y_pred_xgb = xgb.predict_proba(X_test)[:, 1]
fpr_xgb, tpr_xgb, _ = roc_curve(y_test, y_pred_xgb)
print('xgboost的AUC為:' , roc_auc_score(y_test, y_pred_xgb))
2.2.6 結(jié)果對比
我們運行整個代碼柠逞,結(jié)果為:
RF+LR的AUC為: 0.972532755993
GBT+LR的AUC為: 0.984711442675
xgboost+LR的AUC為: 0.992587688381
xgboost新特征與原始特征+LR的AUC為: 0.992632312284
GBT的AUC為: 0.98220013158
RF的AUC為: 0.965762807823
xgboost的AUC為: 0.99284427301
我們可以看到:對于RF和GBT,與LR進行融合后的結(jié)果要比單獨使用RF和GBT要好景馁。而對于Xgboost板壮,單獨使用Xgboost效果最好,其次是xgboost新特征與原始特征+LR合住,最后才是xgboost+LR绰精。這與我們預(yù)期不符撒璧。為什么會出現(xiàn)這樣的結(jié)果,值得我們討論笨使。
畫圖來進一步看下ROC曲線:
plt.figure(1)
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr_rf, tpr_rf, label='RF')
plt.plot(fpr_rf_lm, tpr_rf_lm, label='RF + LR')
plt.plot(fpr_grd, tpr_grd, label='GBT')
plt.plot(fpr_grd_lm, tpr_grd_lm, label='GBT + LR')
plt.plot(fpr_xgb, tpr_xgb, label='XGB')
plt.plot(fpr_xgb_lm, tpr_xgb_lm, label='XGB + LR')
plt.plot(fpr_xgb_originalfeature_lm, tpr_xgb_originalfeature_lm, label='XGB + ori_fea+ LR')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC curve')
plt.legend(loc='best')
plt.show()
plt.figure(2)
plt.xlim(0, 0.2)
plt.ylim(0.8, 1)
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr_rf, tpr_rf, label='RF')
plt.plot(fpr_rf_lm, tpr_rf_lm, label='RF + LR')
plt.plot(fpr_grd, tpr_grd, label='GBT')
plt.plot(fpr_grd_lm, tpr_grd_lm, label='GBT + LR')
plt.plot(fpr_xgb, tpr_xgb, label='XGB')
plt.plot(fpr_xgb_lm, tpr_xgb_lm, label='XGB + LR')
plt.plot(fpr_xgb_originalfeature_lm, tpr_xgb_originalfeature_lm, label='XGB + ori_fea + LR')
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC curve (zoomed in at top left)')
plt.legend(loc='best')
plt.show()
上面的圖為ROC曲線卿樱,下面的圖是對ROC曲線左上角進行了放大。
三硫椰、為什么Xgboost+LR的融合效果沒有想象中那么好
在2.2.6中繁调,我們提到了僅使用Xgboost的結(jié)果反而最好。這是為什么呢靶草?因為XGBoost + LR 只是一種特征工程的方法蹄胰,并不是一種能自動替代特征工程的方法。
借助參考文獻【2】奕翔,我們來驗證 XGBoost + LR 是嘗試自動替代特征工程的方法裕寨,還只是一種特征工程的方法。
我們在自己業(yè)務(wù)的數(shù)據(jù)上做了一些實驗糠悯。下圖便是實驗結(jié)果懒闷,其中: “xgboost+lr1" 是 XGBoost 的葉子節(jié)點特征宾濒、原始屬性特征和二階交叉特征一起給 LR 進行訓練;"xgboost+lr2" 則只有葉子節(jié)點特征給 LR;"lr1" 是原始屬性特征和二階交叉特征; "lr2" 只有原始屬性特征泌辫。
從上面的實驗來看:
"xgboost+lr2" 明顯弱于 "lr1" 方法,說明只用葉子節(jié)點特征的 XGBoost + LR 弱于有特征工程的 LR 算法峭弟。即 XGBoost 葉子節(jié)點不能取代特征工程朗伶,XGBoost + LR 無法取代傳統(tǒng)的特征工程。
"xgboost+lr1" 取得了所有方法中的最好效果昨稼,說明了保留原來的特征工程 XGBoost + LR 方法擁有比較好的效果节视。即 XGBoost 葉子節(jié)點特征是一種有效的特征,XGBoost + LR 是一種有效的特征工程手段假栓。
因此寻行,可以得到以下的結(jié)論:
盡管XGBoost+LR 在工業(yè)和競賽實踐中,都取得了不錯的效果匾荆。但 XGBoost 的葉子節(jié)點不能完全替代人工特征拌蜘, XGBoost + LR 并沒有像深度學習那樣試圖帶來自動特征工程的故事和邏輯。最終牙丽,XGBoost + LR 的格局沒有超越特征工程简卧。
參考文獻
【1】XGBoost+LR融合的原理和簡單實現(xiàn)