美文网首页机器学习
lightGBM原理、改进简述 如何看待lightGBM

lightGBM原理、改进简述 如何看待lightGBM

作者: chaaffff | 来源:发表于2018-12-10 10:35 被阅读212次

    1. foreword

    TSA比赛中,开始整的LR,把原始特征one-hot处理后输入LR训练。过了段时间开始搞RF和XGB,再后面搞LightGBM。

    2. lightGBM简介

    xgboost的出现,让数据民工们告别了传统的机器学习算法们:RF、GBM、SVM、LASSO……..。现在微软推出了一个新的boosting框架,想要挑战xgboost的江湖地位。

    顾名思义,lightGBM包含两个关键点:light即轻量级,GBM 梯度提升机。

    LightGBM 是一个梯度 boosting 框架,使用基于学习算法的决策树。它可以说是分布式的,高效的,有以下优势:

    更快的训练效率

    低内存使用

    更高的准确率

    支持并行化学习

    可处理大规模数据

    与常用的机器学习算法进行比较:速度飞起

    3. xgboost缺点

    XGB的介绍见此篇博文

    其缺点,或者说不足之处:

    每轮迭代时,都需要遍历整个训练数据多次。如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复地读写训练数据又会消耗非常大的时间。

    预排序方法(pre-sorted):首先,空间消耗大。这样的算法需要保存数据的特征值,还保存了特征排序的结果(例如排序后的索引,为了后续快速的计算分割点),这里需要消耗训练数据两倍的内存。其次时间上也有较大的开销,在遍历每一个分割点的时候,都需要进行分裂增益的计算,消耗的代价大。

    对cache优化不友好。在预排序后,特征对梯度的访问是一种随机访问,并且不同的特征访问的顺序不一样,无法对cache进行优化。同时,在每一层长树的时候,需要随机访问一个行索引到叶子索引的数组,并且不同特征访问的顺序也不一样,也会造成较大的cache miss。

    4. lightGBM特点

    以上与其说是xgboost的不足,倒不如说是lightGBM作者们构建新算法时着重瞄准的点。解决了什么问题,那么原来模型没解决就成了原模型的缺点。

    概括来说,lightGBM主要有以下特点:

    基于Histogram的决策树算法

    带深度限制的Leaf-wise的叶子生长策略

    直方图做差加速

    直接支持类别特征(Categorical Feature)

    Cache命中率优化

    基于直方图的稀疏特征优化

    多线程优化

    前2个特点使我们尤为关注的。

    Histogram算法

    直方图算法的基本思想:先把连续的浮点特征值离散化成k个整数,同时构造一个宽度为k的直方图。遍历数据时,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。

    带深度限制的Leaf-wise的叶子生长策略

    Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。

    Leaf-wise则是一种更为高效的策略:每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。

    Leaf-wise的缺点:可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度限制,在保证高效率的同时防止过拟合。

    5. lightGBM调参

    (1)num_leaves

    LightGBM使用的是leaf-wise的算法,因此在调节树的复杂程度时,使用的是num_leaves而不是max_depth。

    大致换算关系:num_leaves = 2^(max_depth)

    (2)样本分布非平衡数据集:可以param[‘is_unbalance’]=’true’

    (3)Bagging参数:bagging_fraction+bagging_freq(必须同时设置)、feature_fraction

    (4)min_data_in_leaf、min_sum_hessian_in_leaf

    // 01. train set and test set

    train_data = lgb.Dataset(dtrain[predictors],label=dtrain[target],feature_name=list(dtrain[predictors].columns), categorical_feature=dummies)

    test_data = lgb.Dataset(dtest[predictors],label=dtest[target],feature_name=list(dtest[predictors].columns), categorical_feature=dummies)

    // 02. parameters

    param = {

        'max_depth':6,

        'num_leaves':64,

        'learning_rate':0.03,

        'scale_pos_weight':1,

        'num_threads':40,

        'objective':'binary',

        'bagging_fraction':0.7,

        'bagging_freq':1,

        'min_sum_hessian_in_leaf':100

    }

    param['is_unbalance']='true'

    param['metric'] = 'auc'

    // 03. cv and train

    bst=lgb.cv(param,train_data, num_boost_round=1000, nfold=3, early_stopping_rounds=30)

    estimators = lgb.train(param,train_data,num_boost_round=len(bst['auc-mean']))

    // 04. test predict

    ypred = estimators.predict(dtest[predictors])

    参考资料

    【1】比XGBOOST更快–LightGBM介绍

    https://zhuanlan.zhihu.com/p/25308051

    如何看待微软新开源的LightGBM?

    GBDT 虽然是个强力的模型,但却有着一个致命的缺陷,不能用类似 mini batch 的方式来训练,需要对数据进行无数次的遍历。如果想要速度,就需要把数据都预加载在内存中,但这样数据就会受限于内存的大小;如果想要训练更多的数据,就要使用外存版本的决策树算法。虽然外存算法也有较多优化,SSD 也在普及,但在频繁的 IO 下,速度还是比较慢的。

    为了能让 GBDT 高效地用上更多的数据,我们把思路转向了分布式 GBDT, 然后就有了 LightGBM。设计的思路主要是两点,1. 单个机器在不牺牲速度的情况下,尽可能多地用上更多的数据;2.

    多机并行的时候,通信的代价尽可能地低,并且在计算上可以做到线性加速。

    基于这两个需求,LightGBM 选择了基于 histogram 的决策树算法。相比于另一个主流的算法 pre-sorted(如 xgboost 中的 exact 算法),histogram 在内存消耗和计算代价上都有不少优势。

    Pre-sorted 算法需要的内存约是训练数据的两倍(2 * #data * #features

    * 4Bytes),它需要用32位浮点来保存 feature value,并且对每一列特征,都需要一个额外的排好序的索引,这也需要32位的存储空间。对于 histogram 算法,则只需要(#data

    * #features * 1Bytes)的内存消耗,仅为 pre-sorted算法的1/8。因为 histogram 算法仅需要存储 feature

    bin value (离散化后的数值),不需要原始的 feature value,也不用排序,而 bin

    value 用 uint8_t (256

    bins) 的类型一般也就足够了。

    在计算上的优势则主要体现在“数据分割”。决策树算法有两个主要操作组成,一个是“寻找分割点”,另一个是“数据分割”。从算法时间复杂度来看,Histogram 算法和 pre-sorted 算法在“寻找分割点”的代价是一样的,都是O(#feature*#data)。而在“数据分割”时,pre-sorted 算法需要O(#feature*#data),而 histogram 算法是O(#data)。因为 pre-sorted 算法的每一列特征的顺序都不一样,分割的时候需要对每个特征单独进行一次分割。Histogram算法不需要排序,所有特征共享同一个索引表,分割的时候仅需对这个索引表操作一次就可以。(更新:这一点不完全正确,pre-sorted 与 level-wise 结合的时候,其实可以共用一个索引表(row_idx_to_tree_node_idx)。然后在寻找分割点的时候,同时操作同一层的节点,省去分割的步骤。但这样做的问题是会有非常多随机访问,有很大的chche miss,速度依然很慢。)。

    另一个计算上的优势则是大幅减少了计算分割点增益的次数。对于一个特征,pre-sorted 需要对每一个不同特征值都计算一次分割增益,而 histogram 只需要计算 #bin (histogram 的横轴的数量) 次。

    最后,在数据并行的时候,用 histgoram 可以大幅降低通信代价。用 pre-sorted 算法的话,通信代价是非常大的(几乎是没办法用的)。所以 xgoobst 在并行的时候也使用 histogram 进行通信。

    当然, histogram 算法也有缺点,它不能找到很精确的分割点,训练误差没有 pre-sorted 好。但从实验结果来看, histogram 算法在测试集的误差和 pre-sorted 算法差异并不是很大,甚至有时候效果更好。实际上可能决策树对于分割点的精确程度并不太敏感,而且较“粗”的分割点也自带正则化的效果。

    在 histogram 算法之上, LightGBM 进行进一步的优化。首先它抛弃了大多数 GBDT 工具使用的按层生长

    (level-wise) 的决策树生长策略,而使用了带有深度限制的按叶子生长 (leaf-wise) 算法。 level-wise 过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,不容易过拟合。但实际上level-wise是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销。因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。leaf-wise则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大(一般也是数据量最大)的一个叶子,然后分裂,如此循环。因此同 level-wise 相比,在分裂次数相同的情况下,leaf-wise 可以降低更多的误差,得到更好的精度。leaf-wise 的缺点是可能会长出比较深的决策树,产生过拟合。因此 LightGBM 在leaf-wise 之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。

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

    如需要更多的细节,可以参考github上的文档:https://github.com/Microsoft/LightGBM/wiki/Features

    相关文章

      网友评论

        本文标题:lightGBM原理、改进简述 如何看待lightGBM

        本文链接:https://www.haomeiwen.com/subject/bfcrhqtx.html