數據比賽Kaggle,天池中最常見的就是XGBoost和LightGBM。
模型是在數據比賽中尤為重要的,但是實際上,在比賽的過程中,大部分朋友在模型上花的時間卻是相對較少的,大家都傾向於將寶貴的時間留在特征提取與模型融合這些方面。在實戰中,我們會先做一個baseline的demo,盡可能快盡可能多的挖掘出模型的潛力,以便后期將精力花在特征和模型融合上。這里就需要一些調參功底。
本文從這兩種模型的一共百余參數中選取重要的十余個進行探討研究。並給大家展示快速輕量級的調參方式。當然,有更高一步要求的朋友,還是得戳LightGBM和XGBoost這兩個官方文檔鏈接。
XGBoost 的重要參數
XGBoost的參數一共分為三類:
-
通用參數:宏觀函數控制。
-
Booster參數:控制每一步的booster(tree/regression)。booster參數一般可以調控模型的效果和計算代價。我們所說的調參,很這是大程度上都是在調整booster參數。
-
學習目標參數:控制訓練目標的表現。我們對於問題的划分主要體現在學習目標參數上。比如我們要做分類還是回歸,做二分類還是多分類,這都是目標參數所提供的。
通用參數
-
booster:我們有兩種參數選擇,
gbtree
和gblinear
。gbtree是采用樹的結構來運行數據,而gblinear是基於線性模型。 -
silent:靜默模式,為
1
時模型運行不輸出。 -
nthread: 使用線程數,一般我們設置成
-1
,使用所有線程。如果有需要,我們設置成多少就是用多少線程。
Booster參數
-
n_estimator: 也作
num_boosting_rounds
這是生成的最大樹的數目,也是最大的迭代次數。
-
learning_rate: 有時也叫作
eta
,系統默認值為0.3
,。每一步迭代的步長,很重要。太大了運行准確率不高,太小了運行速度慢。我們一般使用比默認值小一點,
0.1
左右就很好。 -
gamma:系統默認為
0
,我們也常用0
。在節點分裂時,只有分裂后損失函數的值下降了,才會分裂這個節點。
gamma
指定了節點分裂所需的最小損失函數下降值。 這個參數的值越大,算法越保守。因為gamma
值越大的時候,損失函數下降更多才可以分裂節點。所以樹生成的時候更不容易分裂節點。范圍:[0,∞]
-
subsample:系統默認為
1
。這個參數控制對於每棵樹,隨機采樣的比例。減小這個參數的值,算法會更加保守,避免過擬合。但是,如果這個值設置得過小,它可能會導致欠擬合。 典型值:
0.5-1
,0.5
代表平均采樣,防止過擬合. 范圍:(0,1]
,注意不可取0 -
colsample_bytree:系統默認值為1。我們一般設置成0.8左右。
用來控制每棵隨機采樣的列數的占比(每一列是一個特征)。 典型值:
0.5-1
范圍:(0,1]
-
colsample_bylevel:默認為1,我們也設置為1.
這個就相比於前一個更加細致了,它指的是每棵樹每次節點分裂的時候列采樣的比例
-
max_depth: 系統默認值為
6
我們常用
3-10
之間的數字。這個值為樹的最大深度。這個值是用來控制過擬合的。max_depth
越大,模型學習的更加具體。設置為0
代表沒有限制,范圍:[0,∞]
-
max_delta_step:默認
0
,我們常用0
.這個參數限制了每棵樹權重改變的最大步長,如果這個參數的值為
0
,則意味着沒有約束。如果他被賦予了某一個正值,則是這個算法更加保守。通常,這個參數我們不需要設置,但是當個類別的樣本極不平衡的時候,這個參數對邏輯回歸優化器是很有幫助的。 -
lambda:也稱
reg_lambda
,默認值為0
。權重的L2正則化項。(和Ridge regression類似)。這個參數是用來控制XGBoost的正則化部分的。這個參數在減少過擬合上很有幫助。
-
alpha:也稱
reg_alpha
默認為0
,權重的L1正則化項。(和Lasso regression類似)。 可以應用在很高維度的情況下,使得算法的速度更快。
-
scale_pos_weight:默認為
1
在各類別樣本十分不平衡時,把這個參數設定為一個正值,可以使算法更快收斂。通常可以將其設置為負樣本的數目與正樣本數目的比值。
學習目標參數
objective [缺省值=reg:linear]
-
reg:linear
– 線性回歸 -
reg:logistic
– 邏輯回歸 -
binary:logistic
– 二分類邏輯回歸,輸出為概率 -
binary:logitraw
– 二分類邏輯回歸,輸出的結果為wTx -
count:poisson
– 計數問題的poisson回歸,輸出結果為poisson分布。在poisson回歸中,max_delta_step的缺省值為0.7 (used to safeguard optimization) -
multi:softmax
– 設置 XGBoost 使用softmax目標函數做多分類,需要設置參數num_class(類別個數) -
multi:softprob
– 如同softmax,但是輸出結果為ndata*nclass的向量,其中的值是每個數據分為每個類的概率。
eval_metric [缺省值=通過目標函數選擇]
-
rmse
: 均方根誤差 -
mae
: 平均絕對值誤差 -
logloss
: negative log-likelihood -
error
: 二分類錯誤率。其值通過錯誤分類數目與全部分類數目比值得到。對於預測,預測值大於0.5被認為是正類,其它歸為負類。 error@t: 不同的划分閾值可以通過 ‘t’進行設置 -
merror
: 多分類錯誤率,計算公式為(wrong cases)/(all cases) -
mlogloss
: 多分類log損失 -
auc
: 曲線下的面積 -
ndcg
: Normalized Discounted Cumulative Gain -
map
: 平均正確率
一般來說,我們都會使用xgboost.train(params, dtrain)
函數來訓練我們的模型。這里的params
指的是booster
參數。
兩種基本的實例
我們要注意的是,在xgboost中想要進行二分類處理的時候,我們僅僅在 objective
中設置成 binary
,會發現輸出仍然是一堆連續的值。這是因為它輸出的是模型預測的所有概率中最大的那個值。我們可以后續對這些概率進行條件處理得到最終類別,或者直接調用xgboost
中的XGBClassifier()
類,但這兩種函數的寫法不太一樣。大家看我下面的例子。
from numpy import loadtxt from xgboost import XGBClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score # 導入數據 dataset = loadtxt('pima-indians-diabetes.csv', delimiter=",") # split data into X and y X = dataset[:, 0:8] Y = dataset[:, 8] # split data into train and test sets seed = 7 test_size = 0.33 X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=test_size, random_state=seed) # 設置參數 model = XGBClassifier(max_depth=15, learning_rate=0.1, n_estimators=2000, min_child_weight=5, max_delta_step=0, subsample=0.8, colsample_bytree=0.7, reg_alpha=0, reg_lambda=0.4, scale_pos_weight=0.8, silent=True, objective='binary:logistic', missing=None, eval_metric='auc', seed=1440, gamma=0) model.fit(X_train, y_train) # 進行預測 y_pred = model.predict(X_test) predictions = [round(value) for value in y_pred] # 查看准確率 accuracy = accuracy_score(y_test, predictions) print("Accuracy: %.2f%%" % (accuracy * 100.0))
以上是xgboost.train()
寫法,這是xgboost最原始的封裝函數。這樣訓練我們預測輸出的是一串連續值,是xgboost在這幾個類別上概率最大的概率值。我們如果想要得到我們的分類結果,還需要進行其他操作。
幸運的是,xgboost為了貼合sklearn的使用,比如gridsearch這些實用工具,又開發了XGBoostClassifier()
和XGBoostRegression()
兩個函數。可以更加簡單快捷的進行分類和回歸處理。注意xgboost的sklearn包沒有 feature_importance
這個量度,但是get_fscore()
函數有相同的功能。當然,為了和sklearn保持一致,寫法也發生變化,具體請看下面代碼:
import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score from sklearn.datasets import load_breast_cancer # 二分類解決乳腺癌 cancer = load_breast_cancer() x = cancer.data y = cancer.target train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0) # 分訓練集和驗證集 # 這里不需要Dmatrix xlf = xgb.XGBClassifier(max_depth=10, learning_rate=0.01, n_estimators=2000, silent=True, objective='binary:logistic', nthread=-1, gamma=0, min_child_weight=1, max_delta_step=0, subsample=0.85, colsample_bytree=0.7, colsample_bylevel=1, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=1440, missing=None) xlf.fit(train_x, train_y, eval_metric='error', verbose=True, eval_set=[(valid_x, valid_y)], early_stopping_rounds=30) # 這個verbose主要是調節系統輸出的,如果設置成10,便是每迭代10次就有輸出。 # 注意我們這里eval_metric=‘error’便是准確率。這里面並沒有accuracy命名的函數,網上大多例子為auc,我這里特意放了個error。 y_pred = xlf.predict(valid_x, ntree_limit=xlf.best_ntree_limit) auc_score = roc_auc_score(valid_y, y_pred) y_pred = xlf.predict(valid_x, ntree_limit=xlf.best_ntree_limit) # xgboost沒有直接使用效果最好的樹作為模型的機制,這里采用最大樹深限制的方法,目的是獲取剛剛early_stopping效果最好的,實測性能可以 auc_score = roc_auc_score(valid_y, y_pred) # 算一下預測結果的roc值
那么我們介紹了這么多,重點就來了:如何又快又好的調參?首先我們需要了解grid search是個什么原理。
GridSearch 簡介
這是一種調參手段;窮舉搜索:在所有候選的參數選擇中,通過循環遍歷,嘗試每一種可能性,表現最好的參數就是最終的結果。其原理就像是在數組里找最大值。(為什么叫網格搜索?以有兩個參數的模型為例,參數a有3種可能,參數b有4種可能,把所有可能性列出來,可以表示成一個3*4的表格,其中每個cell就是一個網格,循環過程就像是在每個網格里遍歷、搜索,所以叫grid search)
其實這個就跟我們常用的遍歷是一樣的。建議大家使用sklearn里面的GridSearch函數,簡潔速度快。
import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.datasets import load_breast_cancer from sklearn.model_selection import GridSearchCV cancer = load_breast_cancer() x = cancer.data[:50] y = cancer.target[:50] train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0) # 分訓練集和驗證集 # 這里不需要Dmatrix parameters = { 'max_depth': [5, 10, 15, 20, 25], 'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15], 'n_estimators': [50, 100, 200, 300, 500], 'min_child_weight': [0, 2, 5, 10, 20], 'max_delta_step': [0, 0.2, 0.6, 1, 2], 'subsample': [0.6, 0.7, 0.8, 0.85, 0.95], 'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9], 'reg_alpha': [0, 0.25, 0.5, 0.75, 1], 'reg_lambda': [0.2, 0.4, 0.6, 0.8, 1], 'scale_pos_weight': [0.2, 0.4, 0.6, 0.8, 1] } xlf = xgb.XGBClassifier(max_depth=10, learning_rate=0.01, n_estimators=2000, silent=True, objective='binary:logistic', nthread=-1, gamma=0, min_child_weight=1, max_delta_step=0, subsample=0.85, colsample_bytree=0.7, colsample_bylevel=1, reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=1440, missing=None) # 有了gridsearch我們便不需要fit函數 gsearch = GridSearchCV(xlf, param_grid=parameters, scoring='accuracy', cv=3) gsearch.fit(train_x, train_y) print("Best score: %0.3f" % gsearch.best_score_) print("Best parameters set:") best_parameters = gsearch.best_estimator_.get_params() for param_name in sorted(parameters.keys()): print("\t%s: %r" % (param_name, best_parameters[param_name])) #極其耗費時間,電腦沒執行完
我們需要注意的是,Grid Search 需要交叉驗證支持的。這里的cv=3
,是個int數,就代表3-折驗證。實際上cv可以是一個對象,也可以是其他類型。分別代表不同的方式驗證。具體的大家可看下面這段表述。
Possible inputs for cv are:
None, to use the default 3-fold cross-validation,
integer, to specify the number of folds.
An object to be used as a cross-validation generator.
An iterable yielding train/test splits.
cv的可能輸入包括:
None,使用默認的3倍交叉驗證,
用作交叉驗證生成器的對象。