模型融合---GBDT調參總結


 一、GBDT類庫弱學習器參數

 

 

參數分為三類 
第一類:Miscellaneous Parameters: Other parameters for overall functioning. 沒啥用 
第二類:Boosting Parameters: These affect the boosting operation in the model. 
n_estimators 最大弱學習器的個數,太小欠擬合,太大過擬合 
learning_rate 學習率,太大過擬合,一般很小0.1,和n_estimators一起調 
subsample 子采樣,防止過擬合,太小欠擬合。GBDT中是不放回采樣 
第三類:Tree-Specific Parameters: These affect each individual tree in the model. 
max_features 最大特征數 
max_depth 最大樹深,太大過擬合 
min_samples_split 內部節點再划分所需最小樣本數,越大越防過擬合 
min_weight_fraction_leaf 葉子節點最小的樣本權重和。如果存在較多樣本有缺失值,或者分類樹樣本的分布類別偏差很大,就會引入樣本權重,這時我們就要注意這個值了。越大越防過擬合 
max_leaf_nodes:最大葉子節點數 ,太大過擬合 
min_impurity_split:節點划分最小不純度 
presort:是否對數據進行預分類,以加快擬合中最佳分裂點的發現。默認False,適用於大數據集。小數據集使用True,可以加快訓練。是否預排序,預排序可以加速查找最佳分裂點,對於稀疏數據不管用,Bool,auto:非稀疏數據則預排序,若稀疏數據則不預排序

 

 

 

接下來把調參的整個過程整理一下: 

 

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

 

 

 

二、回歸

數據集:已知用戶的30個特征,預測用戶的信用值

from sklearn.ensemble import GradientBoostingRegressor
from sklearn.grid_search import GridSearchCV

#用平均值填補缺失值
gbdt_train_label = train_data['信用分']

gbdt_train_data = train_data[columns_]
gbdt_test_data = test_data[columns_]

gbdt_train_data = gbdt_train_data.fillna(gbdt_train_data.mean())
gbdt_test_data = gbdt_test_data.fillna(gbdt_test_data.mean())

 

#填補6個月平均占比總費用為其他列的平均值
all_rows = list(gbdt_train_data.index)
inf_rows = list(gbdt_train_data.loc[gbdt_train_data['6個月平均占比總費用'] == float('inf')].index)
inf_mean = gbdt_train_data.ix[list(filter(lambda x: x not in inf_rows,all_rows))].mean() 

gbdt_train_data['6個月平均占比總費用'][gbdt_train_data['6個月平均占比總費用'] == float('inf')] = inf_mean['6個月平均占比總費用']

1. 找樹的數量最佳值

#找樹的數量最佳值
param_gbdt = {'n_estimators':list(range(100,600,50))}
gbdt_search = GridSearchCV(estimator=GradientBoostingRegressor(learning_rate=0.1,min_samples_split=300,min_samples_leaf=20,
                                          max_depth=8,max_features='sqrt',subsample=0.8,random_state=75),
                          param_grid=param_gbdt,scoring='neg_mean_squared_error',iid=False,cv=3)
gbdt_search.fit(gbdt_train_data,gbdt_train_label)
print(gbdt_search.grid_scores_)
print(gbdt_search.best_params_)
print(gbdt_search.best_score_)

 

2.  找樹的深度最佳值

#找樹的深度最佳值
param_gbdt1 = {'max_depth':[6,7,8,9,10]}
gbdt_search1 = GridSearchCV(estimator=GradientBoostingRegressor(learning_rate=0.1,n_estimators = 200,min_samples_split=300,
                            min_samples_leaf=20,max_features='sqrt',subsample=0.8,random_state=75),
                            param_grid=param_gbdt1,scoring='neg_mean_squared_error',iid=False,cv=5)
gbdt_search1.fit(gbdt_train_data,gbdt_train_label)
print(gbdt_search1.grid_scores_)
print(gbdt_search1.best_params_)
print(gbdt_search1.best_score_)

3. 找min_samples_split,min_samples_leaf最佳值

#找min_samples_split,min_samples_leaf最佳值
param_gbdt2 = {'min_samples_split':[500,700,900,1100],
               'min_samples_leaf':[30,50,70,90]}
gbdt_search2 = GridSearchCV(estimator=GradientBoostingRegressor(learning_rate=0.1,n_estimators = 100,max_depth=6,
                                max_features='sqrt',subsample=0.8,random_state=75),n_jobs=3,
                            param_grid=param_gbdt2,scoring='neg_mean_squared_error',iid=False,cv=5)
gbdt_search2.fit(gbdt_train_data,gbdt_train_label)
print(gbdt_search2.grid_scores_)
print(gbdt_search2.best_params_)
print(gbdt_search2.best_score_)

4. 粗調完成,得到了上述參數的最佳大致范圍,接下來,可以進行細調,就是在上述最佳參數附近范圍進行搜索,這里就只對n_estimators和學習率進行調整

param_gbdt3 = {'learning_rate':[0.06,0.08,0.1],
               'n_estimators':[100,150,200,250]}
gbdt_search3 = GridSearchCV(estimator=GradientBoostingRegressor(min_samples_split=700,min_samples_leaf=70,
                                max_depth=9,max_features='sqrt',subsample=0.8,random_state=75),n_jobs=3,
                            param_grid=param_gbdt3,scoring='neg_mean_squared_error',iid=False,cv=5)
gbdt_search3.fit(gbdt_train_data,gbdt_train_label)
print(gbdt_search3.grid_scores_)
print(gbdt_search3.best_params_)
print(gbdt_search3.best_score_)

可以看到,最佳學習率為 0.06,提升樹數量為250 ;此時r2提升到0.7907 。  

 

三、分類

前提:假設數據已經處理完畢,現在開始訓練。

modelfit函數包含訓練,測試,交叉驗證,輸出accuracy和AUC,畫出重要特征的功能

def modelfit(alg, dtrain, dtest, predictors, performCV=True, printFeatureImportance=True, cv_folds=5):
    '''
    alg是分類器,dtrain是訓練集(包括label),dtes是測試集,predictors是除了label和ID的其他特征,performCV指是否要交叉驗證
    '''
    #訓練訓練集
    alg.fit(dtrain[predictors], dtrain['Disbursed']) #predictors是除了待預測的其他特征,Distursed是目標特征
        
    #預測測試集
    dtrain_predictions = alg.predict(dtrain[predictors])
    dtrain_predprob = alg.predict_proba(dtrain[predictors])[:,1]
    
    #交叉驗證
    if performCV:
        cv_score = cross_validation.cross_val_score(alg, dtrain[predictors], dtrain['Disbursed'], cv=cv_folds, scoring='roc_auc')
    
    #輸出模型的accuracy、AUC
    print("\nModel Report")
    print("Accuracy : %.4g" % metrics.accuracy_score(dtrain['Disbursed'].values, dtrain_predictions))
    print("AUC Score (Train): %f" % metrics.roc_auc_score(dtrain['Disbursed'], dtrain_predprob))
    
    if performCV:#如果選了要交叉驗證,就輸出交叉驗證的平均值,標准差,最小值,最大值
        print("CV Score : Mean - %.7g | Std - %.7g | Min - %.7g | Max - %.7g" % (np.mean(cv_score),np.std(cv_score),np.min(cv_score),np.max(cv_score)))
                
    #畫出重要特征圖像
    if printFeatureImportance:
        feat_imp = pd.Series(alg.feature_importances_, predictors).sort_values(ascending=False)
        feat_imp.plot(kind='bar', title='Feature Importances')
        plt.ylabel('Feature Importance Score')

  

接着就要創建一個基線模型(baseline model)。這里我們用AUC來作為衡量標准,所以用常數的話AUC就是0.5。一般來說用默認參數設置的GBM模型就是一個很好的基線模型,我們來看看這個模型的輸出和特征重要性:

#Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm0 = GradientBoostingClassifier(random_state=10)
modelfit(gbm0, train, test, predictors,printOOB=False)

 

從圖上看出,CV的平均值是0.8319,后面調整的模型會做得比這個更好

 

 

GBDT主要有兩種類型的參數:

Tree-specific parameters:

  • min_samples_split
  • min_samples_leaf
  • max_depth
  • min_leaf_nodes
  • max_features
  • loss function

Boosting specific paramters:

  • n_estimators
  • learning_rate
  • subsample

 

參數調節的一般方法:樹參數和boosting參數。learning rate沒有什么特別的調節方法,因為只要我們訓練的樹足夠多learning rate總是小值來得好。

雖然隨着決定樹的增多GBM並不會明顯得過度擬合,高learing rate還是會導致這個問題,但如果我們一味地減小learning rate、增多樹,計算就會非常昂貴而且需要運行很長時間。了解了這些問題,我們決定采取以下方法調參:

(1)選擇一個相對來說稍微高一點的learning rate。一般默認的值是0.1,不過針對不同的問題,0.05到0.2之間都可以

(2)決定當前learning rate下最優的決定樹數量。它的值應該在40-70之間。記得選擇一個你的電腦還能快速運行的值,因為之后這些樹會用來做很多測試和調參。

(3)接着調節樹參數來調整learning rate和樹的數量。我們可以選擇不同的參數來定義一個決定樹,后面會有這方面的例子

(4)降低learning rate,同時會增加相應的決定樹數量使得模型更加穩健

 

1. 固定 learning rate和需要估測的決定樹數量

  為了決定boosting參數,我們得先設定一些參數的初始值,可以像下面這樣:

  • min_ samples_ split=500: 這個值應該在總樣本數的0.5-1%之間,由於我們研究的是不均等分類問題,我們可以取這個區間里一個比較小的數,500。
  • min_ samples_ leaf=50: 可以憑感覺選一個合適的數,只要不會造成過度擬合。同樣因為不均等分類的原因,這里我們選擇一個比較小的值。
  • max_ depth=8: 根據觀察數和自變量數,這個值應該在5-8之間。這里我們的數據有87000行,49列,所以我們先選深度為8。
  • max_ features=’sqrt’: 經驗上一般都選擇平方根。
  • subsample=0.8: 開始的時候一般就用0.8

  注意我們目前定的都是初始值,最終這些參數的值應該是多少還要靠調參決定。現在我們可以根據learning rate的默認值0.1來找到所需要的最佳的決定樹數量,可以利用網格搜索(grid search)實現,以10個數遞增,從20測到80。

#Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
param_test1 = {'n_estimators':range(20,81,10)}
gsearch1 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, min_samples_split=500,
                                  min_samples_leaf=50,max_depth=8,max_features='sqrt', subsample=0.8,random_state=10), 
                       param_grid = param_test1, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch1.fit(train[predictors],train[target])

gsearch1.grid_scores_, gsearch1.best_params_, gsearch1.best_score_

  可以看出對於0.1的learning rate, 60個樹是最佳的,而且60也是一個合理的決定樹數量,所以我們就直接用60。但在一些情況下上面這段代碼給出的結果可能不是我們想要的,比如:

  • 如果給出的輸出是20,可能就要降低我們的learning rate到0.05,然后再搜索一遍。
  • 如果輸出值太高,比如100,因為調節其他參數需要很長時間,這時候可以把learniing rate稍微調高一點。

 

2. 調節樹參數

  樹參數可以按照這些步驟調節:

  • 調節max_depth和 num_samples_split
  • 調節min_samples_leaf
  • 調節max_features

  需要注意一下調參順序,對結果影響最大的參數應該優先調節,就像max_depthnum_samples_split

  重要提示:接着我會做比較久的網格搜索(grid search),可能會花上15-30分鍾。你在自己嘗試的時候應該根據電腦情況適當調整需要測試的值。

  max_depth可以相隔兩個數從5測到15,而min_samples_split可以按相隔200從200測到1000。這些完全憑經驗和直覺,如果先測更大的范圍再用迭代去縮小范圍也是可行的。

#Grid seach on subsample and max_features
param_test2 = {'max_depth':range(5,16,2), 'min_samples_split':range(200,1001,200)}
gsearch2 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, n_estimators=60,
                                                max_features='sqrt', subsample=0.8, random_state=10), 
                       param_grid = param_test2, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch2.fit(train[predictors],train[target])

gsearch2.grid_scores_, gsearch2.best_params_, gsearch2.best_score_

  從結果可以看出,我們從30種組合中找出最佳的max_depth是9,而最佳的min_smaples_split是1000。1000是我們設定的范圍里的最大值,有可能真正的最佳值比1000還要大,所以我們還要繼續增加min_smaples_split。樹深就用9。接着就來調節min_samples_leaf,可以測30,40,50,60,70這五個值,同時我們也試着調大min_samples_split的值。 

#Grid seach on subsample and max_features
param_test3 = {'min_samples_split':range(1000,2100,200), 'min_samples_leaf':range(30,71,10)}
gsearch3 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, n_estimators=60,max_depth=9,
                                                    max_features='sqrt', subsample=0.8, random_state=10), 
                       param_grid = param_test3, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch3.fit(train[predictors],train[target])

  

gsearch3.grid_scores_, gsearch3.best_params_, gsearch3.best_score_

 

這樣min_samples_split的最佳值是1200,而min_samples_leaf的最佳值是60。注意現在CV值增加到了0.8396。現在我們就根據這個結果來重新建模,並再次評估特征的重要性。

modelfit(gsearch3.best_estimator_, train, test, predictors)

  比較之前的基線模型結果可以看出,現在我們的模型用了更多的特征,並且基線模型里少數特征的重要性評估值過高,分布偏斜明顯,現在分布得更加均勻了。

  接下來就剩下最后的樹參數max_features了,可以每隔兩個數從7測到19。

#Grid seach on subsample and max_features
param_test4 = {'max_features':range(7,20,2)}
gsearch4 = GridSearchCV(estimator = GradientBoostingClassifier(learning_rate=0.1, n_estimators=60,max_depth=9, 
                            min_samples_split=1200, min_samples_leaf=60, subsample=0.8, random_state=10),
                       param_grid = param_test4, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch4.fit(train[predictors],train[target])

gsearch4.grid_scores_, gsearch4.best_params_, gsearch4.best_score_

最佳的結果是7,正好就是我們設定的初始值(平方根)。當然你可能還想測測小於7的值,鼓勵這么做。而按照我們的設定,現在的樹參數是這樣的:

  • min_samples_split: 1200
  • min_samples_leaf: 60
  • max_depth: 9
  • max_features: 7

3. 調節子樣本比例來降低learning rate

 接下來就可以調節子樣本占總樣本的比例,我准備嘗試這些值:0.6,0.7,0.75,0.8,0.85,0.9。

#Grid seach on subsample and max_features
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=9, 
                            min_samples_split=1200, min_samples_leaf=60, subsample=0.8, random_state=10, max_features=7),
                       param_grid = param_test5, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch5.fit(train[predictors],train[target])

gsearch5.grid_scores_, gsearch5.best_params_, gsearch5.best_score_

  給出的結果是0.85。這樣所有的參數都設定好了,現在我們要做的就是進一步減少learning rate,就相應地增加了樹的數量。需要注意的是樹的個數是被動改變的,可能不是最佳的,但也很合適。隨着樹個數的增加,找到最佳值和CV的計算量也會加大,為了看出模型執行效率,我還提供了我每個模型在比賽的排行分數(leaderboard score),怎么得到這個數據不是公開的,你很難重現這個數字,它只是為了更好地幫助我們理解模型表現。

  現在我們先把learning rate降一半,至0.05,這樣樹的個數就相應地加倍到120。

#Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm_tuned_1 = GradientBoostingClassifier(learning_rate=0.05, n_estimators=120,max_depth=9, min_samples_split=1200, 
                                         min_samples_leaf=60, subsample=0.85, random_state=10, max_features=7)
modelfit(gbm_tuned_1, train, test, predictors)

 

  接下來我們把learning rate進一步減小到原值的十分之一,即0.01,相應地,樹的個數變為600。

#Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm_tuned_2 = GradientBoostingClassifier(learning_rate=0.01, n_estimators=600,max_depth=9, min_samples_split=1200, 
                                         min_samples_leaf=60, subsample=0.85, random_state=10, max_features=7)
modelfit(gbm_tuned_2, train, test, predictors)

  繼續把learning rate縮小至二十分之一,即0.005,這時候我們有1200個樹。

#Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm_tuned_3 = GradientBoostingClassifier(learning_rate=0.005, n_estimators=1200,max_depth=9, min_samples_split=1200, 
                                         min_samples_leaf=60, subsample=0.85, random_state=10, max_features=7,
                                         warm_start=True)
modelfit(gbm_tuned_3, train, test, predictors, performCV=False)

  排行得分稍微降低了,我們停止減少learning rate,只單方面增加樹的個數,試試1500個樹

#Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
gbm_tuned_4 = GradientBoostingClassifier(learning_rate=0.005, n_estimators=1500,max_depth=9, min_samples_split=1200, 
                                         min_samples_leaf=60, subsample=0.85, random_state=10, max_features=7,
                                         warm_start=True)
modelfit(gbm_tuned_4, train, test, predictors, performCV=False)

還有一個技巧就是用“warm_start”選項。這樣每次用不同個數的樹都不用重新開始

 

 

參考文獻:

【1】scikit-learn 梯度提升樹(GBDT)調參小結

【2】Gradient Boosting Machine(GBM)調參方法詳解 

【3】https://github.com/aarshayj/Analytics_Vidhya/tree/master/Articles


免責聲明!

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



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