(十)LightGBM
本系列重点在浅显易懂,快速上手。不进行过多的理论讲解:也就是不去深究what,而是关注how。全文围绕以下三个问题展开:
1)长什么样?
2)解决什么问题?
3)怎么实现?
3.1)从数学讲,原理
3.2)具体实例
3.3)从代码上讲,如何掉包实现
定义
可以用如下一个简单公式来说明LightGBM和XGBoost的关系:
LightGBM = XGBoost + Histogram + GOSS + EFB。
关于XGBoost的详细论述请看本系列的第九篇文章——(九)XGBoost的原理、具体实例、代码实现
XGBoost与GBDT比较大的不同就是目标函数的定义,基本思想是一致的,同样是利用加法模型与前向分步算法实现学习的优化过程。预测过程如下:
y ^ i = ∑ k = 1 K f k ( x i ) \hat{y}_{i}=\sum_{k=1}^{K}f_{k}({x}_{i}) y^i=k=1∑Kfk(xi)
其中, f k f_k fk表示回归X树,K为回归树的数量。
XGBoost是由GBDT发展而来,同样是利用加法模型与前向分步算法实现学习的优化过程。
回归
二分类
LightGBM在XGBoost上主要有3方面的优化。
1,Histogram算法:直方图算法。
2,GOSS算法:基于梯度的单边采样算法。
3,EFB算法:互斥特征捆绑算法。
XGBoost生成一片叶子的复杂度可以粗略估计为 = 特征数量*候选分裂点数量*样本的数量。
Histogram算法的主要作用是减少候选分裂点数量,
GOSS算法的作用是减少样本的数量,
EFB算法的作用是减少特征的数量。
通过这3个算法的引入,LightGBM生成一片叶子需要的复杂度大大降低了,从而极大节约了计算时间。
同时Histogram算法还将特征由浮点数转换成0~255位的整数进行存储,从而极大节约了内存存储。
Histogram直方图算法
直方图算法是替代XGBoost的预排序(pre-sorted)算法的。预排序算法首先将样本按照特征取值排序,然后从全部特征取值中找到最优的分裂点位,该算法的候选分裂点数量与样本数量成正比。而直方图算法通过将连续特征值离散化到固定数量(如255个)的bins上,使得候选分为点位为常数个(num_bins -1)。
此外,直方图算法还能够作直方图差加速。当节点分裂成两个时,右边叶子节点的直方图等于其父节点的直方图减去左边叶子节点的直方图。从而大大减少构建直方图的计算量。
利用直方图对于每个特征的所有候选分割点按照其范围分成N个箱子,累加箱子内的梯度提升值,对于箱子里的每个候选分割点都计算 带来的梯度增益,对于每个箱子分别保存其累计梯度、箱子内的样本数量。之后再分裂节点时直接对直方图遍历进行分割点的候选即可,通过直方图的方式,虽然分割的精度变差了,但是对最后的结果影响不大,一方面能够提升计算效率,另一方面这种较粗的分割点可以起到一种正则化的效果。之后进行结点分裂时候,只需要根据梯度之和计算loss即可。对Gain增益的计算在梯度计算中。
增益计算公式:
Δ l o s s = s L 2 n L + s R 2 n R − s P 2 n P \Delta loss=\frac{s_L^2}{n_L}+\frac{s_R^2}{n_R}-\frac{s_P^2}{n_P} Δloss=nLsL2+nRsR2−nPsP2
s L s_L sL左边bin的梯度之和, n L n_L nL左边的样本数量, s R s_R sR右边bin的梯度之和, n R n_R nR右边的样本数量, s P s_P sP父节点的梯度之和, n P n_P nP父节点的样本数量。
SL指的是左边的bin至当前bin的梯度之和,nL指样本数量,以6.5为分裂点。
Δ l o s s = ( − 0.43 ) 2 4 + ( 0.18 + 0.12 − 0.33 ) 2 16 − ( − 0.43 + 0.18 + 0.12 − 0.33 ) 2 20 = 0.04127 \begin{aligned}&\Delta\mathrm{loss}=\frac{(-0.43)^{2}}{4}+\frac{(0.18+0.12-0.33)^{2}}{16}\\&-\frac{(-0.43+0.18+0.12-0.33)^{2}}{20}=0.04127\end{aligned} Δloss=4(−0.43)2+16(0.18+0.12−0.33)2−20(−0.43+0.18+0.12−0.33)2=0.04127
直方图做差, 一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图, 需要遍历该叶子上的所有数据, 但直方图做差仅需遍历直方图的k个桶。利用这个方法, LightGBM可以在构造一个叶子的直方图后(父节点在上一轮就已经计算出来了), 可以用非常微小的代价得到它兄弟叶子的直方图, 在速度上可以提升一倍。直方图差加速:
GOSS基于梯度的单边采样算法
GOSS算法全称为Gradient-based One-Side Sampling,即基于梯度的单边采样算法。
样本的梯度越小,则样本的训练误差越小,表示样本已经训练的很好了。最直接的做法就是丢掉这部分样本,然而直接扔掉会影响数据的分布,因此lightGBM采用了one-side 采样的方式来适配:GOSS。
GOSS首先根据数据的梯度绝对值排序,选取top a个实例。然后在剩余的数据中随机采样b个实例。这样算法就会更关注训练不足的实例,而不会过多改变原数据集的分布。GOSS保留了所有的大梯度样本,对小梯度样本进行随机采样,同时为了保证分布的一致性,在计算信息增益的时候,将采样的小梯度样本乘以一个常量:(1−a)/b,a表示Top a×100%的大梯度样本比例值,b表示小梯度样本的采样比值,乘以1−a是因为大梯度样本采样的整体是整个样本集N,小梯度样本采样的候选样本集为(1−a)*N,除以b是因为采样导致小梯度样本的整体分布减少,为此需要将权重放大1/b倍)。
- 取样步骤:
输入:训练数据,迭代步数d,大梯度数据的采样率a,小梯度数据的采样率b,损失函数和若学习器的类型(一般为决策树);
输出:训练好的强学习器;
(1)根据样本点的梯度的绝对值对它们进行降序排序;
(2)对排序后的结果选取前a*100%的样本生成一个大梯度样本点的子集;
(3)对剩下的样本集合(1-a)*100%的样本,随机的选取b/(1-a)*100%个样本点,生成一个小梯度样本点的集合;
(4)将大梯度样本和采样的小梯度样本合并;
(5)将小梯度样本乘上一个权重系数;
(6)使用上述的采样的样本,学习一个新的弱学习器;
(7)不断地重复(1)~(6)步骤直到达到规定的迭代次数或者收敛为止。
通过上面的算法可以在不改变数据分布的前提下不损失学习器精度的同时大大的减少模型学习的速率。
从上面的描述可知,当a=0时,GOSS算法退化为随机采样算法;当a=1时,GOSS算法变为采取整个样本的算法。在许多情况下,GOSS算法训练出的模型精确度要高于随机采样算法。另一方面,采样也将会增加若学习器的多样性,从而潜在的提升了训练出的模型泛化能力。
- 实例:
这是不采用GOSS采样的情况:
这是采用GOSS采样的情况:
梯度绝对值的阈值是0.1,大于0.1的数据有2,小于0.1的有6个。大梯度的数据全部保留,大梯度的采样率a=2/8。小梯度的采样个数为2个,采样率为b=2/8。则小梯度的数据都乘 (1-a)/b=(1-2/8)/(2/8)=3。
EFB互斥特征捆绑算法
EFB算法全称是Exclusive Feature Bundling,即互斥特征绑定算法。(将多个特征捆绑到一起)
EFB是通过特征捆绑的方式减少特征维度(其实是降维技术)的方式,来提升计算效率。通常被捆绑的特征都是互斥的(一个特征值为零,一个特征值不为零),这样两个特征捆绑起来才不会丢失信息。如果两个特征并不是完全互斥(部分情况下两个特征都是非零值),可以用一个指标对特征不互斥程度进行衡量,称之为冲突比率,当这个值较小时,我们可以选择把不完全互斥的两个特征捆绑,而不影响最后的精度。
- EFB的算法步骤如下:
- 将特征按照非零值的个数进行排序
- 计算不同特征之间的冲突比率
- 遍历每个特征并尝试合并特征,使冲突比率最小化
将特征1和特征2进行捆绑,形成新的特征。只把值都为0的特征合并。
实例
请看本系列的第九篇文章——
(九)XGBoost的原理、具体实例、代码实现
参考xgboost原理分析以及实践_SCUT_Sam-CSDN博客_xgboost流程图
以二分类情况为例。15条数据,两个特征。这里为了简单起见,树的深度设置为3(max_depth=3)
,树的颗数设置为(num_boost_round=2)
,学习率为0.1(eta=0.1)
。另外再设置两个正则的参数, λ = 1 , γ = 0 λ=1,γ=0 λ=1,γ=0。
损失函数选择logloss
。
ID | x1 | x2 | y |
---|---|---|---|
1 | 1 | -5 | 0 |
2 | 2 | 5 | 0 |
3 | 3 | -2 | 1 |
4 | 1 | 2 | 1 |
5 | 2 | 0 | 1 |
6 | 6 | -5 | 1 |
7 | 7 | 5 | 1 |
8 | 6 | -2 | 0 |
9 | 7 | 2 | 0 |
10 | 6 | 0 | 1 |
11 | 8 | -5 | 1 |
12 | 9 | 5 | 1 |
13 | 10 | -2 | 0 |
14 | 8 | 2 | 0 |
15 | 9 | 0 | 1 |
logloss
交叉熵损失函数的一阶导数以及二阶导数:
g i = y ^ i − y i h i =