【機器學習】GBDT梯度下降提升算法及參數尋優實例


1、GBDT類庫概述

GBDT有很多簡稱,有GBT(Gradient Boosting Tree), GTB(Gradient Tree Boosting), GBRT(Gradient Boosting Regression Tree),MART(Multiple Additive Regression Tree),其實都是指的同一種算法。sklearn中稱為GradientTree Boosting,分類為GradientBoostingClassifier,回歸為GradientBoostingRegressor。

GBDT也是集成學習Boosting家族的成員,但是卻和傳統的Adaboost有很大的不同。回顧下Adaboost,是利用前一輪迭代弱學習器的誤差率來更新訓練集的權重,這樣一輪輪的迭代下去。GBDT也是迭代,使用了前向分布算法,但是弱學習器限定了只能使用CART回歸樹模型,同時迭代思路和Adaboost也有所不同

在GBDT的迭代中,假設我們前一輪迭代得到的強學習器是ft−1(x), 損失函數是L(y,ft−1(x)),我們本輪迭代的目標是找到一個CART回歸樹模型的弱學習器ht(x),讓本輪的損失L(y,ft(x)=L(y,ft−1(x)+ht(x))最小。也就是說,本輪迭代找到決策樹,要讓樣本的損失盡量變得更小。

GBDT的思想可以用一個通俗的例子解釋,假如有個人30歲,我們首先用20歲去擬合,發現損失有10歲,這時我們用6歲去擬合剩下的損失,發現差距還有4歲,第三輪我們用3歲擬合剩下的差距,差距就只有一歲了(殘差作為下一輪擬合的數據的理解)。如果我們的迭代輪數還沒有完,可以繼續迭代下面,每一輪迭代,擬合的歲數誤差都會減小。

2、GBDT類庫boosting框架參數

首先,看boosting框架相關的重要參數。由於GradientBoostingClassifier和GradientBoostingRegressor的參數絕大部分相同,我們下面會一起來講,不同點會單獨指出。

1)n_estimators: 默認是100,最大的弱學習器的個數,或者弱學習器的最大迭代次數。一般來說n_estimators太小,容易欠擬合,n_estimators太大,又容易過擬合,一般選擇一個適中的數值。在實際調參的過程中,常常將n_estimators和下面介紹的參數learning_rate一起考慮。

2)learning_rate:默認為0.1, 即每個弱學習器的權重縮減系數ν,也稱作步長。是為了過擬合,加上的正則化項系數,我們的強學習器的迭代公式為fk(x)=fk−1(x)+νhk(x)。ν的取值范圍為0<ν≤1。對於同樣的訓練集擬合效果,較小的ν意味着我們需要更多的弱學習器的迭代次數(需要更多的弱學習器)。通常我們用步長和迭代最大次數一起來決定算法的擬合效果。所以這兩個參數n_estimators和learning_rate要一起調參。一般來說,可以從一個小一點的ν開始調參。

3)subsample:默認為1,正則化中的子采樣,防止過擬合,取值為(0,1]。注意這里的子采樣和隨機森林不一樣,隨機森林使用的是放回抽樣,而這里是不放回抽樣如果取值為1,則全部樣本都使用,等於沒有使用子采樣。如果取值小於1,則只有一部分樣本會去做GBDT的決策樹擬合。選擇小於1的比例可以減少方差,即防止過擬合,但是會增加樣本擬合的偏差,因此取值不能太低。推薦在[0.5,0.8]之間,默認是1.0,即不使用子采樣。

4) init: 即我們的初始化的時候的弱學習器,如果不輸入,則用訓練集樣本來做樣本集的初始化分類回歸預測。否則用init參數提供的學習器做初始化分類回歸預測。一般用在我們對數據有先驗知識,或者之前做過一些擬合的時候,如果沒有的話就不用管這個參數了。

5) loss: 即我們GBDT算法中的損失函數。分類模型和回歸模型的損失函數是不一樣的。對於分類模型,有對數似然損失函數”deviance”和指數損失函數”exponential”兩者輸入選擇。默認是對數似然損失函數”deviance”。一般來說,推薦使用默認的”deviance”。它對二元分離和多元分類各自都有比較好的優化。而指數損失函數等於把我們帶到了Adaboost算法。對於回歸模型,有均方差”ls”, 絕對損失”lad”, Huber損失”huber”和分位數損失“quantile”。默認是均方差”ls”。一般來說,如果數據的噪音點不多,用默認的均方差”ls”比較好。如果是噪音點較多,則推薦用抗噪音的損失函數”huber”。而如果我們需要對訓練集進行分段預測的時候,則采用“quantile”。

6) alpha:這個參數只有GradientBoostingRegressor有,當我們使用Huber損失”huber”和分位數損失“quantile”時,需要指定分位數的值。默認是0.9,如果噪音點較多,可以適當降低這個分位數的值。

3、 GBDT類庫弱學習器參數

這里我們再對GBDT的類庫弱學習器的重要參數做一個總結。由於GBDT使用了CART回歸決策樹,因此它的參數基本來源於決策樹類,也就是說,和DecisionTreeClassifier和DecisionTreeRegressor的參數基本類似。

1) max_features: RF划分時考慮的最大特征數。可以使用很多種類型的值,默認是”None”,意味着划分時考慮所有的特征數;如果是”log2”意味着划分時最多考慮log2N個特征;如果是”sqrt”或者”auto”意味着划分時最多考慮N−−√N個特征。如果是整數,代表考慮的特征絕對數。如果是浮點數,代表考慮特征百分比,即考慮(百分比xN)取整后的特征數,其中N為樣本總特征數。一般來說,如果樣本特征數不多,比如小於50,我們用默認的”None”就可以了,如果特征數非常多,我們可以靈活使用剛才描述的其他取值來控制划分時考慮的最大特征數,以控制決策樹的生成時間。

2) max_depth:決策樹最大深度。默認為”None”,決策樹在建立子樹的時候不會限制子樹的深度這樣建樹時,會使每一個葉節點只有一個類別,或是達到min_samples_split。一般來說,數據少或者特征少的時候可以不管這個值。如果模型樣本量多,特征也多的情況下,推薦限制這個最大深度,具體的取值取決於數據的分布。常用的可以取值10-100之間。

3)min_samples_split:內部節點再划分所需最小樣本數,默認2。這個值限制了子樹繼續划分的條件,如果某節點的樣本數少於min_samples_split,則不會繼續再嘗試選擇最優特征來進行划分。默認是2.如果樣本量不大,不需要管這個值。如果樣本量數量級非常大,則推薦增大這個值。

4)min_samples_leaf:葉子節點最少樣本數。這個值限制了葉子節點最少的樣本數,如果某葉子節點數目小於樣本數,則會和兄弟節點一起被剪枝。默認是1,可以輸入最少的樣本數的整數,或者最少樣本數占樣本總數的百分比。如果樣本量不大,不需要管這個值。如果樣本量數量級非常大,則推薦增大這個值。

5)min_weight_fraction_leaf:葉子節點最小的樣本權重和。這個值限制了葉子節點所有樣本權重和的最小值,如果小於這個值,則會和兄弟節點一起被剪枝。默認是0,就是不考慮權重問題。一般來說,如果我們有較多樣本有缺失值,或者分類樹樣本的分布類別偏差很大,就會引入樣本權重,這時我們就要注意這個值了。

6)max_leaf_nodes:最大葉子節點數。通過限制最大葉子節點數,可以防止過擬合,默認是”None”,即不限制最大的葉子節點數。如果加了限制,算法會建立在最大葉子節點數內最優的決策樹。如果特征不多,可以不考慮這個值,但是如果特征分成多的話,可以加以限制,具體的值可以通過交叉驗證得到。

7)min_impurity_split:節點划分最小不純度。這個值限制了決策樹的增長,如果某節點的不純度(基於基尼系數,均方差)小於這個閾值,則該節點不再生成子節點,即為葉子節點。一般不推薦改動默認值1e-7。

8)presort:是否對數據進行預分類,以加快擬合中最佳分裂點的發現。默認False,適用於大數據集。小數據集使用True,可以加快訓練。是否預排序,預排序可以加速查找最佳分裂點,對於稀疏數據不管用,Bool,auto:非稀疏數據則預排序,若稀疏數據則不預排序

上面決策樹參數中最重要的包括最大特征數max_features,最大深度max_depth,內部節點再划分所需最小樣本數min_samples_split和葉子節點最少樣本數min_samples_leaf。

4、 GBDT調參實例

這里用一個二元分類的例子來講解下GBDT的調參。這個例子的數據有87000多行,單機跑會比較慢,下面的例子我選擇了它的前面20000行,[下載鏈接](鏈接: https://pan.baidu.com/s/1bpuFm4R 密碼: j5mj)。

首先導入庫

import pandas as pd  
import numpy as np  
#GBDT庫
from sklearn.ensemble import GradientBoostingClassifier
#交叉驗證
from sklearn import cross_validation, metrics  
#參數尋優
from sklearn.grid_search import GridSearchCV  
import matplotlib.pyplot as plt  

接着把解壓的數據用下面的代碼載入,順便看看數據的類別分布

train= pd.read_csv('train_modified.csv')  
target='Disbursed'# Disbursed的值就是二元分類的輸出 
IDcol= 'ID'  
print(train['Disbursed'].value_counts())  

現在得到訓練集,最后一列,Disbrsed是分類輸出。前面的所有列(不考慮ID列)都是樣本特征。

x_columns= [x for x in train.columns if x not in [target, IDcol]]  
X= train[x_columns]  
y= train['Disbursed']

不管任何參數,都用默認的,我們擬合下數據看看:

gbm0= GradientBoostingClassifier(random_state=10)  
gbm0.fit(X,y)  
y_pred= gbm0.predict(X)  
y_predprob= gbm0.predict_proba(X)[:,1]  
print("Accuracy : %.4g" % metrics.accuracy_score(y.values, y_pred))  
print("AUC Score (Train): %f" % metrics.roc_auc_score(y, y_predprob))

首先從步長(learning rate)和迭代次數(n_estimators)入手。一般來說,開始選擇一個較小的步長來網格搜索最好的迭代次數。這里,我們將步長初始值設置為0.1。對於迭代次數進行網格搜索如下:

param_test1= {'n_estimators':list(range(20,81,10))}  
gsearch1= GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1,
                                                              min_samples_split=300,
                                                              min_samples_leaf=20,
                                                              max_depth=8,
                                                              max_features='sqrt',
                                                              subsample=0.8,
                                                              random_state=10), param_grid= param_test1, scoring='roc_auc',iid=False,cv=5)  
gsearch1.fit(X,y)  
print(gsearch1.grid_scores_,gsearch1.best_params_, gsearch1.best_score_) 

找到了一個合適的迭代次數,現在開始對決策樹進行調參。首先我們對決策樹最大深度max_depth和內部節點再划分所需最小樣本數min_samples_split進行網格搜索。

param_test2= {'max_depth':list(range(3,14,2)), 'min_samples_split':list(range(100,801,200))}  
gsearch2= GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1,
                                                              n_estimators=60,
                                                              min_samples_leaf=20,
                                                              max_features='sqrt',
                                                              subsample=0.8,
                                                              random_state=10),param_grid= param_test2,scoring='roc_auc',iid=False,cv=5)  
gsearch2.fit(X,y)  
print(gsearch2.grid_scores_,gsearch2.best_params_, gsearch2.best_score_ ) 

得到決策樹深度一個比較合理的值,我們把它定下來,對於內部節點再划分所需最小樣本數min_samples_split,我們暫時不能一起定下來,因為這個還和決策樹其他的參數存在關聯。下面我們再對內部節點再划分所需最小樣本數min_samples_split和葉子節點最少樣本數

param_test3= {'min_samples_split':list(range(800,1900,200)),'min_samples_leaf':list(range(60,101,10))}  
gsearch3= GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1,
                                                              n_estimators=60,
                                                              max_depth=7,
                                                              max_features='sqrt',
                                                              subsample=0.8,
                                                              random_state=10),param_grid= param_test3,scoring='roc_auc',iid=False, cv=5)  
gsearch3.fit(X,y)  
print(gsearch3.grid_scores_,gsearch3.best_params_, gsearch3.best_score_)

如果得到min_samples_split在邊界值,還有進一步調試小於邊界的必要。由於這里只是例子,所以大家可以自己下來用包含小於邊界值的網格搜索來尋找合適的值。

調了這么多參數了,終於可以都放到GBDT類里面去看看效果了。現在我們用新參數擬合數據:

gbm1= GradientBoostingClassifier(learning_rate=0.1,
                                 n_estimators=60,
                                 max_depth=7,
                                 min_samples_leaf =60, 
                                 min_samples_split =1200, 
                                 max_features='sqrt',
                                 subsample=0.8,
                                 random_state=10)
gbm1.fit(X,y)  
y_pred= gbm1.predict(X)  
y_predprob= gbm1.predict_proba(X)[:,1]  
print("Accuracy : %.4g" % metrics.accuracy_score(y.values, y_pred))
print("AUC Score (Train): %f" % metrics.roc_auc_score(y, y_predprob)) 

對比我們最開始完全不調參的擬合效果,可見精確度稍有下降,主要原理是我們使用了0.8的子采樣,20%的數據沒有參與擬合。

現在我們再對最大特征數max_features進行網格搜索。

param_test4= {'max_features':list(range(7,20,2))}  
gsearch4= GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1,
                                                              n_estimators=60,
                                                              max_depth=7, 
                                                              min_samples_leaf =60,
                                                              min_samples_split =1200,
                                                              subsample=0.8,
                                                              random_state=10),param_grid= param_test4,scoring='roc_auc',iid=False,cv=5)  
gsearch4.fit(X,y)  
print(gsearch4.grid_scores_,gsearch4.best_params_, gsearch4.best_score_) 

現在我們再對子采樣的比例進行網格搜索:

param_test5= {'subsample':[0.6,0.7,0.75,0.8,0.85,0.9]}  
gsearch5= GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1,
                                                              n_estimators=60,
                                                              max_depth=7,
                                                              min_samples_leaf =60,
                                                              min_samples_split =1200,
                                                              max_features=9,
                                                              random_state=10),param_grid= param_test5,scoring='roc_auc', iid=False, cv=5)  
gsearch5.fit(X,y)  
print(gsearch5.grid_scores_,gsearch5.best_params_, gsearch5.best_score_)

現在我們基本已經得到我們所有調優的參數結果了。這時我們可以減半步長,最大迭代次數加倍來增加我們模型的泛化能力。再次擬合我們的模型:

gbm2= GradientBoostingClassifier(learning_rate=0.05,
                                 n_estimators=120,
                                 max_depth=7,
                                 min_samples_leaf =60,
                                 min_samples_split =1200,
                                 max_features=9,
                                 subsample=0.7,
                                 random_state=10)  
gbm2.fit(X,y)  
y_pred= gbm2.predict(X)  
y_predprob= gbm2.predict_proba(X)[:,1]  
print("Accuracy : %.4g" % metrics.accuracy_score(y.values, y_pred))  
print("AUC Score (Train): %f" % metrics.roc_auc_score(y, y_predprob)) 

下面我們繼續將步長縮小5倍,最大迭代次數增加5倍,繼續擬合我們的模型:

gbm3= GradientBoostingClassifier(learning_rate=0.01,
                                 n_estimators=600,
                                 max_depth=7,
                                 min_samples_leaf =60,
                                 min_samples_split =1200,
                                 max_features=9,
                                 subsample=0.7,
                                 random_state=10)  
gbm3.fit(X,y)  
y_pred= gbm3.predict(X)  
y_predprob= gbm3.predict_proba(X)[:,1]  
print("Accuracy : %.4g" % metrics.accuracy_score(y.values, y_pred))  
print("AUC Score (Train): %f" % metrics.roc_auc_score(y, y_predprob)) 

通過比較結果,分析減小步長增加迭代次數是否可以在保證泛化能力的基礎上增加一些擬合程度。

最后我們繼續步長縮小一半,最大迭代次數增加2倍,擬合我們的模型:

gbm4= GradientBoostingClassifier(learning_rate=0.005,
                                 n_estimators=1200,
                                 max_depth=7,
                                 min_samples_leaf =60,
                                 min_samples_split =1200,
                                 max_features=9,
                                 subsample=0.7,
                                 random_state=10)  
gbm4.fit(X,y)  
y_pred= gbm4.predict(X)  
y_predprob= gbm4.predict_proba(X)[:,1]  
print("Accuracy : %.4g" % metrics.accuracy_score(y.values, y_pred))  
print("AUC Score (Train): %f" % metrics.roc_auc_score(y, y_predprob)) 

如果步長實在太小,導致擬合效果反而變差,也就是說,步長不能設置的過小。

5、參數尋優整理

以上便是整個尋優的過程,接下來把整個過程整理一下:

***1、首先使用默認的參數,進行數據擬合;
2、從步長(learning rate)和迭代次數(n_estimators)入手;一般來說,開始選擇一個較小的步長來網格搜索最好的迭代次數。這里,可以將步長初始值設置為0.1。對於迭代次數進行網格搜索;
3、接下來對決策樹的參數進行尋優
4、首先我們對決策樹最大深度max_depth和內部節點再划分所需最小樣本數min_samples_split進行網格搜索。【min_samples_split暫時不能一起定下來,因為這個還和決策樹其他的參數存在關聯】
5、接着再對內部節點再划分所需最小樣本數min_samples_split和葉子節點最少樣本數min_samples_leaf一起調參;做到這里,min_samples_split要做兩次網格尋優,一次是樹的最大深度max_depth,一次是葉子節點最少樣本數min_samples_leaf。【具體觀察min_samples_split的值是否落在邊界上,如果是可以進一步尋優】
6、繼續對最大特征數max_features進行網格搜索。做完這一步可以看看尋找出的最優參數組合給出的分類器的效果。
7、可以進一步考慮對子采樣的比例進行網格搜索,得到subsample的尋優參數
8、回歸到第2步調整設定的步長(learning rate)和迭代次數(n_estimators),注意兩者的乘積保持不變,這里可以分析得到:通過減小步長可以提高泛化能力,但是步長設定過小,也會導致擬合效果反而變差,也就是說,步長不能設置的過小。***

6、使用GBDT選擇特征

GBDT的一個重要應用在於可以進行特征選擇,通過GBDT模型,可以得到特征值的權重,因此給了我們一種思路就是選擇有效的特征,去除無效的特征,以便進一步通過其他方法構建模型進行數據的訓練。

以下通過一個實例來描述:

import numpy as np
X= np.arange(600).reshape([-1,4])
y = np.zeros(150)
from sklearn.ensemble import GradientBoostingRegressor  
gbdt = GradientBoostingClassifier(  
    init=None,  
    learning_rate=0.1,  
    loss='deviance',  
    max_depth=3,  
    max_features=None,  
    max_leaf_nodes=None,  
    min_samples_leaf=1,  
    min_samples_split=2,  
    min_weight_fraction_leaf=0.0,  
    n_estimators=100,  
    random_state=None,  
    subsample=1.0,  
    verbose=0,  
    warm_start=False)  
print("fit start!") 
gbdt.fit(X, y)  
print("fit success!") 
score = gbdt.feature_importances_  
print(gbdt.feature_importances_.shape) 

#選擇importance>0的特征
X_new = X[:, gbdt.feature_importances_>0]  
print(X_new.shape)  

7、總結:

    GBDT終於講完了,GDBT本身並不復雜,不過要吃透的話需要對集成學習的原理,決策樹原理和各種損失函樹有一定的了解。由於GBDT的卓越性能,只要是研究機器學習都應該掌握這個算法,包括背后的原理和應用調參方法。目前GBDT的算法比較好的庫是xgboost。當然scikit-learn也可以。

    GBDT主要的優點有:

    1) 可以靈活處理各種類型的數據,包括連續值和離散值。

    2) 在相對少的調參時間情況下,預測的准備率也可以比較高。這個是相對SVM來說的。

    3)使用一些健壯的損失函數,對異常值的魯棒性非常強。比如 Huber損失函數和Quantile損失函數。

    GBDT的主要缺點有:

    1)由於弱學習器之間存在依賴關系,難以並行訓練數據。不過可以通過自采樣的SGBT來達到部分並行。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com