LightGBM是一个梯度Boosting框架,使用基于决策树的学习算法。它可以说是分布式的,高效的,有以下优势:
1)更快的训练效率
2)低内存使用
3)更高的准确率
4)支持并行化学习
5)可以处理大规模数据
C3.0(信息增益,信息增益率)−>CART(Gini)−>提升树(AdaBoost)−>GBDT−>XGBoost−>Lightgbm
传统的boosting算法(如GBDT和XGBoost)已经有相当好的效率,但是在如今的大样本和高维度的环境下,传统的boosting似乎在效率和可扩展性上不能满足现在的需求了,主要的原因就是传统的boosting算法需要对每一个特征都要扫描所有的样本点来选择最好的切分点,这是非常的耗时。为了解决这种在大样本高纬度数据的环境下耗时的问题,出现了Lightgbm 。
Lightgbm使用了如下两种解决办法:一是GOSS(Gradient-based One-Side Sampling, 基于梯度的单边采样),不是使用所用的样本点来计算梯度,而是对样本进行采样来计算梯度;二是EFB(Exclusive Feature Bundling, 互斥特征捆绑) ,这里不是使用所有的特征来进行扫描获得最佳的切分点,而是将某些特征进行捆绑在一起来降低特征的维度,是寻找最佳切分点的消耗减少。这样大大的降低的处理样本的时间复杂度,但在精度上,通过大量的实验证明,在某些数据集上使用Lightgbm并不损失精度,甚至有时还会提升精度。
Pre-sorted 算法需要的内存约是训练数据的两倍(2 * #data * #features* 4Bytes),它需要用32位浮点(4Bytes)来保存 feature value,并且对每一列特征,都需要一个额外的排好序的索引,这也需要32位(4Bytes)的存储空间。因此是(2 * #data * #features* 4Bytes)。而对于 histogram 算法,则只需要(#data * #features * 1Bytes)的内存消耗,仅为 pre-sorted算法的1/8。因为 histogram 算法仅需要存储 feature bin value (离散化后的数值),不需要原始的 feature value,也不用排序,而 bin value 用 1Bytes(256 bins) 的大小一般也就足够了。
计算上的优势则是大幅减少了计算分割点增益的次数。对于每一个特征,pre-sorted 需要对每一个不同特征值都计算一次分割增益,代价是O(#feature*#distinct_values_of_the_feature);而 histogram 只需要计算#bins次,代价是(#feature*#bins)。
还有一个很重要的点是cache-miss。事实上,cache-miss对速度的影响是特别大的。预排序中有2个操作频繁的地方会造成cache miss,一是对梯度的访问,在计算gain的时候需要利用梯度,不同特征访问梯度的顺序都是不一样的,且是随机的,因此这部分会造成严重的cache-miss。二是对于索引表的访问,预排序使用了一个行号到叶子节点号的索引表(row_idx_to_tree_node_idx ),来防止数据切分时对所有的数据进行切分,即只对该叶子节点上的样本切分。在与level-wise进行结合的时候, 每一个叶子节点都要切分数据,这也是随机的访问。这样会带来严重的系统性能下降。而直方图算法则是天然的cache friendly。在直方图算法的第3个for循环的时候,就已经统计好了每个bin的梯度,因此,在计算gain的时候,只需要对bin进行访问,造成的cache-miss问题会小很多。
最后,在数据并行的时候,用 histgoram 可以大幅降低通信代价。用 pre-sorted 算法的话,通信代价是非常大的(几乎是没办法用的)。所以 xgoobst 在并行的时候也使用 histogram 进行通信。 (数据并行的优化是Lightgbm的令一个亮点,这里不是特别理解,需要再深入研究)
histogram 算法不能找到很精确的分割点,训练误差没有 pre-sorted 好。但从实验结果来看, histogram 算法在测试集的误差和 pre-sorted 算法差异并不是很大,甚至有时候效果更好。实际上可能决策树对于分割点的精确程度并不太敏感,而且较“粗”的分割点也自带正则化的效果,再加上boosting算法本身就是弱分类器的集成。
直方图算法的基本思想:先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。遍历数据时,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。
带深度限制的Leaf-wise的叶子生长策略
Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。
Leaf-wise则是一种更为高效的策略:每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。
Leaf-wise的缺点:可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度限制,在保证高效率的同时防止过拟合。
传统的特征并行算法旨在于在并行化决策树中的寻找最佳切分点,主要流程如下:
(1)垂直切分数据(不同的Worker有不同的特征集);
(2)在本地特征集寻找最佳切分点 {特征, 阈值};
(3)在各个机器之间进行通信,拿出自己的最佳切分点,然后从所有的最佳切分点中推举出一个最好的切分点,作为全局的切分点;
(4)以最佳划分方法对数据进行划分,并将数据划分结果传递给其他Worker;
(5)其他Worker对接受到的数据进一步划分。
(1)存在计算上的局限,传统特征并行无法加速特征切分(时间复杂度为 )。 因此,当数据量很大的时候,难以加速。
(2)需要对划分的结果进行通信整合,其额外的时间复杂度约为。(一个数据一个字节)
在数据量很大时,传统并行方法无法有效地对特征进行并行,LightGBM 做了一些改变:不再垂直划分数据,即每个Worker都持有全部数据。 因此,LighetGBM中没有数据划分结果之间通信的开销,各个Worker都知道如何划分数据。 而且,样本量也不会变得更大,所以,使每个机器都持有全部数据是合理的。
LightGBM 中特征并行的流程如下:
(1)每个Worker都在本地特征集上寻找最佳划分点{特征, 阈值};
(2)本地进行各个划分的通信整合并得到最佳划分;
(3)执行最佳划分。
然而,该特征并行算法在数据量很大时仍然存在计算上的局限。因此,建议在数据量很大时使用数据并行。
2.1 传统的数据并行算法 数据并行目的是并行化整个决策学习过程。数据并行的主要流程如下:
(1)水平划分数据;
(2)Worker以本地数据构建本地直方图;
(3)将所有Worker的本地直方图整合成全局整合图;
(4)在全局直方图中寻找最佳切分,然后执行此切分。
2.2 传统数据并行的不足: 高通讯开销。 如果使用点对点的通讯算法,一个Worker的通讯开销大约为。 如果使用集体通讯算法(例如, “All Reduce”等),通讯开销大约为。
2.3 LightGBM中的数据并行 LightGBM 中通过减少数据并行过程中的通讯开销,来减少数据并行的开销:
(1)不同于传统数据并行算法中的,整合所有本地直方图以形成全局直方图的方式,LightGBM 使用Reduce scatter的方式对不同Worker的不同特征(不重叠的)进行整合。 然后Worker从本地整合直方图中寻找最佳划分并同步到全局的最佳划分中。
(2)如上面提到的,LightGBM 通过直方图做差法加速训练。 基于此,我们可以进行单叶子的直方图通讯,并且在相邻直方图上使用做差法。 通过上述方法,LightGBM 将数据并行中的通讯开销减少到。
预排序算法中有两个频繁的操作会导致cache-miss,也就是缓存消失(对速度的影响很大,特别是数据量很大的时候,顺序访问比随机访问的速度快4倍以上 )。
对梯度的访问:在计算增益的时候需要利用梯度,对于不同的特征,访问梯度的顺序是不一样的,并且是随机的 对于索引表的访问:预排序算法使用了行号和叶子节点号的索引表,防止数据切分的时候对所有的特征进行切分。同访问梯度一样,所有的特征都要通过访问这个索引表来索引。 这两个操作都是随机的访问,会给系统性能带来非常大的下降。
LightGBM使用的直方图算法能很好的解决这类问题。首先。对梯度的访问,因为不用对特征进行排序,同时,所有的特征都用同样的方式来访问,所以只需要对梯度访问的顺序进行重新排序,所有的特征都能连续的访问梯度。并且直方图算法不需要把数据id到叶子节点号上(不需要这个索引表,没有这个缓存消失问题)
传统的机器学习一般不能支持直接输入类别特征,需要先转化成多维的0-1特征,这样无论在空间上还是时间上效率都不高。LightGBM通过更改决策树算法的决策规则,直接原生支持类别特征,不需要转化,提高了近8倍的速度。
LightGBM是一个性能高度优化的GBTD算法。LightGBM采用了GOSS算法对训练样本集的采样进行了优化,减少了计算梯度时的样本点;同时采用EFB算法,绑定互斥特征,降低选择分裂点时的特征维度。采用了直方图算法和按叶子节点生长的树生成策略。在GOSS和EFB算法的帮助下,使LightGBM在处理大训练样本和高维特征的场景时,在计算速度和内存消耗上明显的优于XGBoost。
(1) 使用num_leaves 因为LightGBM使用的是leaf-wise的算法,因此在调节树的复杂程度时,使用的是num_leaves而不是max_depth。 大致换算关系:num_leaves = 2(max_depth)。它的值的设置应该小于2(max_depth),否则可能会导致过拟合。 (2)对于非平衡数据集:可以param[‘is_unbalance’]='true’ (3)Bagging参数:bagging_fraction+bagging_freq(必须同时设置)、feature_fraction。bagging_fraction可以使bagging的更快的运行出结果,feature_fraction设置在每次迭代中使用特征的比例。 (4) min_data_in_leaf:这也是一个比较重要的参数,调大它的值可以防止过拟合,它的值通常设置的比较大。 (5)max_bin:调小max_bin的值可以提高模型训练速度,调大它的值和调大num_leaves起到的效果类似。
可以应用一般的损失函数,可以处理分类问题和回归问题,常见应用场景比如网页搜索排序、社会生态学等。 优点:a、能够直接处理混合类型的特征; b、对输出空间的异常值的鲁棒性(通过鲁邦的损失函数) 缺点:难以并行,因为本身boosting的思想是一个接一个的训练基学习器
XGBoost模型: 可以使用两种方式防止过拟合:a、直接控制模型复杂度;b、给模型训练增加随机性使其对噪声数据更加鲁棒
LightGBM模型 特点:a、效率和内存上的提升,直方图算法,LightGBM提供一种数据类型的封装相对Numpy, Pandas,Array等数据对象而言节省了内存的使用,原因在于它只需要保存离散的直方图。b、稀疏特征优化,对稀疏特征构建直方图时的时间复杂度为O(2*#非零数据)。c、准确率上的优化
三种模型对比 GBDT无明显的正则化,仅使用了目标函数一阶泰勒展开。 XGBoost使用了目标函数的二阶泰勒展开式(一加快了收敛速度,二本身模型训练的学习率shrinkage可以通过二阶导数做一个逼近),加入了列采样;对缺失值的处理;通过预排序的方法来实现特征并行,提高模型训练效率;XGBoost在选择最佳切分点时可以开启多线程进行,大大提高了运行速度;支持分布式。 LightGBM实现并行方式是通过直方图算法与XGBoost(预排序方式)不同
参考:
西瓜书
cs229吴恩达机器学习课程
李航统计学习
谷歌搜索
公式推导参考:http://t.cn/EJ4F9Q0