XGBoost演算法原理簡介及調參
簡介
當模型沒有達到預期效果的時候,XGBoost就是資料科學家的最終武器。XGboost是一個高度複雜的演算法,有足夠的能力去學習資料的各種各樣的不規則特徵。
用XGBoost建模很簡單,但是提升XGBoost的模型效果卻需要很多的努力。因為這個演算法使用了多維的引數。為了提升模型效果,調參就不可避免,但是想要知道引數怎麼調,什麼樣的引數能夠得出較優的模型輸出就很困難了。
這篇文章適用於XGBoost新手,教會新手學習使用XGBoost的一些有用資訊來幫忙調整引數。
What should you know?
XGBoost(extreme Gradient Boosting) 是一個高階的梯度增強演算法(gradient boosting algorithm),推薦看一下我前一篇翻譯的自該作者的
XGBoost 的優勢
- Regularization:
- 標準的GBM並沒有XGBoost的Regularization,這個能幫助減少過擬合問題
- Parallel Processing:
- XGBoost實現了平行計算,與GBM相比非常快
- 但是基於序列的模型,新模型的建立是基於前面已經建立好的模型,如何能實現平行計算呢?探索一下吧
- XGBoost 支援在Hadoop上實現
- High Flexibility
- XGBoost允許使用者定製優化目標和評價標準
- 這一點將會使得我們對模型有很多可以改進的地方
- Handling Missing Values
- XGBoost有內嵌的處理缺失值的程式
- 其他模型中使用者被要求為缺失值提供相應的與其他值不同的值去填充缺失值,XGBoost會嘗試對缺失值進行分類,並學習這種分類
- Tree Pruning:
- GBM會停止對一個節點進行分裂,當其計算到這個節點的split的loss是負數時,GBM是一個貪婪演算法
- XGBoost的分類取決於max_depth,當樹的深度達到max_depth時,開始進行剪枝,移除沒有正基尼(no positive gain)節點的split
- 另一個優點是一個節點被分裂的時候loss為-2,當其二次分裂的時候loss可能為+10,GBM會停止該節點的分裂,XGBoost會進入到第二步,然後結合兩個分裂的影響,最終為+8
- Built-in Cross-Validation
- XGBoost允許每一個交叉驗證實現boosting過程,因而通過一次run就能獲得boosting迭代的優化量
- 與GBM需要運營grid-search且需要限時值的範圍獲得優化量不同
- Continue on Existing Model
- 使用者可以通過上個版本的XGBoost模型訓練新版本的模型
- GBM的sklearn也有這個特性
XGBoost Parameters
XGBoost的變數型別有三類:
- General Parameters:調控整個方程
- Booster Parameters:調控每步樹的相關變數
- Learning Task Parameters:調控優化表現的變數
1.General Parameters:
booster [default=gbtree]:
- gbtree: tree-based models,樹模型
- gblinear: linear models,線性模型
silent [default=0]:
- 設定成1表示列印執行過程中的相關資訊
- 通常選擇預設值就好,打印出的資訊能夠幫助理解model
- nthread [default to maximum number of threads available if not set]
- 主要用於平行計算,系統的核心數需要作為變數
- 如果希望執行所有的核心,就不需要設定該引數,程式會自己檢測到該值
2.Booster Parameters
雖然XGBoost有兩種boosters,作者在引數這一塊只討論了tree booster,原因是tree booster的表現總是好於 linear booster
- eta [default=0.3]
- 與GBM中學習率的概念相似
- 通過減小每一步的權重能夠使得建立的模型更魯棒
- 通常最終的數值範圍在[0.01-0.2]之間
- min_child_weight [default=1]
- 定義觀測樣本生成的孩子節點的權重最小和
- 這個概念與GBM中的min_child_leaf概念類似,但是又不完全一樣,這個概念指的是某觀測葉子節點中所有樣本權重之和的最小值,而GBM指的是葉子節點的最少樣本量
- 用於防止過擬合問題:較大的值能防止過擬合,過大的值會導致欠擬合問題
- 需要通過CV調參
- max_depth [default=6]
- 樹的最大深度
- 用於防止過擬合問題
- 通過CV調參
- 通常值的範圍:[3-10]
- max_leaf_nodes
- 一棵樹最多的葉子節點的數目
- 與max_depth定義一個就好
- gamma [default=0]
- 一個節點分裂的條件是其分裂能夠起到降低loss function的作用,gamma 定義loss function降低多少才分裂
- 這個變數使得演算法變得保守,它的值取決於 loss function需要被調節
- max_delta_step [default=0]
- 此變數的設定使得我們定義每棵樹的權重估計值的變化幅度。如果值為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]
- l2正則化權重的術語(同 Ridge regression)
- 用於處理XGBoost裡的正則化部分,雖然很多資料科學家不怎麼使用這個引數,但是它可以用於幫助防止過擬合
- alpha [default=0]
- l1正則化的權重術語(同Lasso regression)
- 當特徵量特別多的時候可以使用,這樣能加快演算法的執行效率
- scale_pos_weight [default=1]
- 當樣本不平衡時,需要設定一個大於0的數幫助演算法儘快收斂
3.Learning Task Parameters
此類變數用於定義優化目標每一次計算的需要用到的變數
- objective [default=reg:linear]
- 用於定義loss function,通常有以下幾類
- binary:logistic-用於二分類,返回分類的概率而不是類別(class)
- multi:softmax-多分類問題,返回分類的類別而不是概率
- multi:softprob-與softmax類似,但是返回樣本屬於每一類的概率
- eval_metric [ default according to objective ]
- 這個變數用於 測試資料(validation data.)
- 預設值:迴歸-rmse;分類-error
- 通常值如下:
- rmse – root mean square error
- mae – mean absolute error
- logloss – negative log-likelihood
- error – Binary classification error rate (0.5 threshold)
- merror – Multiclass classification error rate
- mlogloss – Multiclass logloss
- auc: Area under the curve
- seed [default=0]
- 隨機種子的值
有些變數在Python的sklearn的介面中對應命名如下:
1. eta -> learning rate
2. lambda ->reg_lambda
3. alpha -> reg_alpha
可能感到困惑的是這裡並沒有像GBM中一樣提及n_estimators,這個引數實際存在於XGBClassifier中,但實際是通過num_boosting_rounds在我們呼叫fit函式事來體現的。
XGBoost 調參步驟
#匯入需要的資料和庫
#Import libraries:
import pandas as pd
import numpy as np
import xgboost as xgb
from xgboost.sklearn import XGBClassifier
from sklearn import cross_validation, metrics #Additional scklearn functions
from sklearn.grid_search import GridSearchCV #Perforing grid search
import matplotlib.pylab as plt
%matplotlib inline
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 12, 4
train = pd.read_csv('train_modified.csv')
target = 'Disbursed'
IDcol = 'ID'
此處作者呼叫了兩種型別的XGBoost:
1.xgb:xgboost直接的庫,可以呼叫cv函式
2.XGBClassifier: sklearn對XGBoost的包裝,可以允許使用sklearn的網格搜尋功能進行平行計算
#定義一個函式幫助產生xgboost模型及其效果
def modelfit(alg, dtrain, predictors,useTrainCV=True, cv_folds=5, early_stopping_rounds=50):
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, show_progress=False)
alg.set_params(n_estimators=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.booster().get_fscore()).sort_values(ascending=False)
feat_imp.plot(kind='bar', title='Feature Importances')
plt.ylabel('Feature Importance Score')
#xgboost’s sklearn沒有feature_importances,但是#get_fscore() 有相同的功能
General Approach for Parameter Tuning
通常的做法如下:
1.選擇一個相對高一點的學習率(learning rate):通常0.1是有用的,但是根據問題的不同,可以選擇範圍在[0.05,0.3]之間,根據選好的學習率選擇最優的樹的數目,xgboost有一個非常有用的cv函式可以用於交叉驗證並能返回最終的最優樹的數目
2.調tree-specific parameters(max_depth, min_child_weight, gamma, subsample, colsample_bytree)
3.調regularization parameters(lambda, alpha)
4.調低學習率並決定優化的引數
step1:Fix learning rate and number of estimators for tuning tree-based parameters
1.設定引數的初始值:
- max_depth = 5 : [3,10],4-6都是不錯的初始值的選擇
- min_child_weight = 1 : 如果資料是不平衡資料,初始值設定最好小於1
- gamma = 0 : 初始值通常設定在0.1-0.2範圍內,並且在後續的調參中也會經常被調節
- subsample, colsample_bytree = 0.8 : 通常使用0.8作為調參的開始引數,調整範圍為[0.5-0.9]
- scale_pos_weight = 1:因為作者的資料為高度不平衡資料
#通過固定的學習率0.1和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=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb1, train, predictors)
#作者調整後得到的樹的值為140,如果這個值對於當前的系統而言太大了,可以調高學習率重新訓練
step2: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=140, 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_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_
最優的 max_depth=5,min_child_weight=5
因為之前的步長是2,在最優引數的基礎上,在上調下調各一步,看是否能得到更好的引數
param_test2 = {
'max_depth':[4,5,6],
'min_child_weight':[4,5,6]
}
gsearch2 = GridSearchCV(estimator = XGBClassifier( learning_rate=0.1, n_estimators=140, max_depth=5,
min_child_weight=2, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
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_
以上結果跑出來的最優引數為:max_depth=4,min_child_weight=6,另外從作者跑出來的cv結果看,再提升結果比較困難,可以進一步對min_child_weight試著調整看一下效果:
param_test2b = {
'min_child_weight':[6,8,10,12]
}
gsearch2b = GridSearchCV(estimator = XGBClassifier( learning_rate=0.1, n_estimators=140, max_depth=4,
min_child_weight=2, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
param_grid = param_test2b, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch2b.fit(train[predictors],train[target])
modelfit(gsearch3.best_estimator_, train, predictors)
gsearch2b.grid_scores_, gsearch2b.best_params_, gsearch2b.best_score_
step3:Tune gamma
param_test3 = {
'gamma':[i/10.0 for i in range(0,5)]
}
gsearch3 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=140, max_depth=4,
min_child_weight=6, 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])
gsearch3.grid_scores_, gsearch3.best_params_, gsearch3.best_score_
基於以上調好引數的前提下,可以看一下模型的特徵的表現:
xgb2 = XGBClassifier(
learning_rate =0.1,
n_estimators=1000,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb2, train, predictors)
step4: 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=4,
min_child_weight=6, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
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_
#上一步發現最優值均為0.8,這一步做的事情是在附近以0.05的步長做調整
param_test5 = {
'subsample':[i/100.0 for i in range(75,90,5)],
'colsample_bytree':[i/100.0 for i in range(75,90,5)]
}
gsearch5 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=4,
min_child_weight=6, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
param_grid = param_test5, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch5.fit(train[predictors],train[target])
Step 5: Tuning Regularization Parameters
這一步的作用是通過使用過regularization 來降低過擬合問題,大部分的人選擇忽略這個引數,因為gamma 有提供類似的功能
param_test6 = {
'reg_alpha':[1e-5, 1e-2, 0.1, 1, 100]
}
gsearch6 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=4,
min_child_weight=6, gamma=0.1, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
param_grid = param_test6, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch6.fit(train[predictors],train[target])
gsearch6.grid_scores_, gsearch6.best_params_, gsearch6.best_score_
這一步調參之後結果可能會變差,方法是在獲得的最優的引數0.01附近進行微調,看能否獲得更好的結果
param_test7 = {
'reg_alpha':[0, 0.001, 0.005, 0.01, 0.05]
}
gsearch7 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=4,
min_child_weight=6, gamma=0.1, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
param_grid = param_test7, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch7.fit(train[predictors],train[target])
gsearch7.grid_scores_, gsearch7.best_params_, gsearch7.best_score_
然後基於獲得的更好的值,我們再看一下模型的整體表現
xgb3 = XGBClassifier(
learning_rate =0.1,
n_estimators=1000,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.005,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb3, train, predictors)
Step 6: Reducing Learning Rate
最後一步就是 降低學習率並增加更多的樹
xgb4 = XGBClassifier(
learning_rate =0.01,
n_estimators=5000,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.005,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb4, train, predictors)
最後作者分享了兩條經驗:
1.僅僅通過調參來提升模型的效果是很難的
2.想要提升模型的效果,還可以通過特徵工程、模型融合以及stacking方法