本文內容結構主要參考Complete Guide to Parameter Tuning in XGBoost with codes in Python 及其 中文翻譯 XGBoost參數調優完全指南。由于參考博客代碼部分api已經更新,因此對部分代碼進行了更新。
簡介
XGBoost算法現在已經成為很多數據工程師的重要武器。它是一種十分精致的算法,可以處理各種不規則的數據,常年在kaggle中大殺四方。
構造一個使用XGBoost的模型十分簡單。但是,提高這個模型的表現就有些困難(至少我覺得十分糾結)。這個算法使用了好幾個參數。所以為了提高模型的表現,參數的調整十分必要。在解決實際問題的時候,有些問題是很難回答的——你需要調整哪些參數?這些參數要調到什么值,才能達到理想的輸出?
這篇文章最適合剛剛接觸XGBoost的人閱讀。在這篇文章中,我們會學到參數調優的技巧,以及XGboost相關的一些有用的知識。以及,我們會用Python在一個數據集上實踐一下這個算法。
XGBoost(eXtreme Gradient Boosting)是Gradient Boosting算法的一個優化的版本。因為我在前一篇文章——基于Python的Gradient Boosting算法參數調整完全指南
,里面已經涵蓋了Gradient Boosting算法的很多細節了。我強烈建議大家在讀本篇文章之前,把那篇文章好好讀一遍。它會幫助你對Boosting算法有一個宏觀的理解,同時也會對GBM的參數調整有更好的體會。
內容列表
1、XGBoost 的優勢
2、理解XGBoost的參數
3、調參實例
1、XGBoost 的優勢
XGBoost算法可以給預測模型帶來能力的提升。當我對它的表現有更多了解的時候,當我對它的高準確率背后的原理有更多了解的時候,我發現它具有很多優勢:
1.1 正則化
- 標準GBM的實現沒有像XGBoost這樣的正則化步驟。正則化對減少過擬合也是有幫助的。
- 實際上,XGBoost以“正則化提升(regularized boosting)”技術而聞名。
1.2 并行處理
- XGBoost可以實現并行處理,相比GBM有了速度的飛躍。
- 不過,眾所周知,Boosting算法是順序處理的,它怎么可能并行呢?每一課樹的構造都依賴于前一棵樹,那具體是什么讓我們能用多核處理器去構造一個樹呢?我希望你理解了這句話的意思。如果你希望了解更多,點擊這個鏈接。
- XGBoost 也支持Hadoop實現。
1.3 高度的靈活性
- XGBoost 允許用戶定義自定義優化目標和評價標準。
- 它對模型增加了一個全新的維度,所以我們的處理不會受到任何限制。
1.4 缺失值處理
- XGBoost內置處理缺失值的規則。
- 用戶需要提供一個和其它樣本不同的值,然后把它作為一個參數傳進去,以此來作為缺失值的取值。XGBoost在不同節點遇到缺失值時采用不同的處理方法,并且會學習未來遇到缺失值時的處理方法。
1.5 剪枝
- 當分裂時遇到一個負損失時,GBM會停止分裂。因此GBM實際上是一個貪心算法。
- XGBoost會一直分裂到指定的最大深度(max_depth),然后回過頭來剪枝。如果某個節點之后不再有正值,它會去除這個分裂。
- 這種做法的優點,當一個負損失(如-2)后面有個正損失(如+10)的時候,就顯現出來了。GBM會在-2處停下來,因為它遇到了一個負值。但是XGBoost會繼續分裂,然后發現這兩個分裂綜合起來會得到+8,因此會保留這兩個分裂。
1.6 內置交叉驗證
- XGBoost允許在每一輪boosting迭代中使用交叉驗證。因此,可以方便地獲得最優boosting迭代次數。
- 而GBM使用網格搜索,只能檢測有限個值。
需要注意的是在xgboost中迭代次數指的是樹的棵樹。這與我們廣義上的迭代次數是不同的。
1.7 在已有的模型基礎上繼續
- XGBoost可以在上一輪的結果上繼續訓練。這個特性在某些特定的應用上是一個巨大的優勢。
- sklearn中的GBM的實現也有這個功能,兩種算法在這一點上是一致的。
相信你已經對XGBoost強大的功能有了點概念。注意這是我自己總結出來的幾點,你如果有更多的想法,盡管在下面評論指出,我會更新這個列表的!
你的胃口被我吊起來了嗎?棒棒噠!如果你想更深入了解相關信息,可以參考下面這些文章:
2、XGBoost 的參數
XGBoost的作者把所有的參數分成了三類:
- 通用參數:宏觀函數控制。
- Booster參數:控制每一步的booster(tree/regression)。
- 學習目標參數:控制訓練目標的表現。
在這里我會類比GBM來講解,所以作為一種基礎知識,強烈推薦先閱讀這篇文章。
2.1 通用參數
這些參數用來控制XGBoost的宏觀功能。
-
booster
[默認gbtree]
選擇每次迭代的模型,有兩種選擇:- gbtree:基于樹的模型;
- gbliner:線性模型。
-
silent
[默認0]- 當這個參數值為1時,靜默模式開啟,不會輸出任何信息。
- 一般這個參數就保持默認的0,因為這樣能幫我們更好地理解模型。
-
nthread
[默認值為最大可能的線程數]- 這個參數用來進行多線程控制,應當輸入系統的核數。
- 如果你希望使用CPU全部的核,那就不要輸入這個參數,算法會自動檢測它。
2.2 booster參數
盡管有兩種booster可供選擇,我這里只介紹tree booster,因為它的表現遠遠勝過linear booster,所以linear booster很少用到。
-
eta
[default=0.3, alias:learning_rate
]
類似于神經網絡中的學習率,但這里eta(論文中是shrinkage的意思)的作用是限制每棵樹學到的東西大小,以讓更多的樹來進行學習。- 和GBM中的 learning rate 參數類似。
- 通過減少每一步的權重,可以提高模型的魯棒性。
- 典型值為0.01-0.2。
-
min_child_weight
[default=1]
最小葉子節點中樣本的權重和,有點類似與隨機森林中的最小葉子節點樣本數,設置大點有防止過擬合的作用。- 決定最小葉子節點樣本權重和。
- 和GBM的 min_child_leaf 參數類似,但不完全一樣。XGBoost的這個參數是最小樣本權重的和,而GBM參數是最小樣本總數。
- 這個參數用于避免過擬合。當它的值較大時,可以避免模型學習到局部的特殊樣本。
- 但是如果這個值過高,會導致欠擬合。這個參數需要使用CV來調整。
-
max_depth
[default=6]
和GBM中的參數相同,這個值為樹的最大深度。- 這個值也是用來避免過擬合的。max_depth越大,模型會學到更具體更局部的樣本。
- 需要使用CV函數來進行調優。
- 典型值:3-10
-
max_leaf_nodes
每棵樹最大的節點或葉子的數量。- 可以替代max_depth的作用。因為如果生成的是二叉樹,一個深度為n的樹最多生成n2個葉子。
- 如果定義了這個參數,GBM會忽略max_depth參數。
-
gamma
[default=0, alias:min_split_loss
]
可以理解為一般決策樹中分裂時候熵(或基尼系數)減少的量,大于這個量才進行分裂。- 在節點分裂時,只有分裂后損失函數的值下降了,才會分裂這個節點。Gamma指定了節點分裂所需的最小損失函數下降值。
- 這個參數的值越大,算法越保守。這個參數的值和損失函數息息相關,所以是需要調整的。
-
max_delta_step
[default=0]- 這參數限制每棵樹權重改變的最大步長。如果這個參數的值為0,那就意味著沒有約束。如果它被賦予了某個正值,那么它會讓這個算法更加保守。
- 通常,這個參數不需要設置。但是當各類別的樣本十分不平衡時,它對邏輯回歸是很有幫助的。
- 這個參數一般用不到,但是你可以挖掘出來它更多的用處。
-
subsample
[default=1]- 和GBM中的subsample參數一模一樣。這個參數控制對于每棵樹,隨機采樣的比例。
- 減小這個參數的值,算法會更加保守,避免過擬合。但是,如果這個值設置得過小,它可能會導致欠擬合。
- 典型值:0.5-1。
-
colsample_bytree
[default=1]- 和GBM里面的max_features參數類似。用來控制每棵隨機采樣的列數的占比(每一列是一個特征)。
- 典型值:0.5-1
-
colsample_bylevel
[default=1]- 用來控制樹的每一級的每一次分裂,對列數的采樣的占比。
- 我個人一般不太用這個參數,因為
subsample
參數和colsample_bytree
參數可以起到相同的作用。但是如果感興趣,可以挖掘這個參數更多的用處。
lambda
[default=1, alias:reg_lambda
]
權重的L2正則化項。(和Ridge regression類似)。
- 這個參數是用來控制XGBoost的正則化部分的。雖然大部分數據科學家很少用到這個參數,但是這個參數在減少過擬合上還是可以挖掘出更多用處的。
-
alpha
[default=0, alias:reg_alpha
]- 權重的L1正則化項。(和Lasso regression類似)。
- 可以應用在很高維度的情況下,使得算法的速度更快。
-
scale_pos_weight
[default=1]- 在各類別樣本十分不平衡時,把這個參數設定為一個正值,可以使算法更快收斂。
- 通常可以將其設置為負樣本的數目與正樣本數目的比值。
2.3 學習目標參數
這個參數用來控制理想的優化目標和每一步結果的度量方法。
-
objective
[default=reg:squarederror]
這個參數定義需要被最小化的損失函數。最常用的值有:- reg:squarederror 均方誤差的回歸預測。
- binary:logistic 二分類的邏輯回歸,返回預測的概率(不是類別)。
- multi:softmax 使用softmax的多分類器,返回預測的類別(不是概率)。
- 在這種情況下,你還需要多設一個參數:num_class(類別數目)。
- multi:softprob 和multi:softmax參數一樣,但是返回的是每個數據屬于各個類別的概率。
-
eval_metric
[默認值取決于objective
參數的取值]- 對于有效數據的度量方法。
- 對于回歸問題,默認值是rmse,對于分類問題,默認值是error。
- 典型值有:
- rmse 均方根誤差
- mae 平均絕對誤差
- logloss 負對數似然函數值
- error 二分類錯誤率(閾值為0.5)
- merror 多分類錯誤率
- mlogloss 多分類logloss損失函數
- auc 曲線下面積
-
seed
[default=0]- 隨機數的種子
- 設置它可以復現隨機數據的結果,也可以用于調整參數
如果你之前用的是Scikit-learn,你可能不太熟悉這些參數。但是有個好消息,python的XGBoost模塊有一個sklearn包,XGBClassifier。這個包中的參數是按sklearn風格命名的。會改變的函數名是:
1、eta -> learning_rate
2、lambda -> reg_lambda
3、alpha -> reg_alpha
你肯定在疑惑為啥咱們沒有介紹和GBM中的n_estimators類似的參數。XGBClassifier中確實有一個類似的參數,但是,是在標準XGBoost實現中調用擬合函數時,把它作為num_boosting_rounds參數傳入。
XGBoost Guide 的一些部分是我強烈推薦大家閱讀的,通過它可以對代碼和參數有一個更好的了解:
XGBoost Parameters (official guide)
XGBoost Demo Codes (xgboost GitHub repository)
Python API Reference (official guide)
3、調參示例
我們從Data Hackathon 3.x AV版的hackathon中獲得數據集,和GBM 介紹文章中是一樣的。更多的細節可以參考competition page
數據集可以從這里下載。我已經對這些數據進行了一些處理:
- City變量,因為類別太多,所以刪掉了一些類別。
- DOB變量換算成年齡,并刪除了一些數據。
- 增加了 EMI_Loan_Submitted_Missing 變量。如果EMI_Loan_Submitted變量的數據缺失,則這個參數的值為1。否則為0。刪除了原先的EMI_Loan_Submitted變量。
- EmployerName變量,因為類別太多,所以刪掉了一些類別。
- 因為Existing_EMI變量只有111個值缺失,所以缺失值補充為中位數0。
- 增加了 Interest_Rate_Missing 變量。如果Interest_Rate變量的數據缺失,則這個參數的值為1。否則為0。刪除了原先的Interest_Rate變量。
- 刪除了Lead_Creation_Date,從直覺上這個特征就對最終結果沒什么幫助。
- Loan_Amount_Applied, Loan_Tenure_Applied 兩個變量的缺項用中位數補足。
- 增加了 Loan_Amount_Submitted_Missing 變量。如果Loan_Amount_Submitted變量的數據缺失,則這個參數的值為1。否則為0。刪除了原先的Loan_Amount_Submitted變量。
- 增加了 Loan_Tenure_Submitted_Missing 變量。如果 Loan_Tenure_Submitted 變量的數據缺失,則這個參數的值為1。否則為0。刪除了原先的 Loan_Tenure_Submitted 變量。
- 刪除了LoggedIn, Salary_Account 兩個變量
- 增加了 Processing_Fee_Missing 變量。如果 Processing_Fee 變量的數據缺失,則這個參數的值為1。否則為0。刪除了原先的 Processing_Fee 變量。
- Source前兩位不變,其它分成不同的類別。
- 進行了離散化和獨熱編碼(一位有效編碼)。
如果你有原始數據,可以從資源庫里面下載data_preparation的Ipython notebook 文件,然后自己過一遍這些步驟。
首先,import必要的庫,然后加載數據。
%matplotlib inline
import pandas as pd
import numpy as np
import xgboost as xgb
from xgboost.sklearn import XGBClassifier
# 也可以直接下面這樣,兩個接口用法基本都是一樣的
# from xgboost import XGBClassifier
from sklearn import metrics
from sklearn.model_selection import GridSearchCV, train_test_split
import matplotlib.pyplot as plt
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 12, 4
train = pd.read_csv('train_modified.csv')
target = 'Disbursed'
IDcol = 'ID'
注意我import了兩種XGBoost:
xgb - 直接引用xgboost。接下來會用到其中的“cv”函數。
XGBClassifier - 是xgboost的sklearn包。這個包允許我們像GBM一樣使用Grid Search 和并行處理。
在向下進行之前,我們先定義一個函數,它可以幫助我們建立XGBoost models 并進行交叉驗證。好消息是你可以直接用下面的函數,以后再自己的models中也可以使用它。
def modelfit(alg, dtrain, predictor, useTrainCV=True, cv_folds=5,
early_stopping_rounds=200):
if useTrainCV:
xgb_param = alg.get_xgb_params()
xgtrain = xgb.DMatrix(dtrain[predictors].values, label=dtrain[target].values)
cvresult = xgb.cv(xgb_param, xgtrain,
num_boost_round=alg.get_params()['n_estimators'],
nfold=cv_folds, metrics='auc',
early_stopping_rounds=early_stopping_rounds,
verbose_eval=False, show_stdv=True)
alg.set_params(n_estimators=cvresult.shape[0])
# 最佳 n_estimators數,也即樹的棵樹
print(cvresult.shape[0])
# Fit the algorithm on the data
alg.fit(dtrain[predictors], dtrain['Disbursed'], eval_metric='auc')
# Predict training set
dtrain_predictions = alg.predict(dtrain[predictors])
dtrain_predprob = alg.predict_proba(dtrain[predictors])[:,1]
# Print model report
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))
feat_imp = pd.Series(alg.get_booster().get_fscore()).sort_values(ascending=False)
feat_imp.plot(kind='bar', title='Feature Importance')
plt.ylabel('Feature Importance Score')
return cvresult
Python中的xgboost可以通過
get_fscore
和get_score
獲取特征重要性,其中特征重要性有多種度量方法,先看看官方對于這個方法的說明:
Get feature importance of each feature. Importance type can be defined as:
- ‘weight’: the number of times a feature is used to split the data across all trees.
- ‘gain’: the average gain across all splits the feature is used in.
- ‘cover’: the average coverage across all splits the feature is used in.
- ‘total_gain’: the total gain across all splits the feature is used in.
- ‘total_cover’: the total coverage across all splits the feature is used in.
get_fscore
應該是重要性度量為gain的返回值,而get_score
則有多種重要性衡量方法。
更詳細的說明可以參考xgboost特征重要性指標: weight, gain, cover
3.1 參數調優的一般方法
我們會使用和GBM中相似的方法。需要進行如下步驟:
- 選擇較高的學習速率(learning rate)。一般情況下,學習速率的值為0.1。但是,對于不同的問題,理想的學習速率有時候會在0.05到0.3之間波動。選擇對應于此學習速率的理想決策樹數量。XGBoost有一個很有用的函數“cv”,這個函數可以在每一次迭代中使用交叉驗證,并返回理想的決策樹數量。
- 對于給定的學習速率和決策樹數量,進行決策樹特定參數調優(max_depth, min_child_weight, gamma, subsample, colsample_bytree)。在確定一棵樹的過程中,我們可以選擇不同的參數,待會兒我會舉例說明。
- xgboost的正則化參數的調優。(lambda, alpha)。這些參數可以降低模型的復雜度,從而提高模型的表現。
- 降低學習速率,確定理想參數。
下面一起詳細地一步步進行這些操作。
第一步:確定學習速率和tree_based 參數調優的估計器數目
為了確定boosting參數,我們要先給其它參數一個初始值。咱們先按如下方法取值:
max_depth
= 5 :這個參數的取值最好在3-10之間。我選的起始值為5,但是你也可以選擇其它的值。起始值在4-6之間都是不錯的選擇。min_child_weight
= 1:在這里選了一個比較小的值,因為這是一個極不平衡的分類問題。因此,某些葉子節點下的值會比較小。gamma
= 0: 起始值也可以選其它比較小的值,在0.1到0.2之間就可以。這個參數后繼也是要調整的。subsample
,colsample_bytree
= 0.8: 這個是最常見的初始值了。典型值的范圍在0.5-0.9之間。scale_pos_weight
= 1: 這個值是因為類別十分不平衡。
請注意,上面這些參數的值只是一個初始的估計值,后繼需要調優。這里把學習速率就設成默認的0.1。然后用xgboost中的cv函數來確定最佳的決策樹數量。前文中的函數可以完成這個工作。
# Choose all predictors except target & IDcols
predictors = [x for x in train.columns if x not in [target, IDcol]]
xgb1 = XGBClassifier(learning_rate=0.1,
n_estimators=1000,
max_depth=5,
min_child_weight=1,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective='binary:logistic',
nthread=20,
scale_pos_weight=1,
seed=27)
res = modelfit(xgb1, train, predictors)
從輸出結果可以看出,在學習速率為0.1時,理想的決策樹數目是123。這個數字對你而言可能比較高,當然這也取決于你的系統的性能。
第二步: max_depth
和 min_weight
參數調優
我們先對這兩個參數調優,是因為它們對最終結果有很大的影響。首先,我們先大范圍地粗調參數,然后再小范圍地微調。
注意:在這一節我會進行高負荷的柵格搜索(grid search),這個過程大約需要15-30分鐘甚至更久,具體取決于你系統的性能。你也可以根據自己系統的性能選擇不同的值。
這里先寫個函數來更好的輸出gridsearch的結果:
def parse_cvres(cv_res, rank_method=0):
"""以更好看的方式輸出cv result.
rank_method: 0表示按均值排序,1表示按方差排序
"""
params_score = []
for rank in cv_res['rank_test_score']:
mean = round(cv_res['mean_test_score'][rank-1], 4)
std = round(cv_res['std_test_score'][rank-1], 4)
params = cv_res['params'][rank-1]
params_score.append([mean, std, params])
params_score = sorted(params_score, key=lambda x:x[rank_method],reverse=True)
for mean, std, params in params_score:
print(f"mean: {mean}, std: {std}, params: {params}")
網格搜索參數:
# Step 2: Tune max_depth and min_child_weight
param_test1 = {
'max_depth':range(3, 10, 2),
'min_child_weight':range(1,6,2)
}
gsearch1 = GridSearchCV(
estimator=XGBClassifier(
learning_rate=0.1, n_estimators=123,
gamma=0, subsample=0.8,
colsample_bytree=0.8, objective='binary:logistic',
nthread=20, scale_pos_weight=1, seed=27),
param_grid=param_test1, scoring='roc_auc', n_jobs=4,
iid=False, cv=5)
gsearch1.fit(train[predictors], train[target])
print(gsearch1.best_params_, gsearch1.best_score_)
parse_cvres(gsearch1.cv_results_)
至此,我們對于數值進行了較大跨度的12中不同的排列組合,可以看出理想的max_depth值為5,理想的min_child_weight值為1。在這個值附近我們可以再進一步調整,來找出理想值。我們把上下范圍各拓展1,因為之前我們進行組合的時候,參數調整的步長是2。
param_test2 = {
'max_depth': [4,5,6],
'min_child_weight': [0,1,2]
}
gsearch2 = GridSearchCV(
estimator=XGBClassifier(
learning_rate=0.1, n_estimators=123, gamma=0, subsample=0.8,
colsample_bytree=0.8, objective='binary:logistic',
nthread=20, scale_pos_weight=1, seed=27),
param_grid=param_test1, scoring='roc_auc', n_jobs=4,
iid=False, cv=5)
gsearch2.fit(train[predictors], train[target])
print(gsearch2.best_params_, gsearch2.best_score_)
parse_cvres(gsearch2.cv_results_)
至此,我們得到max_depth的理想取值為5,min_child_weight的理想取值為1。同時,我們還能看到cv的得分有了小小一點提高。需要注意的一點是,隨著模型表現的提升,進一步提升的難度是指數級上升的,尤其是你的表現已經接近完美的時候。
第三步:gamma
參數調優
在已經調整好其它參數的基礎上,我們可以進行gamma參數的調優了。Gamma參數取值范圍可以很大,我這里把取值范圍設置為0~0.5了。你其實也可以取更精確的gamma值。
# Step 3: Tune gamma
param_test3 = {
'gamma':[i/10.0 for i in range(0,5)]
}
gsearch3 = GridSearchCV(
estimator = XGBClassifier(
learning_rate =0.1, n_estimators=123, max_depth=5,
min_child_weight=1, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
param_grid = param_test3, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch3.fit(train[predictors],train[target])
print(gsearch3.best_params_, gsearch3.best_score_)
parse_cvres(gsearch3.cv_results_)
從這里可以看出來,我們在第一步調參時設置的初始gamma值就是比較合適的。也就是說,理想的gamma值為0。在這個過程開始之前,最好重新調整boosting回合,也即
n_estimators
數量,因為參數都有變化。
xgb2 = XGBClassifier(learning_rate =0.1,
n_estimators=1000,
max_depth=5,
min_child_weight=1,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective= 'binary:logistic',
nthread=20,
scale_pos_weight=1,
seed=27)
modelfit(xgb2, train, predictors)
跟最初沒有變化,有點小尷尬,不過這只是練習。
第四步:調整subsample
和 colsample_bytree
參數
下一步是嘗試不同的subsample 和 colsample_bytree 參數。我們分兩個階段來進行這個步驟。這兩個步驟都取0.6,0.7,0.8,0.9作為起始值。
# Step 4: Tune subsample and colsample_bytree
param_test4 = {
'subsample':[i/10.0 for i in range(6,10)],
'colsample_bytree':[i/10.0 for i in range(6,10)]
}
gsearch4 = GridSearchCV(
estimator = XGBClassifier(
learning_rate =0.1, n_estimators=177, max_depth=5,
min_child_weight=1, gamma=0, objective= 'binary:logistic',
nthread=20, scale_pos_weight=1,seed=27),
param_grid = param_test4, scoring='roc_auc',
n_jobs=-1, iid=False, cv=5)
gsearch4.fit(train[predictors],train[target])
print(gsearch4.best_params_, gsearch4.best_score_)
parse_cvres(gsearch4.cv_results_)
從這里可以看出來,
subsample
和 colsample_bytree
參數的理想取值分別是0.8和0.9。現在,我們以0.05為步長,在這個值附近嘗試取值。
param_test5 = {
'subsample': [i/100.0 for i in range(75,90,5)],
'colsample_bytree': [i/100.0 for i in range(85,100,5)]
}
gsearch5 = GridSearchCV(
estimator = XGBClassifier(
learning_rate =0.1, n_estimators=123, max_depth=5,
min_child_weight=1, gamma=0, objective= 'binary:logistic',
nthread=20, scale_pos_weight=1, seed=27),
param_grid = param_test5, scoring='roc_auc',n_jobs=-1,iid=False, cv=5)
gsearch5.fit(train[predictors],train[target])
print(gsearch5.best_params_, gsearch5.best_score_)
parse_cvres(gsearch5.cv_results_)
最佳取值發生了一點點小變化,最終取值是:
-
colsample_bytree
: 0.95 ; -
subsample
: 0.85。
第五步:正則化參數調優
下一步是應用正則化來降低過擬合。由于gamma
函數提供了一種更加有效地降低過擬合的方法,大部分人很少會用到這個參數。但是我們在這里也可以嘗試用一下這個參數。我會在這里調整reg_alpha
參數,然后reg_lambda
參數留給你來完成。
param_test6 = {
'reg_alpha':[0, 1e-5, 1e-2, 0.1, 1, 100]
}
gsearch6 = GridSearchCV(
estimator = XGBClassifier(
learning_rate =0.1, n_estimators=123, max_depth=5,
min_child_weight=1, gamma=0, subsample=0.85, colsample_bytree=0.95,
objective= 'binary:logistic', nthread=20, scale_pos_weight=1,seed=27),
param_grid = param_test6, scoring='roc_auc',n_jobs=-1,iid=False, cv=5)
gsearch6.fit(train[predictors],train[target])
print(gsearch6.best_params_, gsearch6.best_score_)
parse_cvres(gsearch6.cv_results_)
我們可以看到,相比之前的結果,CV的得分甚至還降低了。但是我們之前使用的取值是十分粗糙的,我們在這里選取一個比較靠近理想值(0.01)的取值,來看看是否有更好的表現。
param_test7 = {
'reg_alpha':[1e-6, 1e-5, 1e-4]
}
gsearch7 = GridSearchCV(
estimator = XGBClassifier(
learning_rate =0.1, n_estimators=123, max_depth=5,
min_child_weight=1, gamma=0, subsample=0.85, colsample_bytree=0.95,
objective= 'binary:logistic', nthread=20, scale_pos_weight=1,seed=27),
param_grid = param_test7, scoring='roc_auc',n_jobs=-1,iid=False, cv=5)
gsearch7.fit(train[predictors],train[target])
print(gsearch7.best_params_, gsearch7.best_score_)
parse_cvres(gsearch7.cv_results_)
第6步:降低學習速率
最后,我們使用較低的學習速率,以及使用更多的決策樹。我們可以用XGBoost中的CV函數來進行這一步工作。
# Step 6: Reducing Learning Rate
xgb3 = XGBClassifier(learning_rate =0.01,
n_estimators=5000,
max_depth=5,
min_child_weight=1,
gamma=0,
subsample=0.85,
colsample_bytree=0.95,
reg_alpha=0.0001,
objective= 'binary:logistic',
nthread=20,
scale_pos_weight=1,
seed=27)
modelfit(xgb3, train, predictors)
至此,你可以看到模型的表現有了小幅提升,調整每個參數帶來的影響也更加清楚了。
在文章的末尾,我想分享兩個重要的思想:
- 僅僅靠參數的調整和模型的小幅優化,想要讓模型的表現有個大幅度提升是不可能的。
- 要想讓模型的表現有一個質的飛躍,需要依靠其他的手段,諸如,特征工程(feature egineering) ,模型組合(ensemble of model),以及堆疊(stacking)等。
你可以從 這里 下載iPython notebook文件,里面包含了文章中提到的所有代碼。如果你使用R語言,請閱讀這篇文章。
結束語
這篇文章主要講了如何提升XGBoost模型的表現。首先,我們介紹了相比于GBM,為何XGBoost可以取得這么好的表現。緊接著,我們介紹了每個參數的細節。我們定義了一個可以重復使用的構造模型的函數。
最后,我們討論了使用XGBoost解決問題的一般方法,在AV Data Hackathon 3.x problem數據上實踐了這些方法。
總結一下,XGBoost調參指南:
- 1、先選擇一個較大的
n_estimators
,其余的參數可以先使用較常用的選擇或默認參數,然后借用xgboost自帶的cv
方法中的early_stop_rounds找到最佳n_estimators
; - 2、在第一步的基礎上調參
max_depth
和min_child_weight
; - 3、調節
gamma
。調完之后可在這些參數基礎上可重新執行第一步調參步驟重新確定最佳n_estimators
,當然這一步其實可以在每一步調參后執行,但是為了節約時間減少麻煩,還是幾步一回調比較好; - 4、調節
subsample
和colsample_bytree
; - 5、調節正則化參數;
- 6、最后還可以重復上面的步驟繼續微調。
希望看過這篇文章之后,你能有所收獲,下次使用XGBoost解決問題的時候可以更有信心。
參考:
Complete Guide to Parameter Tuning in XGBoost
XGBoost參數調優完全指南
XGBoost Parameters 官方文檔
XGBoost Python Package 官方文檔
XGBoost Python Example 官方示例