Ch.4 LightGBM sklearn API实现与进阶

1.LightGBM算法特性

  根据上一小节的LGBM优化策略的总结,我们不难发现LGBM算法具备如下特性:

  • 超高的计算效率

  其实从上面的和GBDT的对比中不难发现,LGBM最大的特点就是计算效率上进行了诸多优化,这方面的优化在具体建模过程中表现为两个方面,其一是建模时间极大缩短,其二则是计算过程的内存占用大幅减少,并且,LGBM同时提供多线程并行计算、分布式计算和GPU加速计算等多种计算实现方式,选择合适的计算方式,可以进一步提升计算效率;

  • 性能强劲

  而同时,算法原理层面的“简化操作”并不会对LGBM的实际预测效率产生太大的影响,尽管从理论上来说,LGBM算法的精确性是介于RF和XGB之间(强于随机森林、弱与XGB),但实际建模过程中,这些微弱的理论层面的性能差异并不会带来太多实际影响,并且复杂度降低有时候反而会使得模型更克服过拟合问题,从而获得一个更好的预测结果。在实际使用过程中,LGBM和XGB性能层面并没有明显差异。

  • 功能完善

  并且作为新兴的(次世代)集成学习算法,LGBM提供了 Python、R 和 C++ 的 API,易于与其他机器学习库集成,且支持Windos、Linux、MacOS等平台部署,实现层面也同时拥有sklearn API和原生API等多种调用方式,功能层面同样支持多种损失函数、能够应对排序任务、时间序列任务等。

  功能完善、性能强劲,同时能保持高效的计算效率,这也是Light一词的精髓所在。

我们将在Ch3.LightGBM原理进阶进行更加完整深入的原理介绍。在不影响LGBM使用的情况下,接下来我们将开始介绍

2.LightGBM算法的快速使用

  在了解了LGBM基本原理和性能特性后,接下来让我们尝试快速使用先尝试着快速使用LGBM算法,并在实际使用过程中,逐步总结LGBM的计算效率和算法性能上的特性。

1.1 LGBM算法库安装

  首次使用LGBM算法前,需要进行LGBM算法库的安装,这里推荐直接使用pip进行安装,在命令行中输入如下命令进行LGBM算法库的安装:

pip install lightgbm -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,即可按照如下方式进行导入:

1
import lightgbm as lgb
1
lgb?
Type:        module
String form: <module 'lightgbm' from 'D:\\anaconda3\\lib\\site-packages\\lightgbm\\__init__.py'>
File:        d:\anaconda3\lib\site-packages\lightgbm\__init__.py
Docstring:  
LightGBM, Light Gradient Boosting Machine.

Contributors: https://github.com/microsoft/LightGBM/graphs/contributors.

目前课程采用的LGBM版本是3.3.5,可以通过如下方式查看LGBM版本号:

1
lgb.__version__
'3.3.5'

如果是低于这个版本的LGBM,则可在命令行中输入以下命令对LGBM进行升级:

pip install --upgrade lightgbm -i https://pypi.tuna.tsinghua.edu.cn/simple --user

1.2 LGBM的sklearn API快速使用

  接下来我们尝试训练LGBM模型。对于LGBM来说,支持多种不同类型的数据输入以及多种不同类型的训练方式,这里我们先从最简单的应用情况开始介绍,即围绕CSV格式数据进行DATaFrame数据格式读取,并采用类sklearn的建模风格进行模型的训练。这里我们先采用鸢尾花数据集进行简单模型测试,数据导入和数据集划分如下:

1
2
3
# 读取数据
iris = load_iris()
data = pd.DataFrame(data= np.c_[iris['data'], iris['target']], columns= iris['feature_names'] + ['target'])
1
data.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) target
0 5.1 3.5 1.4 0.2 0.0
1 4.9 3.0 1.4 0.2 0.0
2 4.7 3.2 1.3 0.2 0.0
3 4.6 3.1 1.5 0.2 0.0
4 5.0 3.6 1.4 0.2 0.0
1
data.shape
(150, 5)
1
2
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(data.drop("target", axis=1), data["target"], test_size=0.2)
1
X_train.head()
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
46 5.1 3.8 1.6 0.2
81 5.5 2.4 3.7 1.0
122 7.7 2.8 6.7 2.0
116 6.5 3.0 5.5 1.8
101 5.8 2.7 5.1 1.9
1
y_train
46     0.0
81     1.0
122    2.0
116    2.0
101    2.0
      ... 
83     1.0
92     1.0
12     0.0
24     0.0
11     0.0
Name: target, Length: 120, dtype: float64

然后是模型实例化过程。和XGB类似,LGBM不同类型的模型(及功能)可以直接通过导入子模块的方式来调用,也可以直接通过父模块.子模块的方式直接进行调用,例如我们可以通过如下方式调用LGBM的分类模型:

1
2
gbm = lgb.LGBMClassifier()
gbm
LGBMClassifier()

也可以先导入,再调用:

1
from lightgbm import LGBMClassifier
1
2
gbm = LGBMClassifier()
gbm
LGBMClassifier()

在sklearn API中,模型已经设置好了默认超参数取值,可以完全不认识任何模型超参数的情况下进行建模。sklearn API中LGBM的超参数情况如下:

1
LGBMClassifier?

具体的模型训练过程和sklearn中其他模型一样,通过fit进行训练,并利用predict进行结果输出:

1
2
# 训练模型
gbm.fit(X_train, y_train)
LGBMClassifier()

然后输出预测结果,同样可以输出类别结果和概率预测结果:

1
gbm.predict(X_test)
array([1., 0., 2., 1., 2., 2., 1., 2., 2., 2., 2., 1., 0., 2., 0., 1., 0.,
       0., 1., 2., 2., 0., 1., 1., 1., 0., 1., 2., 2., 2.])
1
gbm.predict_proba(X_test)

我们可以借助argmax方法将概率预测结果转化为类别预测结果:

1
np.argmax(gbm.predict_proba(X_test), 1)
array([1, 0, 2, 1, 2, 2, 1, 2, 2, 2, 2, 1, 0, 2, 0, 1, 0, 0, 1, 2, 2, 0,
       1, 1, 1, 0, 1, 2, 2, 2], dtype=int64)

同样,我们可以调用accuracy_score函数快速查看最终模型在训练集和测试集上的准确率:

1
accuracy_score(y_train, gbm.predict(X_train)), accuracy_score(y_test, gbm.predict(X_test))
(1.0, 0.9666666666666667)

至此,我们就完成了一次简单的LGBM算法的sklearn API调用。

2.LightGBM sklearn API超参数解释与使用方法

  接下来我们进一步解释LGBM的sklearn API中各评估器中的超参数及使用方法。尽管此时我们并未深入讲解LGBM的算法原理,但LGBM评估器的整体超参数构成和GBDT、XGB类似,我们可以借助此前的知识、并通过类比的方法了解LGBM的每个超参数的具体作用。

  在LGBM的sklearn API中,总共包含四个模型类(也就是四个评估器),分别是lightgbm.LGBMModel、LGBMClassifier 和 LGBMRegressor 以及LGBMRanker:

1679126801449

其中LGBMModel是 LightGBM 的基本模型类,它是一个泛型模型类,可以用于各种类型的问题(如分类、回归等)。通常,我们不直接使用 LGBMModel,而是使用针对特定任务的子类使用不同的类,即分类问题使用 LGBMClassifier 、回归问题使用 LGBMRegressor,而排序问题则使用LGBMRanker。接下来我们重点解释分类和回归模型类的超参数。

2.1 LGBMClassifier超参数概览

  和sklearn中其他评估器命名规则一致,Classifier是用于分类问题的评估器,Regressor是用于回归问题的评估器,两个评估器的超参数构成类似,这里我们先看LGBMClassifier的超参数构成:

1
LGBMClassifier?

总的来看,我们可以将LGBMClassifier的超参数分为决策树剪枝超参数、Boosting过程控制超参数、特征和数据处理类超参数和其他超参数四类。其中决策树剪枝超参数包括max_depth(树的最大深度)、num_leaves(叶子节点数)、min_child_samples(子节点的最小样本数)等,都是大家非常熟悉的超参数;Boosting过程控制超参数和XGB的booster类似,包含boosting_type(提升类型,如gbdt、dart、goss、rf),n_estimators(迭代次数)、learning_rate(学习率)和reg_alpha(L1正则化系数)、reg_lambda(L2正则化系数);而特征和数据处理类超参数则主要与数据采样有关,包含subsample(样本子集的比例)、subsample_freq(进行子采样的频率)、colsample_bytree(列采样的比例)等;而其他参数则是一些对建模效果并没有重要影响的超参数,例如random_state(随机数种子)、n_jobs(并行计算所用的CPU核心数)、class_weight(类别权重)等。

  从整体来看,LGBMClassifier的超参数构成并不复杂,对比XGB,甚至可以说是更加简洁清晰。

  接下来我们就逐个解释这些超参数,并对其使用方法进行说明。

2.2 LGBMClassifier的决策树剪枝超参数

  LGBM中决策树剪枝超参数和其他评估器中剪枝超参数并无区别,这类超参数已经在课程中反复解释过多次,超参数及解释如下:

Name Description
num_leaves 一棵树上的叶子节点数,默认为 31
max_depth 树的最大深度,默认值为 -1,表示无限制
min_split_gain 相当于min_impurity_decrease,再分裂所需最小增益。默认值为 0,表示无限制
min_child_weight 子节点的最小权重和。默认值为 1e-3。较大的 min_child_weight 可以防止过拟合
min_child_samples 相当于min_samples_leaf,单个叶子节点上的最小样本数量。默认值为 20。较大的 min_child_samples 可以防止过拟合

能够发现,相比决策树或者随机森林,LGBM的决策树剪枝类参数有所精简,保留了最能影响决策树生长的核心超参数。

2.3 LGBMClassifier的Boosting过程控制超参数解释

  然后来看更加重要的Boosting过程控制超参数,首先较为复杂的是boosting_type,该超参数解释如下:

  • boosting_type: 使用的梯度提升算法类型,默认为 ‘gbdt’,可选项包括 ‘gbdt’(梯度提升决策树)、‘dart’(Dropouts meet Multiple Additive Regression Trees)、‘goss’(Gradient-based One-Side Sampling)和 ‘rf’(Random Forest,随机森林)。其中GBDT是最常用、且性能最稳定的 boosting 类型,也是boosting_type默认取值;而dart (Dropouts meet Multiple Additive Regression Trees)则是一种结合了 Dropout 和多重加性回归树的方法。它在每次迭代过程中随机选择一部分树进行更新,会较大程度增加模型随机性,可以用于存在较多噪声的数据集或者数据集相对简单(需要减少过拟合风险)的场景中;GOSS 是一种基于梯度的单侧采样方法。它在每次迭代中只使用具有较大梯度的样本进行训练,从而降低计算复杂度。goss(Gradient-based One-Side Sampling)则是前文介绍的梯度的单边采样算法,可以在保持较高精度的同时加速训练过程,适用于大规模数据集,可以在保持较高精度的同时加速训练过程,有些时候精度不如GBDT;而rf则是采用随机森林来进行“Boosting过程”,或者说此时就不再是Boosting,而是替换成了Bagging过程,类似于XGBRFClassifier的计算过程,此时LGBM本质上将按照RF的计算范式进行计算。

然后与之相关的是subsample_for_bin参数:

  • subsample_for_bin:该参数表示对连续变量进行分箱时(直方图优化过程)抽取样本的个数,默认取值为200000,当实际抽样个数大于输入训练数据样本数量时,会带入全部样本进行计算。而如果boosting_type选择的是goss,则在直方图优化时会自动完成抽样,具体抽样策略是:会保留所有较小梯度的样本(即那些已经被模型很好拟合的样本),并对较大梯度的样本进行采样。这种策略能够在加速训练(大梯度样本的贡献)的同时有效防止过拟合(小梯度样本的贡献)。因此,如果boosting_type选择的是 “goss”,。则subsample_for_bin参数会失去作用,此时无论subsample_for_bin取值多少都不影响最终结果。

如果需要控制goss过程,则需要借助top_rate 和 other_rate 这两个参数,但是这两个参数只存在于LGBM原生API中,在sklearn中并没有,因此在使用 LightGBM 的 sklearn API 时,GOSS 采样方法会自动进行调整。关于这两个参数的解释和使用,我们将在介绍LGBM原生API时讲解。

其他参数和GBDT及XGB类似,具体解释如下:

Name Description
learning_rate 学习率,即每次迭代中梯度提升的步长,默认值为 0.1
n_estimators 迭代次数,即生成的基学习器的数量,默认值为 100
reg_alpha L1 正则化系数,默认值为 0
reg_lambda L2 正则化系数。默认值为 0

2.4 LGBMClassifier的特征和数据处理类超参数

  LGBM中特征和数据处理类超参数主要用于每次迭代时数据和特征的分配,核心作用是控制模型训练过程的随机性,其超参数的基本解释如下:

Name Description
subsample 模型训练时抽取的样本数量,取值范围为 (0, 1],表示抽样比例,默认为1.0
subsample_freq 抽样频率,表示每隔几轮进行一次抽样,默认取值为0,表示不进行随机抽样
colsample_bytree 在每次迭代(树的构建)时,随机选择特征的比例,取值范围为 (0, 1],默认为1.0

这里有以下几点需要注意:

  • subsample和subsample_for_bin之间的关系
      这两个参数尽管从字面理解都是抽样比例,但实际上这两个参数是完全独立的,彼此之间并不影响。其中subsample_for_bin抽样结果用于直方图构建,而subsample抽样结果则是用于模型训练,这两个抽样过程彼此独立,互不影响;

  • subsample和subsample_freq之间的关系
      更加关键的是subsample_freq参数,如果subsample_freq=0,则无论subsample取值为多少,模型训练时都不会进行随机抽样;换而言之,只有subsample_freq不等于0,且subsample不等于1.0时,才会进行抽样;

  • subsample_freq和colsample_bytree之间的关系
      不同于subsample是样本抽样,colsample_bytree是每次迭代(每次构建一颗树时)进行的特征抽样,并且colsample_bytree不受subsample_freq影响,即无论subsample_freq取值为多少,每次建树时都会根据colsample_bytree设置的比例进行列抽样。

  • LGBM和RF的不同特征抽样(分配)规则

  同时需要注意的是,LGBM和随机森林不同,随机森林是每棵树的每次分裂时都随机分配特征,而LGBM是每次构建一颗树时随机分配一个特征子集,这颗树在成长过程中每次分裂都是依据这个特征子集进行生长。

2.5 LGBMClassifier的其他超参数

  最后则是一些用于辅助建模的超参数,具体超参数及解释如下:

Name Description
objective 指定目标函数,默认为None,会自动判断是二分类还是多分类问题,这里我们也可以手动设置 ‘binary’(用于二分类问题)或’multiclass’(用于多分类问题)
class_weight 样本权重设置参数
importance_type 特征重要性计算方式,默认为 ‘split’,表示使用特征在模型中被选中作为分裂特征的次数,可选 ‘gain’,表示使用特征在模型中的分裂收益之和作为重要性评估指标
random_state 随机数种子
n_jobs 并行的线程数,默认为-1,调用全部可用线程
silent 是否沉默(不输出日志),默认为’warn’,仅显示警告和报错,可选’info’,用于打印全部信息

  首先是关于目标函数的设置,对于LGBM的sklearn API来说,objective参数较为简单,只有’binary’和’multiclass’两种,其中’binary’表示逻辑回归损失,也就是二分类交叉熵损失,而’multiclass’则代表多分类交叉熵损失。

当然,LGBM也支持softmax损失,只不过无法通过objective传入。softmax损失默认是只有原生API才支持调用,若需要传入sklearn API中,则需要借助**kwargs参数,以原生API形式进行传输。原生API的调用方法我们将在下一小节进行探讨。

  此外,需要注意用于计算特征重要性的importance_type参数,这也是LGBM算法的特点之一——即提供了两种不同的用于评估特征重要性的方法。很多时候我们在特征工程阶段都会创建海量特征来增强数据表现,并且通过一些数值指标或者模型来进行特征筛选,进而兼顾建模效率和预测效果。考虑到LGBM同时拥有非常高的计算效率,这使得LGBM算法成为筛选特征的最常用的模型之一。

具体特征衍生方法,我们将在特征工程阶段进行详细介绍。

  至此,我们就完整解释了LGBMClassifier的全部超参数。接下来我们继续讨论LGBMRegressor超参数。LGBMRegressor和LGBMClassifier只有两点不同,其一是LGBMRegressor没有class_weight参数,其二则是LGBMRegressor的损失函数和LGBMClassifier完全不同。接下来我们就LGBMRegressor的损失函数选取进行解释。

2.6 LGBMRegressor损失函数

1
from lightgbm import LGBMRegressor
1
LGBMRegressor?

LGBMRegressor的损失函数包含了GBDT和XGB的各类回归类损失函数,相关计算过程及应用场景此前都有详细讨论,这里只进行简要说明:

  • 均方误差(MSE, Mean Squared Error):最常用的的损失函数,此时objective=‘regression’ 或 objective=‘regression_l2’;
  • 平均绝对误差(MAE, Mean Absolute Error):通常用于标签存在异常值情况,此时objective=‘regression_l1’;
  • Huber损失(Huber Loss):适用于目标值存在大量异常值或者噪声时。Huber损失在预测误差较小时表现为均方误差,在预测误差较大时表现为平均绝对误差,这使得它对异常值具有更好的鲁棒性。此时objective=‘quantile’;
  • Quantile损失(Quantile Loss):用于分位数回归,最小化预测值与真实值之间的分位数损失,适用于需要对预测分布进行精细控制的场景(例如围绕某种分布进行预测)。此时objective=‘quantile’;
  • Poisson损失(Poisson Loss): 适用于计数问题,即目标值是非负整数且服从泊松分布。此时objective=‘poisson’
  • Gamma损失(Gamma Loss):适用于预测非负实数且服从伽马分布的目标值。此时objective=‘gamma’
  • Tweedie损失(Tweedie Loss):适用于广义线性模型(Generalized Linear Models, GLMs)中的 Tweedie 分布(非对称分布)的数据集。此时objective=‘tweedie’。

  能够发现,大多数损失函数都是针对标签存在某个具体分布时进行的预测,一般情况下我们都是在均方误差(MSE)、平均绝对误差(MAE)和Huber损失中进行选择,通常情况首选MSE,而当标签存在噪声或者异常点时,MAE会表现出更好的泛化能力。并且由于MAE较为特殊的计算过程,导致其在正常情况数据集下精度不如MSE。而Huber则是二者的综合,适用于标签存在少量异常值的数据集,Huber对异常值较为鲁棒,同时又可以保留较好的精度。

2.LightGBM sklearn API进阶使用方法

  接下来,我们进一步尝试进行更复杂的LGBM API的调用,以满足更加复杂的建模过程。这里我们仍然是分sklearn API和原生API两种调用方式进行尝试。

  首先是sklearn API进阶调用方法。在实际建模过程中,如果需要进一步借助sklearn功能模块进行建模,例如需要进行网格搜索超参数优化、构建机器学习流或者和sklearn其他评估器进行模型融合等,则可以调用LGBM的sklearn API进行快速执行。当然,大多数sklearn的相关功能都在此前的课程中有所尝试,这里我们对此快速进行回顾。

2.1 交叉验证

  首先是交叉验证,通过调用LGBM的sklearn API,我们可以将LGBM作为评估器输入到交叉验证相关函数中进行快速计算:

1
from sklearn.model_selection import cross_val_score
1
cross_val_score?
1
cross_val_score(LGBMClassifier(), X_train, y_train)
array([0.83333333, 1.        , 1.        , 1.        , 0.875     ])

2.2 网格搜索

  接着,我们进一步尝试借助网格搜索对LGBM评估器进行超参数优化。当然此时我们还未详细解读LGBM的超参数含义,这里我们先以测试相关功能为主,关于LGBM超参数的解读会在介绍完算法原理后进行探讨。这里我们简单设置超参数搜索空间为’n_estimators’: [90, 100, 110]进行网格搜索测试,LGBM和其他GBDT框架下的集成学习一样,n_estimators都代表迭代次数或者基础学习器个数。

1
gbm = LGBMClassifier()
1
LGBMClassifier?
1
param_grid = {'n_estimators': [90, 100, 110]}
1
LGBM_search = GridSearchCV(estimator=gbm, param_grid=param_grid, n_jobs=10)
1
LGBM_search.fit(X_train, y_train)
GridSearchCV(estimator=LGBMClassifier(), n_jobs=10,
             param_grid={'n_estimators': [90, 100, 110]})
1
LGBM_search.best_score_
0.9416666666666668
1
LGBM_search.best_params_
{'n_estimators': 90}

2.3 构建Pipeline

  然后我们尝试借助LGBM的sklearn API构建Pipeline。Pipeline也是sklearn特有的一项功能,通过Pipeline的构建,我们可以将数据清洗、特征衍生和机器学习封装成一个评估器,进而进行快速的数据处理和预测,甚至我们还可以围绕这个pipeline进行超参数优化,进而达到自动数据清洗、自动机器学习算法超参数优化等目的。

  这里我们尝试将多项式特征衍生、标准化和LGBM三个评估器封装为一个pipeline,在后续预测时,数据会自动按照二阶多项式衍生和Z-Score标准化的流程先进行处理,然后再带入LGBM模型中进行预测:

1
2
3
LGBM_pipe = make_pipeline(PolynomialFeatures(), 
StandardScaler(),
LGBMClassifier())
1
PolynomialFeatures?

然后以Pipeline作为完整的评估器,围绕数据集进行数据处理和模型训练:

1
LGBM_pipe.fit(X_train, y_train)
Pipeline(steps=[('polynomialfeatures', PolynomialFeatures()),
                ('standardscaler', StandardScaler()),
                ('lgbmclassifier', LGBMClassifier())])

最终输出预测结果:

1
LGBM_pipe.score(X_test, y_test)
0.9666666666666667

2.4 Pipeline超参数优化与自动机器学习

  最后,我们尝试围绕上述Pipeline进行超参数优化。在Lesson 6中我们曾介绍,对于一个Pipeline来说,其中的每个评估器中的每个超参数,都可以放在一个超参数空间中进行搜索和优化,因此这里我们可以通过网格搜索的方法,自动搜索出(某个小范围内)最佳多项式特征衍生的阶数和LGBM的最佳基础分类器个数:

1
LGBM_pipe.get_params()
1
2
param_pipe = {'polynomialfeatures__degree': [2, 3, 4], 
'lgbmclassifier__n_estimators': [90, 100, 110]}
1
LGBM_pipe_search = GridSearchCV(estimator=LGBM_pipe, param_grid=param_pipe, n_jobs=10)
1
LGBM_pipe_search.fit(X_train, y_train)
GridSearchCV(estimator=Pipeline(steps=[('polynomialfeatures',
                                        PolynomialFeatures()),
                                       ('standardscaler', StandardScaler()),
                                       ('lgbmclassifier', LGBMClassifier())]),
             n_jobs=10,
             param_grid={'lgbmclassifier__n_estimators': [90, 100, 110],
                         'polynomialfeatures__degree': [2, 3, 4]})
1
LGBM_pipe_search.best_params_
{'lgbmclassifier__n_estimators': 90, 'polynomialfeatures__degree': 3}
1
LGBM_pipe_search.score(X_test, y_test)
0.9666666666666667

不难看出,此过程能够一步到位实现自动数据清洗和自动模型超参数优化。不过上述流程目前为止还是过于简单,在本课程最后的案例阶段,我们会有对此更进一步探讨。

  此外,通过调用sklearn API,我们还能够对LGBM评估器进行诸如模型本地保存、构造模型融合评估器等,这些内容我们也将在后续部分内容中逐步进行介绍。

  • LGBM各模型的sklearn API简介

  当然,除了分类模型外,LGBM的sklearn API还包括回归模型和排序模型两种,分别可以通过如下方式进行调用:

1
2
# LGBM回归模型的sklearn API
from lightgbm import LGBMRegressor
1
LGBMRegressor?
1
2
# LGBM排序模型的sklearn API
from lightgbm import LGBMRanker
1
LGBMRanker?

其中排序模型更多的应用于时间序列和推荐系统等领域。相关API说明可以参考LGBM官方API说明文档,各API的具体调用方法和超参数说明我们会在实践环节具体讨论。