boosting算法原理以及GBDT与Xgboost的比较

在融合算法家族中,不同于bagging和Averaging算法主要降低子模型的方差,boosting算法主要用来降低偏差(保持方差不变)。GBDT和Xgboost算法是boosting算法中应用比较广泛的两种算法,我们下面就来介绍一下这两种算法。

前向分步算法

GBDT和Xgboost的基本模型都可以归类为前向分布算法,为了后续便于理解,我们首先来看下前向分布算法的机制。

我们知道,融合模型的基本模型 --- 加法模型:                                                           

                                                                   f_m(x) = \sum_{m=1}^M \alpha_mh_m(x)

其中\alpha为子模型的权重,h为子模型。已知损失函数L(y, f(x)),择我们的问题可以表示为:                                               

                                                              \underset{\alpha,h}{min} \sum_{i =1 }^NL(y_i, \sum_{m=1}^M\alpha_mh_m(x))

该问题的求解辅助度是非常高的,不具有现实意义,为了简化计算,我们采用一种贪心的策略。即在计算每个子模型时,只考虑优化该子模型的参数与权重,来使得当前融合模型的损失函数最小:                                                            

                                                                   \underset{\alpha,h}{min} \sum_{i=1}^N L (y_i, \alpha h(x))

我们可以重复以上步骤M次,得到的依然是M个子模型组成的融合模型;不同的是这不一定是一个全局最优的模型,只是计算过程中的每一步都是最优的。

GBDT回归算法原理

在前向分步算法中,\underset{\alpha,h}{min} \sum_{i=1}^N L (y_i, \alpha h(x))这个问题具体该怎么解决呢?假设在前向算法分步算法中,我们已经获得了前 m - 1 个子模型,现在要计算第m个子模型,当前损失函数为:                                                        

                                                              Loss = \sum_{i=1}^N[L(y_i, f_{m-1}(x_i))]

我们此时可以把f看做为Loss函数的自变量,那么f只要以其负梯度的方向移动就可以使损失函数变小:

                                                     (\bigtriangledown L)_i = \frac{\partial L(y_i,f_{m-1}(x_i))}{\partial f_{m-1}(x_i)}, i = 1,2,...,N

如果我们以MSE(均方误差)作为损失函数,即 L(y_i, f(x_i)) = \frac{1}{2} (f(x_i) - y_i)^2,则有:

                                                                  (\bigtriangledown L)_i \\ = f_{m-1}(x_i) - y_i \\ = r_{im}

也就是loss关于f_m-1的梯度就是残差r_{im}。所以,要计算第m个子模型,我们只需要用子模型h_m去拟合残差就可以了,而我们从算法流程可以看到,若学习器生成的过程,可以等效为损失函数梯度下降的过程。

以上就是GBDT回归的基本原理,但是还有一个问题,hm拟合残差需要用什么样的模型呢?我们知道boosting为减少偏差的融合算法,所以我们一般会选择偏差较大而方差较小的弱模型,在GBDT中,我们采用了以CART树为基础的浅层决策树。

Xgboost算法原理

前面介绍的GBDT利用Loss函数的一阶导数信息来优化(梯度下降法是基于损失函数的一阶导数),一个很自然的想法是利用Loss函数的二阶导数来优化。Xgboost就是这样的一种算法,对于Loss函数的二阶近似可以表示为:

                                      L(y_i, f_{m-1}(x_i) + h_m(x_i)) \approx L(y_i, f_{m-1}(x_i)) + G_ih_m + \frac{1}{2}H_i h_m^2

这里G_i = \frac{\partial L}{\partial f_{m-1}}H_i = \frac{\partial^2L}{\partial f_{m-1}^2}。为了求得最优的hm,上式对hm求导:

                                                           \frac{\partial L(y_i, f_{m-1}(x_i) + h_m(x_i))}{\partial h_m} \approx G_i + H_i h_m

并令其为0,可以获得:

                                                                                 h_m = - \frac{G_i}{H_i}

加入正则项的XGboost

当然,Xgboost内部为了防止过拟合,在Loss函数的二阶近似的基础上加入了正则项,以防止过拟合发生:

                                    

其中。这里T是hm作为CART树的叶节点数目,同时hm可以表示为:

                                                      

这里q(x)表示x属于哪个树叶,w_j表示树叶j所代表的值(因为是回归树,样本落入了特定树叶就有了固定的数值)。我们令  为落在叶子  上的样本的集合。则有:

                                    boosting算法原理以及GBDT与Xgboost的比较_第1张图片

注意到,如果树结构已经确定了,那么上式的变量就只有w而已,且上式为w的一个二次方程,所以我们可以求得w的最优值:

                                                                  

如此,损失函数表示为:

                                                                

XGboost树结构的确定

那么问题来了,没有固定的树结构无法求取w和loss,树结构如何求取呢?遍历所有树结构显然不可行,一般来说我们确定树结构都要使用贪心策略,而贪心策略的基础就是上面我们提到的由树结构可以完全确定Loss_q,不断地对节点进行分叉,直到整体的Loss_q不再降低。这里树结构的确定可以分为两个问题:(1) 是否需要分叉(2)怎么选择分叉点 。对于第一个问题,我们只需要查看Loss_q前后的大小即可。对于第二个问题,我们知道可以根据式

                                         boosting算法原理以及GBDT与Xgboost的比较_第2张图片

知道,只需要知道节点内部样本的h值,就可以很方便的计算出loss;一个很自然的想法是用样本的h代替特征值本身来做切分点,因为对h做百分位切分会使得计算简单很多。当然用h来代替原始特征值是有依据的,它可以代表样本在loss中的重要程度(见[1])。

XGboost并行化处理

XGboost并没有在树这个层面上做并行处理,这是boosting算法本身的性质决定的。但是在单一的树生长过程中,我们是可以使用并行化的。在树的生长过程中,最耗时的就是特征值或者h值排序,XGboost将这些排序信息统一存储在了我们称之为block的结构中(分布式系统则是多个block,每个block存储一定量的行的数据),这样可以避免重复排序工作。

 

参考文献:

[1] XGBoost: A Scalable Tree Boosting System

[2] Gradient Boosting梯度提升-GBDT与XGBoost解析及应用

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(机器学习)