模型神器組合,yyds!
↑ 關(guān)注 + 星標(biāo) ,每天學(xué)Python新技能
后臺回復(fù)【大禮包】送你Python自學(xué)大禮包
kaggle上有一個(gè)調(diào)參神器非常熱門,在top方案中頻頻出現(xiàn),它就是OPTUNA。知道很多小伙伴苦惱于漫長的調(diào)參時(shí)間里,這次結(jié)合一些自己的經(jīng)驗(yàn),給大家?guī)硪粋€(gè)LGBM模型+OPTUNA調(diào)參的使用教程,這對可謂是非常實(shí)用且容易上分的神器組合了,實(shí)際工作中也可使用。
LightGBM不多說了,之前分享過很多文章,它是在XGBoost基礎(chǔ)上對效率提升的優(yōu)化版本,由微軟發(fā)布的,運(yùn)行效率極高,且準(zhǔn)確度不降。目前是公認(rèn)比較好,且廣泛使用的機(jī)器學(xué)習(xí)模型了,分類回歸均可滿足。GridSearch。確實(shí)最開始我也在用GridSearch,暴力美學(xué)雖然好,但它的缺點(diǎn)很明顯,運(yùn)行太耗時(shí),時(shí)間成本太高。相比之下,基于貝葉斯框架下的調(diào)參工具就舒服多了。這類開源工具也很多,常見的比如HyperOPT。當(dāng)然今天主角不是它,而是另外一個(gè)更香的OPTUNA,輕量級且功能更強(qiáng)大,速度也是快到起飛!LGBM 配合舉例講解,下面先從 LGBM 的幾個(gè)主要超參數(shù)開始介紹,然后再根據(jù)這些超參設(shè)置 Optuna 進(jìn)行調(diào)參。LightGBM參數(shù)概述
影響決策樹結(jié)構(gòu)和學(xué)習(xí)的參數(shù) 影響訓(xùn)練速度的參數(shù) 提高精度的參數(shù) 防止過擬合的參數(shù)
Optuna 就可以自動找到這些類別之間最平衡的參數(shù)組合。LGBM的4類超參進(jìn)行介紹。1、控制樹結(jié)構(gòu)的超參數(shù)
LGBM 中,控制樹結(jié)構(gòu)的最先要調(diào)的參數(shù)是max_depth(樹深度) 和 num_leaves(葉子節(jié)點(diǎn)數(shù))。這兩個(gè)參數(shù)對于樹結(jié)構(gòu)的控制最直接了斷,因?yàn)?LGBM 是 leaf-wise 的,如果不控制樹深度,會非常容易過擬合。max_depth一般設(shè)置可以嘗試設(shè)置為3到8。num_leaves最大值應(yīng)該是2^(max_depth)。所以,確定了max_depth也就意味著確定了num_leaves的取值范圍。min_data_in_leaf,它的大小也與是否過擬合有關(guān)。它指定了葉子節(jié)點(diǎn)向下分裂的的最小樣本數(shù),比如設(shè)置100,那么如果節(jié)點(diǎn)樣本數(shù)量不夠100就停止生長。當(dāng)然,min_data_in_leaf的設(shè)定也取決于訓(xùn)練樣本的數(shù)量和num_leaves。對于大數(shù)據(jù)集,一般會設(shè)置千級以上。提高準(zhǔn)確性的超參數(shù)
LGBM中n_estimators和learning_rate的最佳組合。n_estimators控制決策樹的數(shù)量,而learning_rate是梯度下降的步長參數(shù)。經(jīng)驗(yàn)來說,LGBM 比較容易過擬合,learning_rate可以用來控制梯度提升學(xué)習(xí)的速度,一般值可設(shè)在 0.01 和 0.3 之間。一般做法是先用稍多一些的子樹比如1000,并設(shè)一個(gè)較低的learning_rate,然后通過early_stopping找到最優(yōu)迭代次數(shù)。max_bin(默認(rèn)值為255)來提高準(zhǔn)確率。因?yàn)樽兞糠窒涞臄?shù)量越多,信息保留越詳細(xì),相反,變量分箱數(shù)量越低,信息越損失,但更容易泛化。這個(gè)和特征工程的分箱是一個(gè)道理,只不過是通過內(nèi)部的hist直方圖算法處理了。如果max_bin過高,同樣也存在過度擬合的風(fēng)險(xiǎn)。更多超參數(shù)來控制過擬合
lambda_l1 和 lambda_l2 對應(yīng)著 L1 和 L2 正則化,和 XGBoost 的 reg_lambda 和 reg_alpha 是一樣的,對葉子節(jié)點(diǎn)數(shù)和葉子節(jié)點(diǎn)權(quán)重的懲罰,值越高懲罰越大。這些參數(shù)的最佳值更難調(diào)整,因?yàn)樗鼈兊拇笮∨c過擬合沒有直接關(guān)系,但會有影響。一般的搜索范圍可以在 (0, 100)。LGBM就會提示warning,無法找到可以分裂的了。參數(shù)含義和 XGBoost 的 gamma 是一樣,說明數(shù)據(jù)質(zhì)量已經(jīng)達(dá)到了極限了。比較保守的搜索范圍是 (0, 20),它可以用作大型參數(shù)網(wǎng)格中的額外正則化。(0,1)之間。feature_fraction指定訓(xùn)練每棵樹時(shí)要采樣的特征百分比,它存在的意義也是為了避免過擬合。因?yàn)橛行┨卣髟鲆婧芨撸赡茉斐擅靠米訕浞至训臅r(shí)候都會用到同一個(gè)特征,這樣每個(gè)子樹就同質(zhì)化了。而如果通過較低概率的特征采樣,可以避免每次都遇到一樣的強(qiáng)特征,從而讓子樹的特征變得差異化,即泛化。bagging_fraction指定用于訓(xùn)練每棵樹的訓(xùn)練樣本百分比。要使用這個(gè)參數(shù),還需要設(shè)置 bagging_freq,道理和feature_fraction一樣,也是讓沒棵子樹都變得好而不同。在 Optuna 中創(chuàng)建搜索網(wǎng)格
Optuna 中的優(yōu)化過程首先需要一個(gè)目標(biāo)函數(shù),該函數(shù)里面包括:字典形式的參數(shù)網(wǎng)格 創(chuàng)建一個(gè)模型(可以配合交叉驗(yàn)證 kfold)來嘗試超參數(shù)組合集用于模型訓(xùn)練的數(shù)據(jù)集 使用此模型生成預(yù)測 根據(jù)用戶定義的指標(biāo)對預(yù)測進(jìn)行評分并返回
Kfold,這樣可以保證模型的穩(wěn)定性。最后一行返回了需要優(yōu)化的 CV 分?jǐn)?shù)的平均值。目標(biāo)函數(shù)可以自己設(shè)定,比如指標(biāo)logloss最小,auc最大,ks最大,訓(xùn)練集和測試集的auc差距最小等等。import optuna # pip install optuna
from sklearn.metrics import log_loss
from sklearn.model_selection import StratifiedKFold
def objective(trial, X, y):
# 后面填充
param_grid = {}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=1121218)
cv_scores = np.empty(5)
for idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
model = lgbm.LGBMClassifier(objective="binary", **param_grid)
model.fit(
X_train,
y_train,
eval_set=[(X_test, y_test)],
eval_metric="binary_logloss",
early_stopping_rounds=100,
)
preds = model.predict_proba(X_test)
cv_scores[idx] = preds
return np.mean(cv_scores)
Optuna比較常見的參數(shù)設(shè)置方式有suggest_categorical,suggest_int,suggest_float。其中,suggest_int和suggest_float的設(shè)置方式為(參數(shù),最小值,最大值,step=步長)。def objective(trial, X, y):
# 字典形式的參數(shù)網(wǎng)格
param_grid = {
"n_estimators": trial.suggest_categorical("n_estimators", [10000]),
"learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3),
"num_leaves": trial.suggest_int("num_leaves", 20, 3000, step=20),
"max_depth": trial.suggest_int("max_depth", 3, 12),
"min_data_in_leaf": trial.suggest_int("min_data_in_leaf", 200, 10000, step=100),
"max_bin": trial.suggest_int("max_bin", 200, 300),
"lambda_l1": trial.suggest_int("lambda_l1", 0, 100, step=5),
"lambda_l2": trial.suggest_int("lambda_l2", 0, 100, step=5),
"min_gain_to_split": trial.suggest_float("min_gain_to_split", 0, 15),
"bagging_fraction": trial.suggest_float(
"bagging_fraction", 0.2, 0.95, step=0.1
),
"bagging_freq": trial.suggest_categorical("bagging_freq", [1]),
"feature_fraction": trial.suggest_float(
"feature_fraction", 0.2, 0.95, step=0.1
),
}
創(chuàng)建 Optuna 自動調(diào)起來
from optuna.integration import LightGBMPruningCallback
def objective(trial, X, y):
# 參數(shù)網(wǎng)格
param_grid = {
"n_estimators": trial.suggest_categorical("n_estimators", [10000]),
"learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3),
"num_leaves": trial.suggest_int("num_leaves", 20, 3000, step=20),
"max_depth": trial.suggest_int("max_depth", 3, 12),
"min_data_in_leaf": trial.suggest_int("min_data_in_leaf", 200, 10000, step=100),
"lambda_l1": trial.suggest_int("lambda_l1", 0, 100, step=5),
"lambda_l2": trial.suggest_int("lambda_l2", 0, 100, step=5),
"min_gain_to_split": trial.suggest_float("min_gain_to_split", 0, 15),
"bagging_fraction": trial.suggest_float("bagging_fraction", 0.2, 0.95, step=0.1),
"bagging_freq": trial.suggest_categorical("bagging_freq", [1]),
"feature_fraction": trial.suggest_float("feature_fraction", 0.2, 0.95, step=0.1),
"random_state": 2021,
}
# 5折交叉驗(yàn)證
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=1121218)
cv_scores = np.empty(5)
for idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):
X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
# LGBM建模
model = lgbm.LGBMClassifier(objective="binary", **param_grid)
model.fit(
X_train,
y_train,
eval_set=[(X_test, y_test)],
eval_metric="binary_logloss",
early_stopping_rounds=100,
callbacks=[
LightGBMPruningCallback(trial, "binary_logloss")
],
)
# 模型預(yù)測
preds = model.predict_proba(X_test)
# 優(yōu)化指標(biāo)logloss最小
cv_scores[idx] = log_loss(y_test, preds)
return np.mean(cv_scores)
LightGBMPruningCallback,這個(gè)callback類很方便,它可以在對數(shù)據(jù)進(jìn)行訓(xùn)練之前檢測出不太好的超參數(shù)集,從而顯著減少搜索時(shí)間。study = optuna.create_study(direction="minimize", study_name="LGBM Classifier")
func = lambda trial: objective(trial, X, y)
study.optimize(func, n_trials=20)
direction可以是minimize,也可以是maximize,比如讓auc最大化。然后可以設(shè)置trials來控制嘗試的次數(shù),理論上次數(shù)越多結(jié)果越優(yōu),但也要考慮下運(yùn)行時(shí)間。best_value和bast_params屬性,調(diào)參就出來了。print(f"\tBest value (rmse): {study.best_value:.5f}")
print(f"\tBest params:")
for key, value in study.best_params.items():
print(f"\t\t{key}: {value}")
-----------------------------------------------------
Best value (binary_logloss): 0.35738
Best params:
device: gpu
lambda_l1: 7.71800699380605e-05
lambda_l2: 4.17890272377219e-06
bagging_fraction: 0.7000000000000001
feature_fraction: 0.4
bagging_freq: 5
max_depth: 5
num_leaves: 1007
min_data_in_leaf: 45
min_split_gain: 15.703519227860273
learning_rate: 0.010784015325759629
n_estimators: 10000
得到這個(gè)參數(shù)組合后,我們就可以拿去跑模型了,看結(jié)果再手動微調(diào),這樣就可以省很多時(shí)間了。
結(jié)語
Optuna調(diào)參LGBM的代碼框架,使用及其方便,參數(shù)區(qū)間范圍需要根據(jù)數(shù)據(jù)情況自行調(diào)整,優(yōu)化目標(biāo)可以自定定義,不限于以上代碼的logloss。Optuna的強(qiáng)大之處,后面會對比同類的調(diào)參工具介紹,敬請期待
。
推薦閱讀
推薦一個(gè)公眾號,幫助程序員自學(xué)與成長
評論
圖片
表情

