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_leavesmax_depth一起调优。

LightGBM 参数及调优_第1张图片
LightGBM 参数及调优_第2张图片

lightgbm 文档上的照片

subsample

使用subsample(或 bagging_fraction),您可以指定每次树构建迭代使用的行的百分比。这意味着将随机选择一些行来拟合每个学习器(树)。这提高了泛化能力,并且也提高了训练速度。
LightGBM 参数及调优_第3张图片

我建议对基线模型使用较小的子样本值,然后在完成其他实验(不同的特征选择,不同的树结构)后增加这个值。

feature_fraction

feature_fraction或 sub_feature 处理列采样,LightGBM 将在每次迭代(树)上随机选择特征子集。例如,如果将其设置为 0.6,LightGBM 将在训练每棵树之前选择 60% 的特征。

此功能有两种用法:

  • 可用于加速训练
  • 可用于处理过拟合
    LightGBM 参数及调优_第4张图片

max_depth

此参数控制每个训练树的最大深度,并将影响:

  • num_leaves 参数的最佳值
  • 模型表现
  • 训练时间

注意如果您使用较大的max_depth值,您的模型可能会过拟合

max_bin

分箱是一种在离散视图(直方图)中表示数据的技术。Lightgbm 使用基于直方图的算法在创建弱学习器的同时找到最佳分割点。因此,每个连续的数字特征(例如视频的观看次数)都应该被分成离散的 bin。

LightGBM 参数及调优_第5张图片
LightGBM 和 XGBoost上的照片解释

此外,在这个GitHub r epo 中,您可以找到一些全面的实验,这些实验充分解释了更改 max_bin 对 CPU 和 GPU 的影响。

LightGBM 参数及调优_第6张图片

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 文档,其中所有内容都是最新的!


现在,这就是我们要做的。

  1. 第一步,我们在Notebook中生成代码。它是公开的,您可以下载它。

  2. 第二步,我们在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 os

SEARCH_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 it
   data= 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中跟踪您的结果

LightGBM 参数及调优_第7张图片
在 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 参数以提高模型性能的示例

和一些其他的东西有关更详细的信息,请参阅资源。

资源

  1. Laurae extensive guide with good defaults etc
  2. LightGBM Python-package
  3. LightGBM’s documentation
  4. LightGBM: A Highly Efficient Gradient Boosting
    Decision Tree
  5. Greedy Function Approximation A Gradient Boosting Machine

你可能感兴趣的:(机器学习,决策树,人工智能,机器学习)