Java教程

推荐算法 之 XGBoost

本文主要是介绍推荐算法 之 XGBoost,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

        XGBoost与gbdt比较大的不同就是目标函数的定义,但这俩在策略上是类似的,都是聚焦残差,GBDT旨在通过不断加入新的树最快速度降低残差,而XGBoost则可以人为定义损失函数(可以是最小平方差、logistic loss function、hinge loss function或者人为定义的loss function),只需要知道该loss function对参数的一阶、二阶导数便可以进行boosting,其进一步增大了模型的泛化能力,其贪婪法寻找添加树的结构以及loss function中的损失函数与正则项等一系列策略也使得XGBoost预测更准确。

        在之前提到的GBDT+LR的文章中,我们提到了加法模型(文章在这),同理XGBoost也是一个加法模型,由K个基模型组成的一个加法模型:(加法是指树的结果都累加在一起)

        \hat{y_i} = \sum^K_{t=1}f_t(x_i)     

        其中 f_k 就是第k个模型,\hat{y_i} 是第i个样本的预测值。于是我们就可以定义损失函数了:

Loss=\sum^N_{i=1}l(y_i,\hat{y_i}) = \sum^N_{i=1}l(y_i,\sum^K_{t=1}f_t(x_i))

        然后一个重点来了,GBDT呀,AdaBoost呀都是建树的时候猛地造,后面再去做剪枝,到那时XGBoost这里就很聪明了,在建树阶段就加入了正则项来防止过拟合。

        于是,Lossfunction就有新的表现形式了:

obj = \sum^N_{i=1}l(\hat{y_i},y_i)+\sum^K_{t=1}\Omega (f_t)

        之前我们就提到了Boosting也是一个加法模型(前向加法),所以我们可以根据对于第i个样本的第t-1棵树来写出第t棵树的预测:

\hat{y_i}^t = \hat{y_i}^{t-1} + f_t(x_i)

        这就立马套路起来了,开始改写!

obj^t = \sum^N_{i=1}l(y_i, \hat{y_i}^{t-1}+f_t(x_i))+\sum^K_{t=1}\Omega(f_t)

        然后我们观察这个式子:里面就只有一个f_t(x_i) 是未知的,其余都是已知的,但这个f_t恰恰是最难求的,于是我们用比较迂回的方法去求解这个复杂的函数。

        我们采用泰勒展开去逼近这个函数,用二阶泰勒展开:

        然后把\hat{y_{i}}^{t-1} 视为x,把f_t(x_i) 视为delta x,于是按照二阶展开的模版继续改写!

        obj^t = \sum^N_{i=1}\left [ l(y_i,\hat{y_i}^{t-1})+loss'f_t(x_i)+\frac{1}{2}loss''f^2_t(x_i) \right ] +\sum^K_{t=1}\Omega(f_i)

        因为我们定义的是l(y_i,\hat{y_i}^{t-1}) 是 f(x),所以后面都是loss的一阶导和二阶导,然后定义了x是\hat{y_i}^{t-1},所以我们是对这个\hat{y_i}^{t-1} 进行求导的。

        由于我们现在已经在第t棵树了,就是第t步了,所以我们的loss里面的f(x)其实是一个常数项,于是我们可以继续化简:

        obj^t = \sum^N_{i=1}\left [loss'f_t(x_i)+\frac{1}{2}loss''f^2_t(x_i) \right ] +\sum^K_{t=1}\Omega(f_i)

        于是我们只需要求出每一步(对于当前样本的每一棵树)模型的一阶导和二阶导的值,就可以得到每一步的f(x)了,然后用加法模型,就可以得到一个整体模型了。

        但是但是!其实还可以继续化简的!

        我们继续沿用上面提到的 obj^t,然后将决策树定义为这个:

f_t(x_i) = w_{q(x_i)}

        q(x_i) 就代表了这样本会被分到哪一个叶子上,w就是这个叶子的取值,所以w_{q(x)} 就是代表这个样本的分到哪个叶子结点对应这个叶子结点的取值了。

        而决策树的复杂度在XGBoost里面是这么定义的:

\Omega(f_t) = \gamma T + \frac{1}{2}\lambda\sum^T_{j=1}w_j^2

        其中,T代表了树的叶子数,其实可以自己转化成深度的意思,因为XGBoost是一个二叉树,所以值越小就代表着越不容易过拟合。再看隔壁的,w_j就代表每一个叶子的权重了(下面提到T的求和都是叶子数目了)

        所以一句话总的来说就是XGBoost的正则项是叶子数和叶子对应权重 两个东西去衡量的

        于是,再代入!  (这里的 g_i 就是loss‘ (一阶导),h_i 就是loss'' (二阶导))

         第二步到第三步其实就是换了个角度来看这个公公式了,第二个还是对样本x求和求loss,第三步就马上是对叶子数T求和,怎么理解呢?第一第二步的求和应该是大N才对。

        因为不管怎么输入是什么,最终都会落在这么多棵树,总共T个叶子结点上的,所以我们干脆就让他先落下去,然后直接对叶子结点求loss。

        因为一个叶子不是对应一个样本,一个叶子可能会对应多个样本,所以有了这两项:

\sum_{i \in I_j}loss' 和. \sum_{i \in I_j} loss''

        这两个东西就是代表每一个叶子结点都计算里面 样本的 loss‘ 和 loss'',然后求和加起来乘上对应叶子结点的输出。

        然后还有按照大佬的想法,再继续简化:令:

G_j = \sum_{i \in I_j } loss' 和 H_j = \sum_{i \in I_j}loss''

        于是整个式子就变成了:

obj^t = \sum^T_{j=1}[G_jw_j+\frac{1}{2}(H_j+\lambda)w^2_j] + \gamma T

        我们继续看刚新换上了G和H,欢了这么多次这个不要忘记了究竟是什么东西了那是关于第t-1棵的所有叶子结点的的loss一阶导和二阶导,所以其实相对于第t棵的数目来说,这个G和H其实是一个常数了(因为已经发生了),所以剩下一个 w_j 不知道了。

        于是我们对它进行求导等于0,得出每一个叶子结点对应的权值:

w^*_j = -\frac{G_j}{H_j+\lambda}

        再代入到obj里面:

obj^t = -\frac{1}{2} \sum^T_{j=1}\frac{G_j^2}{H_j+\lambda}+\gamma T

        如此一来,在重新审阅了后面的正则化后,我们求得新的更新函数,还是一个道理,求每个叶子结点每个样本的一阶导数和二阶导数后,根据上面的公式就可以得到第t棵树是怎么样的了。

        下面就是要说怎么选择树了,因为树的划分有很多情况一个非常关键的问题是如何找到叶子的节点的最优切分点,,所以我们要万里挑一!Xgboost 支持两种分裂节点的方法——贪心算法和近似算法。

        贪心算法:

        贪心算法的计算收益过程:

        其实这个和GBDT里面的计算几乎一样的,只是衡量收益的公式发生变化了,例如回归树里面用三个min的衡量。并且在做划分的时候,公式里面就已经带了 \gamma了,这个说明了切分的时候就考虑树的复杂度,防止过拟合

        但是这计算,当特征数目多起来,计算就会非常缓慢,所以有一个新的算法去做这个划分,叫做近似算法 Weight Quantile Sketch,这一部分强哥写得非常棒!文章在这,直接看贡献度那个图。

        0. 要明白算法的核心思想是想尽可能分桶的时候都做到两边的loss分布均匀。

        1. 要明白贡献度h_i 是什么:其实就是二阶导数(loss'' )

        2. 要明白这个贡献度为啥能称为降低loss的贡献度:要将二阶泰勒展开后的式子进行变形,变形出带有残差的影子,就可以解释XGBoost其实也是在拟合残差,然后看系数项就是代表了每个样本对拟合残差的贡献程度了

        3. 要明白分箱是怎么分的:ϵ控制每个桶中样本贡献度比例的大小,就是贡献度的分位点。

        4. 所以XGBoost在选择切分点上,是对候选点做处理,而不是对所有可能点都做计算,这就会非常快了,比GBDT快了不少。而且因为特征之间是独立的,所以可以直接做并行计算

       

        再往下就是树的合成了,XGBoost这里有点特殊,就是他不是一股脑地一直做树的累加,而是得先让每棵树都削弱一下,这里就提到一个叫 shrinkage的东西。

在这里插入图片描述

         上图中会发现每一次迭代得到的新模型前面有个η 这个是让树的叶子节点权重乘以这个系数), 这个叫做收缩率,这个东西加入的目的是削弱每棵树的作用,让后面有更大的学习空间,有助于防止过拟合(注意这个可不像Adaboost那里每个分类器前面的权重值了,那一个是分类器的预测结果乘以权重作为了当前分类器在最后预测结果中的结果。而这里是权重要乘到树的叶子节点上去了,还是有区别的)。

        也就是我不完全信任每一个残差树,每棵树只学到了模型的一部分,希望通过更多棵树的累加来来弥补,这样让这个让学习过程更平滑,而不会出现陡变。

        这个和正则化防止过拟合的原理不一样,这里是削弱模型的作用,而前面正则化是控制模型本身的复杂度, 而这里是削弱每棵树的作用,都是防止过拟合,但是原理不一样。

        所以总的来说,XGBoost是一个很多基(弱)分类器的集成,核心思想是减少残差,通过泰勒二阶展开去逼近函数,来引入一阶二阶导数。从而使得XGBoost可以更加精准地知道哪个样本是对降低loss是有重要作用的,接着把目标函数转换成叶子结点的遍历形式,得到最终的目标函数。在建树的过程中,用到贪心的策略并进行了优化。然后一棵一棵树在收缩率的影响下做累加,从而达到最终的模型。

 

                

                

 

这篇关于推荐算法 之 XGBoost的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!