参考文章:LightGBM——提升机器算法(图解+理论+安装方法+python代码), Lightgbm基本原理介绍
LightGBM (Light Gradient Boosting Machine)是一个实现 GBDT 算法的框架,支持高效率的并行训练,并且具有以下优点:
GBDT 每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据会消耗非常大的时间。尤其面对工业级海量的数据,普通的 GBDT 算法是不能满足其需求的。LightGBM 提出的主要原因就是为了解决 GBDT 在海量数据遇到的问题,让 GBDT 可以更好更快地用于工业实践。
lightGBM和XGBoost进行对比:
XGBoost中采用预排序的方法,计算过程当中是按照value的排序,逐个数据样本来计算划分收益,这样的算法能够精确的找到最佳划分值,但是代价比较大同时也没有较好的推广性。
在LightGBM中,直方图算法的理解是首先确定对于每一个特征需要多少个箱子(bin)并为每一个箱子分配一个整数;然后将浮点数的范围均分成若干区间,区间个数与箱子个数相等,根据特征所在的bin对其进行梯度累加和个数统计,当遍历一次数据后,然后根据直方图的离散值,遍历寻找最优的分割点。
使用直方图算法的优点主要内存消耗的降低和在计算上的代价也大幅降低。
直方图算法有几个需要注意的地方:
当我们用数据的bin描述数据特征的时候带来的变化:首先是不需要像预排序算法那样去存储每一个排序后数据的序列,也就是下图灰色的表,在LightGBM中,这部分的计算代价是0;第二个,一般bin会控制在一个比较小的范围,所以我们可以用更小的内存来存储
在Histogram算法之上,LightGBM进行进一步的优化。首先它抛弃了大多数GBDT工具使用的按层生长 (level-wise)的决策树生长策略,而使用了带有深度限制的按叶子生长 (leaf-wise)算法。
XGBoost采用的是按层生长level-wise生长策略,能够同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合;但不加区分的对待同一层的叶子,带来了很多没必要的开销。因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。
LightGBM采用leaf-wise生长策略,每次从当前所有叶子中找到分裂增益最大(一般也是数据量最大)的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。
直方图做差优化可以达到两倍的加速,可以观察到一个叶子节点上的直方图,可以由它的父亲节点直方图减去它兄弟节点的直方图来得到。根据这一点我们可以构造出来数据量比较小的叶子节点上的直方图,然后用直方图做差来得到数据量比较大的叶子节点上的直方图,从而达到加速的效果。
预排序算法中有两个频繁的操作会导致cache-miss,也就是缓存消失(对速度的影响很大,特别是数据量很大的时候,顺序访问比随机访问的速度快4倍以上 )。
这两个操作都是随机的访问,会给系统性能带来非常大的下降。
LightGBM使用的直方图算法能很好的解决这类问题。首先。对梯度的访问,因为不用对特征进行排序,同时,所有的特征都用同样的方式来访问,所以只需要对梯度访问的顺序进行重新排序,所有的特征都能连续的访问梯度。并且直方图算法不需要把数据id到叶子节点号上(不需要这个索引表,没有这个缓存消失问题)
传统的机器学习一般不能支持直接输入类别特征,需要先转化成多维的0-1特征,这样无论在空间上还是时间上效率都不高。LightGBM通过更改决策树算法的决策规则,直接原生支持类别特征,不需要转化,提高了近8倍的速度。
LightGBM原生支持并行学习,目前支持特征并行(Featrue Parallelization)和数据并行(Data Parallelization)两种,还有一种是基于投票的数据并行(Voting Parallelization)
LightGBM针对这两种并行方法都做了优化。
下图更好的说明了以上这三种并行学习的整体流程:
GOSS(基于梯度的单边采样)方法的主要思想就是,梯度大的样本点在信息增益的计算上扮演着主要的作用,也就是说这些梯度大的样本点会贡献更多的信息增益,因此为了保持信息增益评估的精度,当我们对样本进行下采样的时候保留这些梯度大的样本点,而对于梯度小的样本点按比例进行随机采样即可。
下面就是GOSS算法的伪代码:
输入:训练数据,迭代步数d,大梯度数据的采样率a,小梯度数据的采样率b,损失函数和弱学习器的类型(一般为决策树);
输出:训练好的强学习器;
(1)根据样本点的梯度的绝对值对它们进行降序排序;
(2)对排序后的结果选取前a*100%的样本生成一个大梯度样本点的子集;
(3)对剩下的样本集合(1-a)100%的样本,随机的选取b(1-a)*100%个样本点,生成一个小梯度样本点的集合;
(4)将大梯度样本和采样的小梯度样本合并;
(5)将小梯度样本乘上一个权重系数(1-a)/b;
(6)使用上述的采样的样本,学习一个新的弱学习器;
(7)不断地重复(1)~(6)步骤直到达到规定的迭代次数或者收敛为止。
通过上面的算法可以在不改变数据分布的前提下不损失学习器精度的同时大大的减少模型学习的速率。
从上面的描述可知,当a=0时,GOSS算法退化为随机采样算法;当a=1时,GOSS算法变为采取整个样本的算法。在许多情况下,GOSS算法训练出的模型精确度要高于随机采样算法。另一方面,采样也将会增加若学习器的多样性,从而潜在的提升了训练出的模型泛化能力。
Lightgbm实现中不仅进行了数据采样,也进行了特征抽样,使得模型的训练速度进一步的减少。EFB算法的主要思想就是,通常在实际应用中高纬度的数据往往都是稀疏数据(如one-hot编码),这使我们有可能设计一种几乎无损的方法来减少有效特征的数量。尤其,在稀疏特征空间中许多特征都是互斥的(例如,很少同时出现非0值)。这就使我们可以安全的将互斥特征绑定在一起形成一个特征,从而减少特征维度。
这种方法实现起来有两个难点,第一是哪些特征可以合并到一起,第二是如何将特征合并到一起实现降维,下面分别介绍
下面是Greedy bundle的伪代码:
输入:特征F,最大冲突数K,图G;
输出:特征捆绑集合bundles;
(1)构造一个边带有权重的图,其权值对应于特征之间的总冲突;
(2)通过特征在图中的度来降序排序特征;
(3)检查有序列表中的每个特征,并将其分配给具有小冲突的现有bundling(由γ控制),或创建新bundling。
上述算法的时间复杂度为O(#feature^2)并且在模型训练之前仅仅被处理一次即可。在特征维度不是很大时,这样的复杂度是可以接受的。但是当样本维度较高时,这种方法就会特别的低效。所以对于此,作者又提出的另外一种更加高效的算法:按非零值计数排序,这类似于按度数排序,因为更多的非零值通常会导致更高的冲突概率。 这仅仅改变了上述算法的排序策略,所以只是针对上述算法将按度数排序改为按非0值数量排序,其他不变。
MEF算法将bundle中的特征合并成新的特征,合并的关键是原有的不同特征值在构建后的bundle中仍能够识别。由于基于直方图的方法存储的是离散的bin而不是连续的数值,因此可以通过添加偏移的方法将不同特征的bin值设定为不同的区间。