LightGBM学习分享

LightGBM学习分享

1基本概念

1.1背景:

       GBDT一经提出,就得到了广泛的关注,其演变的算法很多,尤其是Xgboost更是被认为泛化能力最好的算法,其在各种问题中都表现出了优异的性能,但是在使用的过程中可以感受得出来Xgboost模型训练需要较长的时间,也就是说效率不是很高,能不能在Xgboost的基础上进一步改进,使得其训练速度提高呢?有需求就有了努力的方向,正是在这一背景下LightGBM出现了。

1.2简述

从名字可以看出LightGBM就是Light+GBM,对于GBM很熟悉了其全称应该是gradient boosting machine即梯度提升树算法,其在该方面继续延续了Xgboost那一套集成学习方法,不再重述,Light是轻量级的意思,其关注的是模型训练速度,其也是LightGBM提出的初衷。所以我们在看其算法的时候不论其提出采用了什么新的技术记住其就是一个出发点快!

LightGBM在实际代码实现的时候主要提出了两大技术: 

(1)GOSS(Gradient-based One-Side Sampling):减少样本数 

(2)EFB (Exclusive Feature Bundling ):减少特征数

除此之外其在真真实现的时候还采用了直方图,支持分布式等等

直方图差加速:自动处理缺省值,包括是否将0视为缺省值。处理类别特征EFB、GOSS、Leaf-wise等细节LightGBM正是由于本着快的思想,最终导致的一个优点就是能够处理大数据!!!

2图文结合优化对比

LightGBM在哪些地方进行了优化?

概括来说,lightGBM主要有以下特点:
基于Histogram的决策树算法
带深度限制的Leaf-wise的叶子生长策略
直方图做差加速
直接支持类别特征(Categorical Feature)
Cache命中率优化
基于直方图的稀疏特征优化
多线程优化


XGBoost使用的是pre-sorted算法,能够更精确的找到数据分隔点

 

首先,对所有特征按数值进行预排序
其次,在每次的样本分割时,用O(# data)的代价找到每个特征的最优分割点
最后,找到最后的特征以及分割点,将数据分裂成左右两个子节点。
这种pre-sorting算法能够准确找到分裂点,但是在空间和时间上有很大的开销

由于需要对特征进行预排序并且需要保存排序后的索引值(为了后续快速的计算分裂点),因此内存需要训练数据的两倍
在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。
LightGBM使用的是histogram算法,占用的内存更低,数据分隔的复杂度更低。其思想是将连续的浮点特征离散成k个离散值,并构造宽度为kHistogram。然后遍历训练数据,统计每个离散值在直方图中的累计统计量。在进行特征选择时,只需要根据直方图的离散值,遍历寻找最优的分割点。


使用直方图算法有很多优点。首先最明显就是内存消耗的降低,直方图算法不仅不需要额外存储预排序的结果,而且可以只保存特征离散化后的值,而这个值一般用8位整型存储就足够了,内存消耗可以降低为原来的1/8

 

 

Histogram algorithm

Histogram algorithm应该翻译为直方图算法,直方图算法的思想也很简单,首先将连续的浮点数据转换为bin数据,具体过程是首先确定对于每一个特征需要多少的桶bin,然后均分,将属于该桶的样本数据更新为bin的值,最后用直方图表示。(看起来很高大上,其实就是直方图统计,最后我们将大规模的数据放在了直方图中

x = randn(10000,1);
h = histogram(x)

如上图所示,x取值在[0.2,0.4]范围的值有824个。

 

生成 1,000 随机数并创建直方图。将 bin 边界指定为向量,使宽 bin 在直方图的两边,以捕获不满足 的离群值。第一个向量元素是第一个 bin 左边界,而最后一个向量元素是最后一个 bin 右边界

直方图算法有几个需要注意的地方:

使用bin替代原始数据相当于增加了正则化
使用bin意味着很多数据的细节特征被放弃了,相似的数据可能被划分到相同的桶中,这样的数据之间的差异就消失了;
bin数量选择决定了正则化的程度,bin越少惩罚越严重,欠拟合风险越高。
直方图算法需要注意的地方:

构建直方图时不需要对数据进行排序(比XGBoost快),因为预先设定了bin的范围;
直方图除了保存划分阈值和当前bin内样本数以外还保存了当前bin内所有样本的一阶梯度和(一阶梯度和的平方的均值等价于均方损失);
阈值的选取是按照直方图从小到大遍历,使用了上面的一阶梯度和,目的是得到划分之后loss最大的特征及阈值。

Histogram 算法的优缺点:

Histogram算法并不是完美的。由于特征被离散化后,找到的并不是很精确的分割点,所以会对结果产生影响。但在实际的数据集上表明,离散化的分裂点对最终的精度影响并不大,甚至会好一些。原因在于decision tree本身就是一个弱学习器,采用Histogram算法会起到正则化的效果,有效地防止模型的过拟合。
时间上的开销由原来的O(#data * #features)降到O(k * #features)。由于离散化,#bin远小于#data,因此时间上有很大的提升。
Histogram算法还可以进一步加速。一个叶子节点的Histogram可以直接由父节点的Histogram和兄弟节点的Histogram做差得到。一般情况下,构造Histogram需要遍历该叶子上的所有数据,通过该方法,只需要遍历Histogramk个捅。速度提升了一倍。

决策树生长策略

Histogram算法之上,LightGBM进行进一步的优化。首先它抛弃了大多数GBDT工具使用的按层生长 (level-wise)的决策树生长策略,而使用了带有深度限制的按叶子生长 (leaf-wise)算法。

XGBoost采用的是按层生长leveldepth-wise生长策略,能够同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合;但不加区分的对待同一层的叶子,带来了很多没必要的开销。因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。

 

LightGBM采用leaf-wise生长策略,每次从当前所有叶子中找到分裂增益最大(一般也是数据量最大)的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合因此LightGBMLeaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。

直方图差加速

LightGBM另一个优化是Histogram(直方图)做差加速。一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的k个桶。利用这个方法,LightGBM可以在构造一个叶子的直方图后,可以用非常微小的代价得到它兄弟叶子的直方图,在速度上可以提升一倍

 

直接支持类别特征

实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化one-hotting特征,降低了空间和时间的效率。而类别特征的使用是在实践中很常用的。基于这个考虑,LightGBM优化了对类别特征的支持,可以直接输入类别特征,不需要额外的0/1展开。并在决策树算法上增加了类别特征的决策规则。

one-hot编码是处理类别特征的一个通用方法,然而在树模型中,这可能并不一定是一个好的方法,尤其当类别特征中类别个数很多的情况下。主要的问题是:

可能无法在这个类别特征上进行切分(即浪费了这个特征)。使用one-hot编码的话,意味着在每一个决策节点上只能使用one vs rest(例如是不是狗,是不是猫等)的切分方式。当类别值很多时,每个类别上的数据可能会比较少,这时候切分会产生不平衡,这意味着切分增益也会很小(比较直观的理解是,不平衡的切分和不切分没有区别)。
会影响决策树的学习。因为就算可以在这个类别特征进行切分,也会把数据切分到很多零碎的小空间上,如图1左边所示。而决策树学习时利用的是统计信息,在这些数据量小的空间上,统计信息不准确,学习会变差。但如果使用下图右边的分裂方式,数据会被切分到两个比较大的空间,进一步的学习也会更好。
下图右边叶子节点的含义是X=A或者X=C放到左孩子,其余放到右孩子。

 

LightGBM处理分类特征大致流程

为了解决one-hot编码处理类别特征的不足。LightGBM采用了Many vs many的切分方式,实现了类别特征的最优切分。用LightGBM可以直接输入类别特征,并产生上图右边的效果。在1k维的类别特征中寻找最优切分,朴素的枚举算法的复杂度是O(2k),而LightGBM采用了如On Grouping For Maximum Homogeneity的方法实现了O(klogk)的算法

算法流程下图所示:在枚举分割点之前,先把直方图按每个类别的均值进行排序;然后按照均值的结果依次枚举最优分割点。从下图可以看到,Sum(y)/Count(y)为类别的均值。当然,这个方法很容易过拟合,所以在LGBM加入了很多对这个方法的约束和正则化
下图是一个简单的对比实验,可以看到该最优方法在AUC上提升了1.5个点,并且时间只多了20%
下面具体来讲下在代码中如何求解类别特征的最优切分的流程

离散特征建立直方图的过程统计该特征下每一种离散值出现的次数,并从高到低排序,并过滤掉出现次数较少的特征值, 然后为每一个特征值,建立一个bin容器, 对于在bin容器内出现次数较少的特征值直接过滤掉,不建立bin容器。
计算分裂阈值的过程:
先看该特征下划分出的bin容器的个数,如果bin容器的数量小于4,直接使用one vs other方式, 逐个扫描每一个bin容器,找出最佳分裂点;
对于bin容器较多的情况, 先进行过滤,只让子集合较大的bin容器参加划分阈值计算, 对每一个符合条件的bin容器进行公式计算:

公式如下: bin容器下所有样本的一阶梯度之和/bin容器下所有样本的二阶梯度之和 + 正则项(参数cat_smooth)

这里为什么不是label的均值呢?其实上例中只是为了便于理解,只针对了学习一棵树且是回归问题的情况, 这时候一阶导数是Y, 二阶导数是1),得到一个值,根据该值对bin容器从小到大进行排序,然后分从左到右、从右到左进行搜索,得到最优分裂阈值。但是有一点,没有搜索所有的bin容器,而是设定了一个搜索bin容器数量的上限值,程序中设定是32,即参数max_num_catLightGBM中对离散特征实行的是many vs many 策略,这32bin中最优划分的阈值的左边或者右边所有的bin容器就是一个many集合,而其他的bin容器就是另一个many集合。
对于连续特征,划分阈值只有一个,对于离散值可能会有多个划分阈值,每一个划分阈值对应着一个bin容器编号,当使用离散特征进行分裂时,只要数据样本对应的bin容器编号在这些阈值对应的bin集合之中,这条数据就加入分裂后的左子树,否则加入分裂后的右子树。

直接支持高效并行

LightGBM原生支持并行学习,目前支持特征并行和数据并行的两种。特征并行的主要思想是在不同机器在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。数据并行则是让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点
LightGBM针对这两种并行方法都做了优化,在特征并行算法中,通过在本地保存全部数据避免对数据切分结果的通信;在数据并行中使用分散规约(Reduce scatter)直方图合并的任务分摊到不同的机器降低通信和计算,并利用直方图做差进一步减少了一半的通信量
基于投票的数据并行则进一步优化数据并行中的通信代价,使通信代价变成常数级别。在数据量很大的时候,使用投票并行可以得到非常好的加速效果。

 

 

 

网络通信优化

XGBoost由于采用pre-sorted算法,通信代价非常大,所以在并行的时候也是采用histogram算法;LightGBM采用的histogram算法通信代价小,通过使用集合通信算法,能够实现并行计算的线性加速。

3LightGBM原理

提升树是利用加模型与前向分布算法实现学习的优化过程,它有一些高效实现,如XGBoost, pGBRTGBDT(Gradient Boosting Decision Tree)等。其中GBDT采用负梯度作为划分的指标(信息增益),XGBoost则利用到二阶导数。他们共同的不足是,计算信息增益需要扫描所有样本,从而找到最优划分点。在面对大量数据或者特征维度很高时,它们的效率和扩展性很难使人满意。解决这个问题的直接方法就是减少特征量和数据量而且不影响精确度,有部分工作根据数据权重采样来加速booisting的过程,但由于GBDT没有样本权重不能应用。

结合使用 GOSS EFB GBDT 算法就是 LightGBM。微软开源的LightGBM(基于GBDT的)则很好的解决这些问题,它主要包含两个算法:

单边梯度采样,Gradient-based One-Side Sampling(GOSS)

GOSS从减少样本角度):排除大部分小梯度的样本,仅用剩下的样本计算信息增益GBDT虽然没有数据权重,但每个数据实例有不同的梯度,根据计算信息增益的定义,梯度大的实例对信息增益有更大的影响,因此在下采样时,我们应该尽量保留梯度大的样本(预先设定阈值,或者最高百分位间),随机去掉梯度小的样本。我们证明此措施在相同的采样率下比随机采样获得更准确的结果,尤其是在信息增益范围较大时。

GBDT使用决策树,来学习获得一个将输入空间映射到梯度空间的函数。假设训练集有n个实例x1,…,xn,特征维度为s。每次梯度迭时,模型数据变量的损失函数的负梯度方向表示为g1,…,gn,决策树通过最优切分点(最大信息增益点)将数据分到各个节点。GBDT通过分割后的方差衡量信息增益。

GOSS是一种在减少数据量和保证精度上平衡的算法。GOSS是通过区分不同梯度的实例,保留较大梯度实例同时对较小梯度随机采样的方式减少计算量,从而达到提升效率的目的。

EFB是通过特征捆绑的方式减少特征维度(其实是降维技术)的方式,来提升计算效率。通常被捆绑的特征都是互斥的(一个特征值为零,一个特征值不为零),这样两个特征捆绑起来才不会丢失信息。如果两个特征并不是完全互斥(部分情况下两个特征都是非零值),可以用一个指标对特征不互斥程度进行衡量,称之为冲突比率,当这个值较小时,我们可以选择把不完全互斥的两个特征捆绑,而不影响最后的精度。

AdaBoost中,样本权重是数据实例重要性的指标。然而在GBDT中没有原始样本权重,不能应用权重采样。幸运的事,我们观察到GBDT中每个数据都有不同的梯度值,对采样十分有用,即实例的梯度小,实例训练误差也就较小,已经被学习得很好了,直接想法就是丢掉这部分梯度小的数据。然而这样做会改变数据的分布,将会影响训练的模型的精确度,为了避免此问题,我们提出了GOSS

GOSS保留所有的梯度较大的实例,在梯度小的实例上使用随机采样。为了抵消对数据分布的影响,计算信息增益的时候,GOSS对小梯度的数据引入常量乘数GOSS首先根据数据的梯度绝对值排序,选取top a个实例。然后在剩余的数据中随机采样b个实例。接着计算信息增益时为采样出的小梯度数据乘以(1-a)/b,这样算法就会更关注训练不足的实例,而不会过多改变原数据集的分布。

定义:O表示某个固定节点的训练集,分割特征j的分割点d定义为:

其中,

GOSS中,
首先根据数据的梯度将训练降序排序
保留top a个数据实例,作为数据子集A
对于剩下的数据的实例,随机采样获得大小为b的数据子集B
最后我们通过以下方程估计信息增益:


此处GOSS通过较小的数据集估计信息增益V~j(d)将大大地减小计算量。更重要的,我们接下来理论表明GOSS不会丢失许多训练精度,胜过随机采样,理论的证明在附加材料。

 

 

 

我们定义GOSS近似误差为:
根据上述理论,我们得出以下结论:

 

GOSS流程

输入:训练数据,迭代步数d,大梯度数据的采样率a,小梯度数据的采样率b,损失函数和若学习器的类型(一般为决策树);

输出:训练好的强学习器;

1)根据样本点的梯度的绝对值对它们进行降序排序;

2)对排序后的结果选取a*100%的样本生成一个大梯度样本点的子集;

3)对剩下的样本集合(1-a100%的样本,随机的选取b1-a*100%个样本点,生成一个小梯度样本点的集合;

4)将大梯度样本和采样的小梯度样本合并

5)将小梯度样本乘上一个权重系数

6)使用上述的采样的样本,学习一个新的弱学习器

7不断地重复(1~6)步骤直到达到规定的迭代次数或者收敛为止

通过上面的算法可以在不改变数据分布的前提下不损失学习器精度的同时大大的减少模型学习的速率。

从上面的描述可知,当a=0时,GOSS算法退化为随机采样算法;当a=1时,GOSS算法变为采取整个样本的算法。在许多情况下,GOSS算法训练出的模型精确度要高于随机采样算法。另一方面,采样也将会增加若学习器的多样性,从而潜在的提升了训练出的模型泛化能力

互斥特征绑定,Exclusive Feature Bundling(EFB)

EFB从减少特征角度):捆绑互斥特征,也就是他们很少同时取非零值(也就是用一个合成特征代替)。通常真是应用中,虽然特征量比较多,但是由于特征空间十分稀疏,是否可以设计一种无损的方法来减少有效特征呢?特别在稀疏特征空间上,许多特征几乎是互斥的(例如许多特征不会同时为非零值,像one-hot),我们可以捆绑互斥的特征。最后,我们将捆绑问题归约到图着色问题,通过贪心算法求得近似解。
结合使用 GOSS EFB GBDT 算法就是 LightGBM

EBF的算法步骤如下:

将特征按照非零值的个数进行排序
计算不同特征之间的冲突比率
遍历每个特征并尝试合并特征,使冲突比率最小化
高位的数据通常是稀疏的,这种稀疏性启发我们设计一种无损地方法来减少特征的维度。特别的,稀疏特征空间中,许多特征是互斥的,例如他们从不同时为非零值。我们可以绑定互斥的特征为单一特征,通过仔细设计特征臊面算法,我们从特征捆绑中构建了与单个特征相同的特征直方图。这种方式的间直方图时间复杂度从O(#data * #feature)降到O(#data * #bundle),由于#bundle << # feature,我们能够极大地加速GBDT的训练过程而且损失精度。

针对 Leaf-wise(Best-first)树的参数优化

1num_leaves这是控制树模型复杂度的主要参数。理论上, 借鉴 depth-wise , 我们可以设置 num_leaves= 但是, 这种简单的转化在实际应用中表现不佳. 这是因为, 当叶子数目相同时, leaf-wise 树要比 depth-wise 树深得多, 这就有可能导致过拟合. 因此, 当我们试着调整 num_leaves 的取值时, 应该让其小于 . 举个例子, max_depth=7时,depth-wise 树可以达到较高的准确率.但是如果设置 num_leaves 128 , 有可能会导致过拟合, 而将其设置为 70 80 时可能会得到比 depth-wise 树更高的准确率. 其实, depth 的概念在 leaf-wise 树中并没有多大作用, 因为并不存在一个从 leaves depth 的合理映射。

2min_data_in_leaf. 这是处理 leaf-wise 树的过拟合问题中一个非常重要的参数. 它的值取决于训练数据的样本个树和 num_leaves. 将其设置的较大可以避免生成一个过深的树, 但有可能导致欠拟合. 实际应用中, 对于大数据集, 设置其为几百或几千就足够了

3max_depth默认不限制,一般设置该值为5—10即可) 你也可以利用 max_depth 来显式地限制树的深度。

针对更快的训练速度

(1)通过设置 bagging_fraction 和 bagging_freq 参数来使用 bagging 方法;

(2)通过设置 feature_fraction 参数来使用特征的子抽样;

(3)使用较小的 max_bin;

(4)使用 save_binary 在以后的学习过程对数据进行加速加载。

针对更好的准确率

(1)使用较大的 max_bin (学习速度可能变慢);

(2)使用较小的 learning_rate 和较大的 num_iterations;

(3)使用较大的 num_leaves (可能导致过拟合);

(4)使用更大的训练数据;

(5)尝试 dart(一种在多元Additive回归树种使用dropouts的算法).

 处理过拟合

(1)使用较小的 max_bin(默认为255)

(2)使用较小的 num_leaves(默认为31)

(3)使用 min_data_in_leaf(默认为20) 和 min_sum_hessian_in_leaf(默认为)

(4)通过设置 bagging_fraction (默认为1.0)和 bagging_freq (默认为0,意味着禁用bagging,k表示每k次迭代执行一个bagging)来使用 bagging

(5)通过设置 feature_fraction(默认为1.0) 来使用特征子抽样

(6)使用更大的训练数据

(7)使用 lambda_l1(默认为0), lambda_l2 (默认为0)和 min_split_gain(默认为0,表示执行切分的最小增益) 来使用正则

(8)尝试 max_depth 来避免生成过深的树

4相关应用

数据竞赛中刷分拿奖的神兵利器

:本篇有可能因为个人理解与原论文有所偏差,建议结合原论文进行理解

参考

原论文:https://papers.nips.cc/paper/6907-lightgbm-a-highly-efficient-gradient-boosting-decision-tree.pdf

源码:GitHub - microsoft/LightGBM: A fast, distributed, high performance gradient boosting (GBT, GBDT, GBRT, GBM or MART) framework based on decision tree algorithms, used for ranking, classification and many other machine learning tasks.

官方API:Parameters — LightGBM 3.2.1.99 documentation

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值