RF,GBDT,XGBOOST, LightGBM的对比和分析

1.概述

RF、GBDT和都属于集成学习(Ensemble Learning),集成学习的目的是通过结合多个基学习器的预测结果来改善单个学习器的泛化能力和鲁棒性。 xgboost和lightgbm是gbdt的优秀工程实现及优化改进。

2.集成学习方法

bootstrap, boosting, bagging, stacking集成算法原理介绍

详细的bootstrap

3.各种实例解析

3.1.信息增益和决策树

信息增益、信息增益率、gini、特征选择、决策树

决策树系列算法

3.2.RF

随机森林基于Bagging的策略是Bagging的扩展变体,概括RF包括四个部分:1、随机选择样本(放回抽样);2、随机选择特征(相比普通通bagging多了特征采样);3、构建决策树;4、随机森林投票(平均)。 在构建决策树的时候,RF的每棵决策树都最大可能的进行生长而不进行剪枝;在对预测输出进行结合时,RF通常对分类问题使用简单投票法,回归任务使用简单平均法。   

RF的重要特性是不用对其进行交叉验证或者使用一个独立的测试集获得无偏估计,它可以在内部进行评估,也就是说在生成的过程中可以对误差进行无偏估计,由于每个基学习器只使用了训练集中约63.2%的样本,剩下约36.8%的样本可用做验证集来对其泛化性能进行“包外估计”。 

3.3.GBDT

基于Boosting,GBDT与传统的Boosting区别较大,它的每一次计算都是为了减少上一次的残差,而为了消除残差,我们可以在残差减小的梯度方向上建立模型,所以说,在GradientBoost中,每个新的模型的建立是为了使得之前的模型的残差往梯度下降的方法,与传统的Boosting中关注正确错误的样本加权有着很大的区别。 
  在GradientBoosting算法中,关键就是利用损失函数的负梯度方向在当前模型的值作为残差的近似值,进而拟合一棵CART回归树。 GBDT的会累加所有树的结果,而这种累加是无法通过分类完成的,因此GBDT的树都是CART回归树,而不是分类树(尽管GBDT调整后也可以用于分类但不代表GBDT的树为分类树)。

3.4.XGBoost

XGBoost的性能在GBDT上又有一步提升,坊间对XGBoost最大的认知在于其能够自动地运用CPU的多线程进行并行计算,同时在算法精度上也进行了精度的提高。 

1).传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。 —可以通过booster [default=gbtree]设置参数:gbtree: tree-based models/gblinear: linear models

2).传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。 —对损失函数做了改进(泰勒展开)

主要体现在目标函数的定义。

 

 注:红色箭头指向的l即为损失函数;红色方框为正则项,包括L1、L2;红色圆圈为常数项。xgboost利用泰勒展开三项,做一个近似,我们可以很清晰地看到,最终的目标函数只依赖于每个数据点的在误差函数上的一阶导数和二阶导数。

3).xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性 
—正则化包括了两个部分,都是为了防止过拟合,剪枝是都有的,叶子结点输出L2平滑是新增的。

4).shrinkage —还是为了防止过拟合

shrinkage缩减类似于学习速率,在每一步tree boosting之后增加了一个参数n(权重),通过这种方式来减小每棵树的影响力,给后面的树提供空间去优化模型。

5)column subsampling列(特征)抽样,说是从随机森林那边学习来的,防止过拟合的效果比传统的行抽样还好(行抽样功能也有),并且有利于后面提到的并行化处理算法。

6).split finding algorithms(划分点查找算法):
(1)exact greedy algorithm—贪心算法获取最优切分点 
(2)approximate algorithm— 近似算法,提出了候选分割点概念,先通过直方图算法获得候选分割点的分布情况,然后根据候选分割点将连续的特征信息映射到不同的buckets中,并统计汇总信息。详细见论文3.3节 
(3)Weighted Quantile Sketch—分布式加权直方图算法,论文3.4节 
这里的算法(2)、(3)是为了解决数据无法一次载入内存或者在分布式情况下算法(1)效率低的问题,以下引用的还是wepon大神的总结:

可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。

7).对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。 —稀疏感知算法,论文3.4节,Algorithm 3: Sparsity-aware Split Finding

8).Built-in Cross-Validation(内置交叉验证)

XGBoost allows user to run a cross-validation at each iteration of the boosting process and thus it is easy to get the exact optimum number of boosting iterations in a single run. 
This is unlike GBM where we have to run a grid-search and only a limited values can be tested.

9).continue on Existing Model(接着已有模型学习)

User can start training an XGBoost model from its last iteration of previous run. This can be of significant advantage in certain specific applications. 
GBM implementation of sklearn also has this feature so they are even on this point.

10).High Flexibility(高灵活性)

**XGBoost allow users to define custom optimization objectives and evaluation criteria. 
This adds a whole new dimension to the model and there is no limit to what we can do.**

11).并行化处理 —系统设计模块,块结构设计等

xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。

12)此外xgboost还设计了高速缓存压缩感知算法,这是系统设计模块的效率提升。 
当梯度统计不适合于处理器高速缓存和高速缓存丢失时,会大大减慢切分点查找算法的速度。 

针对 exact greedy algorithm采用缓存感知预取算法 

针对 approximate algorithms选择合适的块大小

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

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

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

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

3.5.LightGBM

1)Histogram算法

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

LightGBM里默认的训练决策树时使用直方图算法,XGBoost里现在也提供了这一选项,不过默认的方法是对特征预排序,直方图算法是一种牺牲了一定的切分准确性而换取训练速度以及节省内存空间消耗的算法

  • 在训练决策树计算切分点的增益时,预排序需要对每个样本的切分位置计算,所以时间复杂度是O(#data)而LightGBM则是计算将样本离散化为直方图后的直方图切割位置的增益即可,时间复杂度为O(#bins),时间效率上大大提高了(初始构造直方图是需要一次O(#data)的时间复杂度,不过这里只涉及到加和操作)
  • 直方图做差进一步提高效率,计算某一节点的叶节点的直方图可以通过将该节点的直方图与另一子节点的直方图做差得到,所以每次分裂只需计算分裂后样本数较少的子节点的直方图然后通过做差的方式获得另一个子节点的直方图,进一步提高效率
  • 节省内存
    • 将连续数据离散化为直方图的形式,对于数据量较小的情形可以使用小型的数据类型来保存训练数据
    • 不必像预排序一样保留额外的对特征值进行预排序的信息
  • 减少了并行训练的通信代价

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

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

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

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

Notes:XGBoost现在两种方式都是支持的

3)直接支持类别特征

对于类别类型特征我们原始的做法是进行独热编码,但是这种做法对于基于树的模型而言不是很好,对于基数较大的类别特征,可能会生成非常不平衡的树并且需要一颗很深的树才能达到较好的准确率;比较好的做法是将类别特征划分为两个子集,直接划分方法众多(2^(k-1)-1),对于回归树而言有一种较高效的方法只需要O(klogk)的时间复杂度,基本思想是对类别按照与目标标签的相关性进行重排序,具体一点是对于保存了类别特征的直方图根据其累计值(sum_gradient/sum_hessian)重排序,在排序好的直方图上选取最佳切分位置

4)网络通信优化

使用collective communication算法替代了point-to-point communication算法提升了效率

5)并行学习优化

特征并行

特征并行是为了将寻找决策树的最佳切分点这一过程并行化

  • 传统做法
    • 对数据列采样,即不同的机器上保留不同的特征子集
    • 各个机器上的worker根据所分配的特征子集寻找到局部的最优切分点(特征、阈值)
    • 互相通信来从局部最佳切分点里得到最佳切分点
    • 拥有最佳切分点的worker执行切分操作,然后将切分结果传送给其他的worker
    • 其他的worker根据接收到的数据来切分数据
    • 传统做法的缺点
      • 计算量太大,并没有提升切分的效率,时间复杂度为O(#data)(因为每个worker持有所有行,需要处理全部的记录),当数据量较大时特征并行并不能提升速度
      • 切分结果的通信代价,大约为O(#data/8)(若一个数据样本为1bit)
  • LightGBM的做法
    让每个机器保留整个完整的数据集(并不是经过列采样的数据),这样就不必在切分后传输切分结果数据,因为每个机器已经持有完整的数据集
    • 各个机器上的worker根据所分配的特征子集寻找到局部的最优切分点(特征、阈值)
    • 互相通信来从局部最佳切分点里得到最佳切分点
    • 执行最优切分操作

Notes:典型的空间换时间,差别就是减少了传输切分结果的步骤,节省了这里的通信消耗

数据并行

上述特征并行的方法并没有根本解决寻找切分点的计算效率问题,当记录数过大时需要考虑数据并行的方法

  • 传统做法
    • 行采样,对数据进行横向切分
    • worker使用分配到的局部数据构建局部的直方图
    • 合并局部直方图得到全局的直方图
    • 对全局直方图寻找最优切分点,然后进行切分
    • 缺点:通信代价过高,若使用point-to-point的通信算法,每个机器的通信代价时间复杂度为O(#machine*#feature*#bin),若使用collective通信算法则通信代价为O(2*#feature*#bin)
  • LightGBM的做法(依然是降低通信代价)
    • 不同于合并所有的局部直方图获得全局的直方图,LightGBM通过Reduce Scatter方法来合并不同worker的无交叉的不同特征的直方图,这样找到该直方图的局部最优切分点,最后同步到全局最优切分点
    • 基于直方图做差的方法,在通信的过程中可以只传输某一叶节点的直方图,而对于其邻居可通过做差的方式得到
    • 通信的时间复杂度为O(0.5*#feature*#bin)

6)并行投票

进一步减小了数据并行中的通信代价,通过两轮的投票来减小特征直方图中的通信消耗

7)早停止XGBoost,LightGBM都支持早停止,不过在细节上略有不同

  • XGBoost和LightGBM里的early_stopping则都是用来控制基学习器的数目的
    • 两者都可以使用多组评价指标,但是不同之处在于XGBoost会根据指标列表中的最后一项指标控制模型的早停止,而LightGBM则会受到所有的评估指标的影响
    • 在使用early stopping控制迭代次数后,模型直接返回的是最后一轮迭代的学习器不一定是最佳学习器,而在做出预测时可以设置参数选择某一轮的学习器作出预测
      • XGBoost里保存了三种状态的学习器,分别是bst.best_score, bst.best_iteration, bst.best_ntree_limit,官方的建议是在做预测时设置为bst.best_ntree_limit,实际使用时感觉bst.best_iteration和 bst.best_ntree_limit的表现上区别不大
      • LightGBM则仅提供了bst.best_iteration这一种方式

你可能感兴趣的:(ML,Interview)