在2017年年1月微软在GitHub的上开源了一个新的升压工具LightGBM(Light Gradient Boosting Machine )。它是一种优秀的机器学习算法框架,与XGBoost算法相比,在不降低准确率的前提下,速度提升了10倍左右,占用内存下降了3倍左右。
目录
性能对比
GBDT和XGBoost算法的缺点和不足
LightGBM优化
优化策略:直方图算法(Histogram算法)
优化策略:GOSS(单边梯度采样算法)
优化策略:EFB(独立特征合并)
优化策略:叶子生长(leaf-wise)的算法
优化策略:支持类别特征(即不需要做one-hot编码)
优化策略:支持高效并行
XGBoost与LightGBM对比
其他
下图是LightGBM在GitHub主页上展示的在五个不同数据集上得到的性能实验数据,比起XGBoost算法,LightGBM算法在准确度一样,但在速度和内存的消耗上有更明显的优势。
耗时比较:
准确率比较:
内存消耗:
梯度提升决策树算法的实现原理,它是基于决策树的提升算法,采用前向分布算法,在每次迭代中,通过负梯度拟合残差,从而学习到一颗决策树。在生成决策树的过程中,进行特征选择节点分裂时,需要对特征值进行排序,遍历所有可能的划分点,然后计算信息增益,从而选择出最优的分裂点。每轮迭代都会对整个训练集进行遍历,这样既耗费内存,也非常的耗时。
在这部分比较常用的优化算法有预排序,就是对所有特征值优先排序,计算每个划分点的增益值,并且保存在内存中。在迭代的过程中通过查表的方式进行选择最优分裂点。XGBoost算法已经提供了这种方式的优化,然而它在面对海量数据和特征维度很高的数据集时,算法的效率和扩展性很难让人满意。
如果要对GBDT进行优化,有两个方面:
然而,LightGBM算法正是通过对模型训练时样本点的采样优化和选择分裂点时的特征维度的优化,在不牺牲精度的条件下,提高了训练速度。LightGBM算法是一种改进则是直方图算法,他把连续特征值划分到k个桶中去(连续值分箱),划分点则在这k个点中选取。k< LightGBM采用了基于直方图的算法将连续的特征值离散化成了K个整数,构造宽度为K的直方图,遍历训练数据,统计每个离散值在直方图中的累积统计量。在选取特征的分裂点的时候,只需要遍历排序直方图的离散值。使用直方图算法降低了算法的计算代价,XGBoost采用的预排序需要遍历每一个特征值,计算分裂增益,而直方图算法只需要计算K次,提高了寻找分裂点的效率;降低了算法的内存消耗,不需要存储预排序结果,只需要保存特征离散化后的值。 但是特征值被离散化后,找到的并不是精确的分割点,会不会对学习的精度上造成影响呢?在实际的数据集上表明,离散化的分裂点对最终学习的精度影响并不大,甚至会更好一些。因为这里的决策树本身就是弱学习器,采用直方图离散化特征值反而会起到正则化的效果,提高算法的泛化能力。 XGBoost预排序算法每遍历一个特征值就需要计算一次分裂的增益,而直方图算法只需要计算k次(k可以认为是常数),时间复杂度从O(#data * #feature) 优化到O(k* #features)。 LightGBM的直方图做差加速 一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的k个桶。利用这个方法,LightGBM可以在构造一个叶子的直方图后(父节点在上一轮就已经计算出来了),可以用非常微小的代价得到它兄弟叶子的直方图,在速度上可以提升一倍。 单边梯度采样算法(Grandient-based One-Side Sampling,GOSS),大致的意思是根据样本某一特征上的单梯度作为样本的权值进行训练。 LightGBM使用GOSS算法进行训练样本采样的优化。在AdaBoost算法中,采用了增加被错误分类的样本的权重来优化下一次迭代时对哪些样本进行重点训练。然而GBDT算法中没有样本的权重,但是LightGBM采用了基于每个样本的梯度进行训练样本的优化,具有较大梯度的数据对计算信息增益的贡献比较大。当一个样本点的梯度很小,说明该样本的训练误差很小,即该样本已经被充分训练。然而在计算过程中,仅仅保留梯度较大的样本(例如:预设置一个阈值,或者保留最高若干百分位的梯度样本),抛弃梯度较小样本,会改变样本的分布并且降低学习的精度。GOSS算法的提出很好的解决了这个问题。 GOSS算法的基本思想是首先对训练集数据根据梯度排序,预设一个比例,保留在所有样本中梯度高于的数据样本;梯度低于该比例的数据样本不会直接丢弃,而是设置一个采样比例,从梯度较小的样本中按比例抽取样本。为了弥补对样本分布造成的影响,GOSS算法在计算信息增益时,会对较小梯度的数据集乘以一个系数,用来放大。这样,在计算信息增益时,算法可以更加关注“未被充分训练”的样本数据。 其采样的方式有点巧妙: 总的来说就是a% * #samples + b% * (1 - a%) * #samples个样本作为训练样本。 而这样的构造是为了尽可能保持与总的数据分布一致,并且保证小梯度值的样本得到训练。 此时,已经得到了通过GOSS算法筛选出来的样本和这些样本对应的梯度(残差)。此时的样本数量比没有GOSS处理之前少了很多。并且该方法是根据样本误差当权重做采样的,这样做出来的准确率优于随机采样。,精度更高。 EFB(Exclusive Feature Bundling)中文名叫独立特征合并,顾名思义它就是将若干个特征合并在一起。使用这个算法的原因是因为我们要解决数据稀疏的问题。在很多时候,数据通常都是几千万维的稀疏数据。因此我们对不同维度的数据合并一齐使得一个稀疏矩阵变成一个稠密矩阵。这里就有两个问题:1. 如何确定哪些特征用于融合且效果为较好。2. 如何将这些特征合并到一齐。 高维数据一般是稀疏的,可以设计一种损失最小的特征减少方法。并且,在稀疏特征空间中,许多特征都是互斥的,也就是它们几乎不同时取非0值。因此,我们可以安全的把这些互斥特征绑到一起形成一个特征,然后基于这些特征束构建直方图,这样又可以加速了。 有两个问题待解决,如何判断哪些特征该绑到一起,如何构建绑定。这是NP难的。 首先,转换到图着色问题。G=(V, E),把关联矩阵G的每一行看成特征,从而得到|V|个特征,互斥束就图中颜色相同的顶点。图中点就是特征,边代表两个特征不互斥,也就是特征之间的冲突。如果算法允许小的冲突,可以得到更小的特征束数量,计算效率会更高。证明发现随机污染一小部分特征值,最多影响训练精度 ,是所有束中冲突最大的。通过选取合适的,我们可以很好的在效率和精度之间寻找平衡。 最后,排序就按照束的度来进行。当然,更一步优化是不够造图,直接根据非零值的数量排序,这个根据度排序很像,因为更多非0值意味着更高概率的冲突。更改了排序策略,可以避免重复。 第二个问题,合并特征,从而降低训练复杂度,关键是我们可以确保原先特征值可以从特征束中识别出来。因为直方图存储的是特征的离散桶,而不是连续值,我们可以通过把互斥特征放到不同桶,从而构造一个特征束。这可以通过添加偏移实现。如,假设我们有2个特征在一个特征束中,原先特征A的范围为[0,10),特征B的范围为[0,20),我们给特征B加上一个偏移10,它就变成[10,30),这样我们就可以执行安全的合并了,用特征束[0,30)代替特征A和B。具体算法如下。 EFB算法可以把很多特征绑到一起,形成更少的稠密特征束,这样可以避免对0特征值的无用的计算。加速计算直方图还可以用一个表记录数据的非0值。 拟合该轮残差树前,用EFB算法减少特征量 发现了互斥特征后,下一步就是对互斥特征对进行”捆绑”。 (2)如何把互斥特征对捆绑 大多数的决策树学习算法的树生成方式都是采用按层生长(level-wise)的策略。如下图所示: Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。 不同的是,LightGBM采用了一种更为高效的按叶子生长(leaf-wise)的策略。该策略每次从当前决策树所有的叶子节点中,找到分裂增益最大的一个叶子节点,然后分裂,如此循环往复。这样的机制,减少了对增益较低的叶子节点的分裂计算,减少了很多没必要的开销。与leve-wise的策略相比,在分裂次数相同的情况下,leaf-wise可以降低误差,得到更好的精度。Leaf-wise算法的缺点是可能会生成较深的决策树。因此,LightGBM在Leaf-wise上增加了限制最大深度的参数,在保证算法高效的同时,防止过拟合。 实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化到多维的one-hot编码特征,降低了空间和时间的效率。而类别特征的使用是在实践中很常用的。基于这个考虑,LightGBM优化了对类别特征的支持,可以直接输入类别特征,不需要额外的one-hot编码展开。并在决策树算法上增加了类别特征的决策规则。在Expo数据集上的实验,相比0/1展开的方法,训练速度可以加速8倍,并且精度一致。 LightGBM还具有支持高效并行的优点。LightGBM原生支持并行学习,目前支持特征并行和数据并行的两种。 LightGBM针对这两种并行方法都做了优化,在特征并行算法中,通过在本地保存全部数据避免对数据切分结果的通信;在数据并行中使用分散规约 (Reduce scatter) 把直方图合并的任务分摊到不同的机器,降低通信和计算,并利用直方图做差,进一步减少了一半的通信量。 LightGBM 的单机版本还有很多其他细节上的优化,比如 cache 访问优化,多线程优化,稀疏特征优化等等,更多的细节可以查阅 https://github.com/Microsoft/LightGBM/wiki)上的文档说明。优化汇总对比表: 全部的优化步骤为: (1)在拟合残差树之前,引入GOSS算法剔除权重较小的样本,减少数据量; 直方图方法有什么优点? Lightgbm如何节省时间和空间? Leaf-wise生长方式有什么优缺点? Xgboost中的预排序算法? LightGBM优化
优化策略:直方图算法(Histogram算法)
优化策略:GOSS(单边梯度采样算法)
优化策略:EFB(独立特征合并)
为什么要用EFB?
在处理高维特征数据的时候,容易出现数据稀疏问题,存在有些特征之间是互斥的,这样造成了没有必要的计算开销。EFB方法能够把互斥的特征绑定成一个特征,又不影响数据质量,减少不必要的计算开销。
EFB的实现原理和过程是什么?
关注两个问题:
(1)如何发现互斥特征对;
(2)如何把互斥特征对捆绑。
解决这两个问题,EFB方法的原理也就明白。
(1)如何发现互斥特征对
遍历每一条样本,当两个特征没有同时为非零取值的情况,这样的两个特征为互斥特征;当然,也可以给一个冲突阈值,两个特征同时为非零的个数占总的样本的比例,如果这个比例小于阈值,则也考虑为互斥特征。
例如,以下表格中特征1和特征2就是互斥特征。
特征1的取值在[0,3],让特征2的每个取值都加一个偏置3.1,这样就把特征1和特征2分开了,这两个特征放入同一个直方图中就不会有交叉的地方了。把这两个特放入同一个直方图中做统计,这个直方图的取值在[0,3.2+3.1]–>[0,6.3]。(实质就是减少了一个直方图,减少了计算内存开销)优化策略:叶子生长(leaf-wise)的算法
优化策略:支持类别特征(即不需要做one-hot编码)
优化策略:支持高效并行
(2)在拟合残差树之前,在引入EFB算法,在高维(高维一般也稀疏)的数据情况捆绑互斥(或接近互斥)的特征(捆绑的方法是采用直方图的方法),达到减少特征量;
(3)在拟合残差树之前,对离散值(如输入是”A”, “B”,”C”等取值的特征输入)的处理,让输入的这些离散值直接被模型支持(这一点也是和xgboost的区别,xgboost不能直接支持类似”A”,”B”,”C”的特征输入);
(4)拟合残差树的过程中,采样leaf-wise方法,减少节点的分裂个数,进而减少计算量(主要体现在与xgboost的level-wise的区别);
(5)拟合残差树的过程中,采用直方图方法(以及直方图做差加速)提高求最优分割点的速度(主要体现在与xgboost的预排序的区别)。XGBoost与LightGBM对比
其他
通过以上讲解,
<1> 最后求增益做分裂的过程,仅需存储每个特征的直方图,计算内存消耗很小;
<2> 采用直方图方法求增益,因为离散化取值,与预排序算法相比对于每个特征遍历求增益的次数减少了很多,这样就加快了优分割点的求解速度;
<2> 因为是以某一取值范围为取值,而不是具体的原始数据,实际上可能决策树对于分割点的精确程度并不太敏感,而且较“粗”的分割点也自带正则化的效果。
节省时间:
<1> GOSS减少样本数量,在做每个特征直方图(统计样本个数、梯度之和)的时候因为样本数量变少了,可以减少计算量;
<2> EFB减少特征量,特征数量减少了,直方图的个数也就减少了,即省时间也省计算内存。
节省空间:
<1> 直方图算法,仅需存储每个特征的直方图,计算内存消耗很小;
<2> EFB减少特征量,特征数量减少了,直方图的个数也就减少了,即省时间也省计算内存.
优点:
1 更高效。因为以前计算过的叶子节点的最大增益是不变的,无需重复计算。
2 与level-wise相比,在分裂次数相同的情况,因为每次分裂都是求所有叶子节点中增益最大的那个最为最优分割点,leaf-wise能达到更好的精度。
缺点:
容易过拟合,因此增加了一个最大树深的限制条件。
对于遍历到某一个特征,把样本按该特征的取值排序。这样在遍历分割点的时候就能够很快地将样本分成两批。