LightGBM 参数及调优
翻譯自Understanding LightGBM Parameters (and How to Tune Them)
- 10 分鐘 閱讀
- 作者 MJ Bahmani
- 2022 年 1 月 25 日更新
我已經使用lightGBM有一段時間了。對于大多數扁平數據問題,這是我的首選算法。它有很多突出特性,我建議你瀏覽一下。
但我一直很想了解哪些參數對性能的影響最大,以及我應該如何調整 lightGBM 參數以充分利用它。
我想我應該做一些研究,更多地了解 lightGBM 參數…并分享我的研究過程。
具體來說我做了以下事項:
- 深入研究LightGBM 的文檔
- 瀏覽 Laurae 文章Lauraepp:xgboost / LightGBM 參數
- 查看LightGBM源代碼
- 進行一些實驗
在此過程中,我獲得了有關 lightGBM 參數的更多知識。我希望在閱讀完這篇文章后,您將能夠回答以下問題:
- LightGBM 中實現了哪些 Gradient Boosting 方法,它們有什么區別?
- 哪些參數通常很重要?
- 哪些正則化參數需要調整?
- 如何在 python 中調整 lightGBM 的參數?
梯度提升方法(Gradient Boosting methods)
使用 LightGBM,您可以通過boosting參數指定使用不同類型的 Gradient Boosting 方法,例如:GBDT、DART 和 GOSS
在接下來的部分中,我將解釋和比較這些方法。
lgbm gbdt(gradient boosted decision trees梯度提升決策樹)
該方法是傳統的梯度提升決策樹,首次在這篇文章中提出,XGBoost 和 pGBRT 等一些優秀算法庫都基于它實現。
如今,gbdt 因其準確、效率和穩定性而被廣泛使用。您可能知道 gbdt 是決策樹的集成模型,但它到底是什么意思?
相關文章:理解梯度裁剪(以及它如何解決爆炸梯度問題)
我總結的要點如下。
它基于三個重要原則:
- 弱學習者(決策樹decision trees)
- 梯度優化 (Gradient Optimization )
- 提升技術(Boosting Technique)
基于第一個原則, gbdt 方法中有很多決策樹(弱學習器)。這些樹是按如下順序構建的:
- 第一棵樹學習如何擬合目標變量
- 第二棵樹學習如何擬合第一棵樹的預測值和真實值之間的殘差(差異)
- 第三棵樹學習如何擬合第二棵樹的殘差,依此類推。
所有這些樹都是通過在整個系統中傳播誤差梯度來訓練的。
gbdt 的主要缺點是在每個樹節點中找到最佳分割點是耗時且消耗內存的操作,其他 Gradient Boosting 方法試圖解決該問題。
DART gradient boosting
在這篇出色的論文中,您可以了解有關 DART 梯度提升的所有信息,它通過使用神經網絡中的 dropout、standard方法, 來改進模型正則化并處理一些其他less-obvious方法。
也就是說,gbdt 存在過擬合的問題,也就是在迭代末尾處添加的樹往往只會影響少數實例的預測,而對剩余實例的預測影響甚微。添加 dropout 會使后續迭代中的樹更泛化,從而提高性能。
lgbm goss(基于梯度的單側采樣)
lightgbm名字的由來最重要的原因就是使用了基于這篇論文的Goss方法,而Goss 是更新和更輕(lighter)的 gbdt 實現(因此是light gbm)。
標準 gbdt 是可靠的,但在大型數據集上不夠快。因此,goss 提出了一種基于梯度的采樣方法,以避免搜索整個搜索空間。我們知道,對于每個數據實例,當梯度較小時,意味著不用擔心數據是否經過良好訓練,而當梯度較大時,應該再次重新訓練。所以我們這里有兩類數據實例,具有大梯度和小梯度的數據實例。因此,goss 保留所有具有大梯度的數據,并對具有小梯度的數據進行隨機抽樣(這就是為什么它被稱為 One-Side Sampling)。這使得搜索空間更小,goss 可以更快地收斂。最后,為了更深入地了解 goss,您可以查看這篇博文。
讓我們將這些差異放在一個表格中:
| Lgbm gbdt | 這是默認的boosting方式 | 因為 gbdt 是 lgbm 的默認參數,所以您不必為它更改其余參數的值(但是tuning仍是必須的!) | 過擬合、耗時、耗內存 | |
| Lgbm dart | 嘗試解決gbdt中過擬合的問題 | drop_seed: 選擇dropping models 的隨機seed uniform_dro: 如果你想使用uniform drop設置為true, xgboost_dart_mode: 如果你想使用xgboost dart mode設置為true, skip_drop: 在boosting迭代中跳過dropout過程的概率 drop_rate: dropout率,在 dropout 期間要丟棄的先前樹的一小部分? | 更高的準確率 | 配置太多 |
| Lgbm goss | goss 通過分離出具有較大梯度的實例,為 GBDT 提供了一種新的采樣方法 | top_rate:大梯度數據的保留率 other_rate:小梯度數據的保留率 | 更快的收斂速度 | 數據集較小時過擬合 |
筆記:如果將 boosting 設置為 RF,則 lightgbm 算法的行為類似于隨機森林,而不是 boosting 樹!根據文檔,要使用 RF,您必須使用 bagging_fraction 和 feature_fraction 小于 1。
正則化
在本節中,我將介紹 lightgbm 的一些重要的正則化參數。顯然,這些是您需要調整以對抗過度擬合的參數。
您應該知道,對于小型數據集(<10000 條記錄),lightGBM 可能不是最佳選擇。調整 lightgbm 參數可能對您沒有幫助。
此外,lightgbm 使用leaf-wise樹生長算法,而 XGBoost 使用了depth-wise樹生長算法。Leaf-wise 方法允許樹更快地收斂,但增加了過度擬合的風險。
也許來自 PyData 會議之一的這個演講讓您對 Xgboost 和 Lightgbm 有更多的了解。值得觀看!
Note:如果有人問你 LightGBM 和 XGBoost 的主要區別是什么?您可以輕松地說,它們的區別在于它們的實現方式。
根據lightGBM 文檔,當面臨過度擬合時,您可能需要進行以下參數調整:
- 使用小的 max_bin
- 使用小的 num_leaves
- 使用 min_data_in_leaf 和 min_sum_hessian_in_leaf
- 通過設置 bagging_fraction 和 bagging_freq 使用 bagging
- 通過設置 feature_fraction 使用特征子采樣
- 使用更大的訓練數據
- 嘗試使用 lambda_l1、lambda_l2 和 min_gain_to_split 進行正則化
- 嘗試使用 max_depth 來避免生長深樹
在接下來的部分中,我將更詳細地解釋這些參數中的每一個。
lambda_l1
Lambda_l1(和 lambda_l2)對 l1/l2 的控制以及與 min_gain_to_split 一起用于對抗過度擬合。我強烈建議您通過調參(在后面的部分中探討)來找出這些參數的最佳值。
num_leaves
當然num_leaves是控制模型復雜性的最重要參數之一。使用它,您可以設置每個弱學習器擁有的最大葉子數。大的 num_leaves 增加了訓練集的準確性,也增加了過擬合的風險。根據文檔,一種簡單的計算方法是num_leaves = 2^(max_depth)但是,考慮到在 lightgbm 中,葉子樹比層次樹更深,您需要小心過度擬合!因此,需要將num_leaves與max_depth一起調優。
? lightgbm 文檔上的照片
subsample
使用subsample(或 bagging_fraction),您可以指定每次樹構建迭代使用的行的百分比。這意味著將隨機選擇一些行來擬合每個學習器(樹)。這提高了泛化能力,并且也提高了訓練速度。
我建議對基線模型使用較小的子樣本值,然后在完成其他實驗(不同的特征選擇,不同的樹結構)后增加這個值。
feature_fraction
feature_fraction或 sub_feature 處理列采樣,LightGBM 將在每次迭代(樹)上隨機選擇特征子集。例如,如果將其設置為 0.6,LightGBM 將在訓練每棵樹之前選擇 60% 的特征。
此功能有兩種用法:
- 可用于加速訓練
- 可用于處理過擬合
max_depth
此參數控制每個訓練樹的最大深度,并將影響:
- num_leaves 參數的最佳值
- 模型表現
- 訓練時間
注意如果您使用較大的max_depth值,您的模型可能會過擬合 。
max_bin
分箱是一種在離散視圖(直方圖)中表示數據的技術。Lightgbm 使用基于直方圖的算法在創建弱學習器的同時找到最佳分割點。因此,每個連續的數字特征(例如視頻的觀看次數)都應該被分成離散的 bin。
LightGBM 和 XGBoost上的照片解釋
此外,在這個GitHub r epo 中,您可以找到一些全面的實驗,這些實驗充分解釋了更改 max_bin 對 CPU 和 GPU 的影響。
500 次迭代后的時鐘時間 – GitHub 存儲庫
如果您將 max_bin 定義為 255,則意味著每個特征最多可以有 255 個唯一值。較小的 max_bin 會帶來更快的速度,較大的值會提高準確性。
訓練參數
當你想訓練你的 lightgbm時 ,可能會遇到一些典型問題:
- 訓練是一個耗時的過程-num_iterations
- 處理計算復雜度(CPU/GPU RAM 限制)-early_stop_rounds
- 處理分類特征-categorical_feature
- 處理樣本不均衡問題-unbalanced dataset
- 對自定義指標的需求-feval
- 需要針對分類或回歸問題進行的調整
在本節中,我們將嘗試詳細解釋這些要點。
num_iterations(迭代次數)
num_iterations 指定提升迭代的次數(要構建的樹)別名num_boost_round。您構建的樹越多,您的模型就越準確,其代價是:
- 訓練時間更長
- 過擬合的可能性更高
從較少數量的樹開始構建基線,然后通過增加樹的數量擠壓出性能。
建議使用較小的learning_rate和較大的num_iterations。此外,使用 early_stopping_rounds參數(早停法),解決num_iterations過高卻沒有學到任何有用東西的問題。
early_stopping_rounds
如果驗證指標在最后一輪提前停止后沒有改善,則此參數將停止訓練。這應該與迭代次數成對定義。如果將其設置得太大,則會增加過度擬合的機會(但您的模型可能會更好)。
經驗法則是將其設置為 num_iterations 的 10%。
lightgbm categorical_feature
使用 lightgbm 的優點之一是它可以很好地處理分類特征。是的,這個算法非常強大,但是你必須小心如何使用它的參數。lightgbm 使用一種特殊的整數編碼方法(由Fisher提出)來處理分類特征
實驗表明,這種方法比常用的one-hot encoding帶來更好的性能。
它的默認值是“auto”,這意味著:讓 lightgbm 決定這意味著 lightgbm 將推斷哪些特征是分類的。
它并不總是很好用(一些實驗說明了為什么在這里和這里),我強烈建議您使用此代碼手動設置分類特征
cat_col = dataset_name.select_dtypes(‘object’).columns.tolist()
但是幕后發生了什么以及 lightgbm 如何處理分類特征?
根據 lightgbm 的文檔,我們知道樹學習器不能很好地使用一種熱編碼方法,因為它們在樹中生長得很深。在所提出的替代方法中,樹學習器是最優構造的。例如,對于具有 k 個不同類別的一個特征,有 2^(k-1) – 1 個可能的分區,并且使用Fisher方法可以 通過在值的排序直方圖上找到最佳分割方式來改進**k * log(k)**在分類特征中。
lightgbm is_unbalance vs scale_pos_weight
您在二元分類問題中可能面臨的問題之一是如何處理不平衡的數據集。顯然,您需要平衡正/負樣本,但您如何在 lightgbm 中做到這一點?
lightgbm 中有兩個參數可以讓你處理這個問題is_unbalance 和 scale_pos_weight,但它們之間有什么區別以及如何使用它們?
- 當您設置 Is_unbalace: True 時,算法將嘗試自動平衡主導標簽的權重(與訓練集中的 pos/neg 分數)
- 如果您想更改scale_pos_weight(默認為 1,這意味著假設正標簽和負標簽相等)以防不平衡數據集,您可以使用以下公式(基于 lightgbm 存儲庫上的這個問題)正確設置它
sample_pos_weight = 負樣本數/正樣本數
lgbm feval
有時你想定義一個自定義的評估函數來衡量你的模型的性能,你需要創建一個feval函數。
Feval 函數應該接受兩個參數:
- preds
- train_data
并返回
- eval_name
- eval_result
- is_higher_better
讓我們逐步創建一個自定義指標函數。
定義一個單獨的python函數
def feval_func (preds, train_data) : # 定義一個計算結果return ( 'feval_func_name' , eval_result, False )將此函數用作參數:
print( '開始訓練...' ) lgb_train = lgb.train(...,metric=None,feval=feval_func)Note:要使用 feval 函數而不是 metric,您應該將 metric 參數設置為“None”。
分類參數與回歸參數
我之前提到的大多數事情對于分類和回歸都是正確的,但有些事情需要調整。
具體來說,您應該:
| objective | binary或者multiclass | regression |
| metric | Binary_logloss、AUC或其他 | RMSE、mean_absolute_error或其他 |
| is_unbalance | True 或者 false | — |
| scale_pos_weight | 僅在二分類或多分類中使用 | — |
| num_class | 僅在多分類中使用 | — |
| reg_sqrt | — | 用于擬合 sqrt(標簽) 而不是大范圍標簽的原始值 |
最重要的lightgbm參數
我們已經在前面的部分中回顧并了解了一些關于 lightgbm 參數的知識,但是有關于提升樹的文章,卻沒有提及來自 Laurae 的令人印象深刻的基準測試,都是不完整的。
您可以了解 lightGBM 和 XGBoost 的許多問題的最佳默認參數。
你可以在這里查看,但一些最重要的要點是:
| objective | regression | regression, binary | 枚舉值 | objective_type,app | 當你改變它會影響其他參數 | 指定模型類型 |
| metric | null | 20多種參數 | 多枚舉 | metrics, metric_types | null 表示將自動使用與指定objective對應的指標 | 指定metric,支持多個metric |
| boosting | gbdt | gbdt, rf, dart, goss | 枚舉值 | boosting_type | 如果您將其設置為rf,相當于使用bagging approach方法 | boosting方法 |
| lambda_l1 | 0.0 | [0, ∞] | 浮點數 | reg_alpha | lambda_l1 >= 0.0 | 正則化 |
| bagging_fraction | 1 | [0, 1] | 浮點數 | subsample | 0.0 < bagging_fraction <= 1.0 | 隨機選擇部分數據而不重新采樣 |
| bagging_freq | 0 | [0, ∞] | 整形 | subsample_freq | 要啟用 bagging,bagging_fraction 也應設置為小于 1.0 的值 | 0 表示禁用 bagging;k 表示在每 k 次迭代中執行 bagging |
| num_leaves | 31 | [1, ∞] | 整形 | num_leaf | 1 < num_leaves <= 131072 | 指定一棵樹的最大葉子數 |
| feature_fraction | 1.0 | [0,1] | 浮點數 | sub_feature | 0.0 < feature_fraction <= 1.0 | 如果將其設置為 0.8,LightGBM 將選擇 80% 的特征 |
| max_depth | -1 | [-1, ∞] | 整形 | max_depth | 越大通常越好,但過擬合風險會增加 | 限制樹模型的最大深度 |
| max_bin | 255 | [2, ∞] | 整形 | histogram binning | max_bin > 1 | eal with over-fitting? |
| num_iterations | 100 | [1, ∞] | 整形 | num_boost_round, n_iter | num_iterations >= 0 | boosting迭代次數 |
| learning_rate | 0.1 | [0, 1] | 浮點數 | eta | learning_rate > 0.0,常用:0.05 | 在 dart 中,它也會影響dropped trees的歸一化權重 |
| early_stopping_round | 0 | [0, ∞] | 浮點數 | early_stopping_round | 如果validation在最近一次early_stopping 中沒有改善,將停止訓練 | 模型性能、迭代次數、訓練時間 |
| categorical_feature | 空字符串 | 指定列索引值 | 多整數或字符串 | cat_feature | — | 處理類別特征 |
| bagging_freq | 0 | [0, ∞] | 整形 | subsample_freq | 0表示禁用 bagging;k 表示在每 k 次迭代中執行 bagging | 要啟用 bagging,bagging_fraction 也應設置為小于 1的值 |
| verbosity | 0 | [-∞, ∞] | 整形 | verbose | < 0: Fatal, = 0: Error (Warning), = 1: Info, > 1: Debug | 調試時使用 |
| min_data_in_leaf | 20 | min_data | 整形 | min_data | min_data_in_leaf >= 0 | 可用于處理過擬合 |
Note:您永遠不應將任何參數值設為默認,應該根據您的問題進行調整。也就是說,這些參數是您的調整算法超參數的一個很好的起點
也可以看看
可視化機器學習實驗的指標和超參數的最佳工具
Python 中的超參數調整:完整指南 2020
python中Lightgbm參數調優示例(lightgbm調優)
最后,在解釋了所有重要參數之后,是時候進行一些實驗了!
我將使用流行的 Kaggle 競賽之一:Santander Customer Transaction Prediction。
我將使用這篇文章來解釋如何在 Python中對任何腳本運行超參數調整。
值得一讀!
在我們開始之前,一個重要的問題!我們應該調整哪些參數?
- 注意您要解決的問題,例如 Santander 數據集高度不平衡,并且應該在調整時考慮到這一點! Laurae2lightgbm的貢獻者之一在這里很好地解釋了這一點。
- 有些參數是相互依賴的,必須一起調整或按個調整。例如,min_data_in_leaf 取決于訓練樣本的數量和 num_leaves。
Note:為超參數創建兩個字典是個好主意,一個包含您不想調整的參數和值,另一個包含您想要調整的參數和值范圍。
SEARCH_PARAMS = { 'learning_rate':0.4,'max_depth':15,'num_leaves':20,'feature_fraction':0.8,'subsample':0.2 }FIXED_PARAMS={ 'objective' : 'binary' ,'metric' : 'auc' ,'is_unbalance' : True ,'boosting' : 'gbdt' ,'num_boost_round' : 300 ,'early_stopping_rounds' : 30 }通過這樣做,您可以將基線值與搜索空間分開!
請注意,由于最近的API 更新,這篇文章也需要一些更改——我們正在努力!同時,請查看Neptune 文檔,其中所有內容都是最新的!
現在,這就是我們要做的。
第一步,我們在Notebook中生成代碼。它是公開的,您可以下載它。
第二步,我們在Neptune.ai上跟蹤每個實驗的結果。
見Naptune
可能有用: 如何借助 Neptune-LightGBM 集成跟蹤模型訓練元數據
結果分析
如果您看了上一節,您會注意到我已經對數據集進行了超過 14 次不同的實驗。在這里,我將解釋如何逐步調整超參數的值。
創建基線訓練代碼:
from sklearn.metrics import roc_auc_score, roc_curve from sklearn.model_selection import train_test_split import neptunecontrib.monitoring.skopt as sk_utils import lightgbm as lgb import pandas as pd import neptune import skopt import sys import osSEARCH_PARAMS = {'learning_rate': 0.4,'max_depth': 15,'num_leaves': 32,'feature_fraction': 0.8,'subsample': 0.2}FIXED_PARAMS={'objective': 'binary','metric': 'auc','is_unbalance':True,'bagging_freq':5,'boosting':'dart','num_boost_round':300,'early_stopping_rounds':30}def train_evaluate(search_params):# you can download the dataset from this link(https://www.kaggle.com/c/santander-customer-transaction-prediction/data)# import Dataset to play with itdata= pd.read_csv("sample_train.csv")X = data.drop(['ID_code', 'target'], axis=1)y = data['target']X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=1234)train_data = lgb.Dataset(X_train, label=y_train)valid_data = lgb.Dataset(X_valid, label=y_valid, reference=train_data)params = {'metric':FIXED_PARAMS['metric'],'objective':FIXED_PARAMS['objective'],**search_params}model = lgb.train(params, train_data, valid_sets=[valid_data],num_boost_round=FIXED_PARAMS['num_boost_round'],early_stopping_rounds=FIXED_PARAMS['early_stopping_rounds'],valid_names=['valid'])score = model.best_score['valid']['auc']return score使用您選擇的超參數優化庫(例如 scikit-optimize)
neptune.init('mjbahmani/LightGBM-hyperparameters') neptune.create_experiment('lgb-tuning_final', upload_source_files=['*.*'],tags=['lgb-tuning', 'dart'],params=SEARCH_PARAMS)SPACE = [skopt.space.Real(0.01, 0.5, name='learning_rate', prior='log-uniform'),skopt.space.Integer(1, 30, name='max_depth'),skopt.space.Integer(10, 200, name='num_leaves'),skopt.space.Real(0.1, 1.0, name='feature_fraction', prior='uniform'),skopt.space.Real(0.1, 1.0, name='subsample', prior='uniform') ] @skopt.utils.use_named_args(SPACE) def objective(**params):return -1.0 * train_evaluate(params)monitor = sk_utils.NeptuneMonitor() results = skopt.forest_minimize(objective, SPACE, n_calls=100, n_random_starts=10, callback=[monitor]) sk_utils.log_results(results)neptune.stop()嘗試不同類型的配置并在Neptune中跟蹤您的結果
在 Neptune 中運行比較 | 在應用程序中查看
最后,在下表中,您可以看到參數發生了哪些變化。
| learning_rate | 0.4 | 0.094 |
| max_depth | 15 | 10 |
| num_leaves | 32 | 12 |
| feature_fraction | 0.8 | 0.1 |
| subsample | 0.2 | 0.75 |
| boosting | gbdt | dart |
| Score(auc) | 0.8256 | 0.8605 |
最后的想法
長話短說,你學到了:
- lightgbm的主要參數是什么
- 如何使用 feval 函數創建自定義指標
- 主要參數有哪些好的默認值
- 查看如何調整 lightgbm 參數以提高模型性能的示例
和一些其他的東西有關更詳細的信息,請參閱資源。
資源
Decision Tree
總結
以上是生活随笔為你收集整理的LightGBM 参数及调优的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做一个官网企业网站费用大概需要多少钱?
- 下一篇: 20年研发管理经验谈(四)