LightGBM 的动机
GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型,其主要思想是利用弱分类器(决策树)迭代训练以得到最优模型,该模型具有训练效果好、不易过拟合等优点。GBDT 在工业界应用广泛,通常被用于点击率预测,搜索排序等任务
而 GBDT 在每一次迭代的时候,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。尤其面对工业级海量的数据,普通的 GBDT 算法是不能满足其需求的。
LightGBM 提出的主要原因就是为了解决 GBDT 在海量数据遇到的问题,让 GBDT 可以更好更快地用于工业实践。
LightGBM 与 XGBoost对比:
在不同数据集上的对比
准确率
内存使用情况
计算速度的对比,完成相同的训练量XGBoost通常耗费的时间是LightGBM的数倍之上,在higgs数据集上,它们的差距更是达到了15倍以上。
LightGBM 优化
LightGBM 优化部分包含以下:
- 基于 Histogram 的决策树算法
- 带深度限制的 Leaf-wise 的叶子生长策略
- 直方图做差加速
- 直接支持类别特征(Categorical Feature)
- Cache 命中率优化
- 基于直方图的稀疏特征优化
- 多线程优化。
Histogram 算法
直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。在遍历数据的时候,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。
注:直方图算法是指将样本点离散化成n人箱子,分裂时,整个箱子一起分裂,即整个箱子在左边,
或在右边;XGBoost的近似搜索也类似,但其用的是不直方图,而是分位数;
使用直方图算法有很多优点。首先,最明显就是内存消耗的降低,直方图算法不仅不需要额外存储预排序的结果,而且可以只保存特征离散化后的值,而这个值一般用 8 位整型存储就足够了,内存消耗可以降低为原来的1/8
然后在计算上的代价也大幅降低,预排序算法每遍历一个特征值就需要计算一次分裂的增益,而直方图算法只需要计算k次(k可以认为是常数),时间复杂度从 O(#data#feature)优化到O(k#features)**
当然,Histogram 算法并不是完美的。由于特征被离散化后,找到的并不是很精确的分割点,所以会对结果产生影响。但在不同的数据集上的结果表明,离散化的分割点对最终的精度影响并不是很大,甚至有时候会更好一点。
原因是决策树本来就是弱模型,分割点是不是精确并不是太重要;较粗的分割点也有正则化的效果,
可以有效地防止过拟合;即使单棵树的训练误差比精确分割的算法稍大,但在梯度提升
(Gradient Boosting)的框架下没有太大的影响。
带深度限制的 Leaf-wise 的叶子生长策略
在 Histogram 算法之上,LightGBM 进行进一步的优化。首先它抛弃了大多数 GBDT 工具使用的按层生长 (level-wise) 的决策树生长策略,而使用了带有深度限制的按叶子生长 (leaf-wise) 算法。Level-wise 过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上 Level-wise 是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。
注:level-wise(XGBoost采用的方式)是指分裂时按树的层数分裂出一个完整的决策树,
如分裂的第一层为2个,第二层为4个,第三层为8个.......,第n层为2^n个;
leaf-wise(LightGBM采用的方式)每次从当前所有叶子中,找到分裂增益最大
(一般也是数据量最大)的叶子节点进行分裂。
Leaf-wise 则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同 Level-wise 相比,在分裂次数相同的情况下,Leaf-wise 可以降低更多的误差,得到更好的精度。Leaf-wise 的缺点是可能会长出比较深的决策树,产生过拟合。因此 LightGBM 在 Leaf-wise 之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。
注:leaf-wise分裂完成的有可能不是一棵完全树,且每次分裂时,都会忽略层数,
从当前所有的叶子节点里进行筛选,如上图中第二个箭头右边的图里,筛选的叶子节点为,
第三层的第一个,第四层的第一个和第二个,第二层的第二个;
直方图加速
LightGBM 另一个优化是 Histogram(直方图)做差加速。一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的k个桶。利用这个方法,LightGBM 可以在构造一个叶子的直方图后,可以用非常微小的代价得到它兄弟叶子的直方图,在速度上可以提升一倍。
注:histogram做差加速是指,因为分裂后的左节点和右节点的样本数等于其父节点的样本数,
所以父节点分裂时,左叶子节点分裂完后,右叶子节点里的样本不需要再次计算,
而是直接用父节点的样本减去左叶子节点里的样本得到;
直接支持类别特征
实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化到多维的0/1 特征,降低了空间和时间的效率。而类别特征的使用是在实践中很常用的。基于这个考虑,LightGBM 优化了对类别特征的支持,可以直接输入类别特征,不需要额外的0/1 展开。并在决策树算法上增加了类别特征的决策规则
LightGBM 的单机版本还有很多其他细节上的优化,比如 cache 访问优化,多线程优化,稀疏特征优化等等。优化汇总如下:
LightGBM并行优化
LightGBM 还具有支持高效并行的优点。LightGBM 原生支持并行学习,目前支持特征并行和数据并行的两种。
特征并行的主要思想是在不同机器在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。
数据并行则是让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点。
LightGBM 针对这两种并行方法都做了优化:
在特征并行算法中,通过在本地保存全部数据避免对数据切分结果的通信;
在数据并行中使用分散规约 (Reduce scatter) 把直方图合并的任务分摊到不同的机器,降低通信和计算,并利用直方图做差,进一步减少了一半的通信量。基于投票的数据并行则进一步优化数据并行中的通信代价,使通信代价变成常数级别。在数据量很大的时候,使用投票并行可以得到非常好的加速效果。
其他注意
- 当生长相同的叶子时,Leaf-wise 比 level-wise 减少更多的损失。
- 高速,高效处理大数据,运行时需要更低的内存,支持 GPU
- 不要在少量数据上使用,会过拟合,建议 10,000+ 行记录时使用。
lightGBM的坑
设置提前停止
如果在训练过程中启用了提前停止,可以用 bst.best_iteration从最佳迭代中获得预测结果:
ypred = bst.predict(data,num_iteration = bst.best_iteration )
自动处理类别特征
- 当使用本地分类特征,LightGBM能提供良好的精确度。不像简单的one-hot编码,lightGBM可以找到分类特征的最优分割。
- 用categorical_feature指定分类特征
- 首先需要转换为int类型,并且只支持非负数。转换为连续范围更好。
- 使用min_data_per_group,cat_smooth去处理过拟合(当#data比较小,或者#category比较大)
- 对于具有高基数的分类特征(#category比较大),最好转换为数字特征。
自动处理缺失值
- lightGBM通过默认方式处理缺失值,可以通过设置use_missing = false 来使其无效。
- lightGBM通过默认的方式用NA(NaN)去表示缺失值,可以通过设置zero_as_missing = true 将其变为0
- 当设置zero_as_missing = false(默认)时,在稀疏矩阵里(和lightSVM),没有显示的值视为0
- 当设置zero_as_missing = true时,NA和0(包括在稀疏矩阵里,没有显示的值)视为缺失。
参考来源: https://blog.csdn.net/weixin_39807102/article/details/81912566