最近翻阅了一些介绍GBDT与XGBOOST的原理的博客和论文,网上以及有很多介绍它们的文章了,但博主还是想记录一下它们的原理以及自己的理解,即是方便自己翻阅复习也是希望大佬们提提建议。
GBDT
GBDT即是Gradient Boosting Tree(梯度提升树),很容易联想到的是这里的梯度提升与Gradient Descend(梯度下降)之间的关系。不去理解这个“Boosting ”,单通过翻译的“提升”,想到的一定是以为提升树是在Gradient Descend更新系数时将“-”号改为“+”的提升(我就是这么想的),然而并不是。这里就需要引入“boosting”的概念。
Boosting
Boosting 算法是一种加法模型(additive training),在《机器学习实战》书中讲了一种Boosting叫adboosting,就是用了同样的方法。对一个数据集,建立M个模型,而这些模型非常简单,称为“弱分类器”。
Boosting贯彻“知错就改”的思想,每个分类器上每次分类都将上一次分错的数据权重提高一点再进行分类,这样最终得到的分类器在测试数据与训练数据上都可以得到比较好的成绩。于是越往后执行,训练出的模型就越会在意那些容易分错(权重高)的点。
最终得到M个模型,将这些模型按照权重加起来就得到了最终的模型。
Gradient Boosting
Gradient Boosting是一种Boosting的方法,它主要的思想是,每一次建立模型是在之前建立模型损失函数的梯度下降方向(这里不解释梯度下降的问题,主要介绍Gradient Boosting),朝着梯度下降的方法表明模型在越来越好。Gradient Boosting是怎样的,可以用一个简单的例子来描述:
这里利用后三列的特征来拟合Age这个目标,这里用LikesGardening特征来建立回归树模型,
得到的结果如下图:
图中Age是我们要预测的目标,Tree1为我们预测得到的第一组预测值,Tree1 Residual 是Predictions与真实值的差,利用下一棵回归树以PlaysVideoGames 为特征对第一棵树的Tree1 Residual进行拟合,
并将Tree2得到的预测结果与Tree1得到的预测结果进行相加,得到结果:
也由最后的SEE结果可以看出Combine的结果比Tree1的结果要好,之后再用Tree3对Tree2的残差进行拟合,以此类推。
这样其实就是Gradient Boosting的思想了。梳理下上面的思路,可以得到这样的模型:
于是可以得到最终函数:
这里g(x)为回归树,最终函数等于每次迭代的增量的累加和。
Gradient Boosting Tree
以本人的理解方式用比较长的篇幅介绍了Gradient Boosting,再回到GBDT,GBDT的模型 F 定义为加法模型:
其中,x为输入样本,h为分类回归树,w是分类回归树的参数,a是每棵树的权重。这里h也就是Gradient Boosting里的g(x)。
通过最小化损失函数求解最优模型:
于是在每次更新Gradient Boosting Tree 模型时,需要重新计算(a,w),下面是Gradient Boosting Tree 算法的原理:
看上去貌似很麻烦但仔细观察下,其实万变也不离上面的最小化损失函数和梯度下降,主要是计算w*和p*,如果不理解公式应该再回味下之前的公式。
XGBOOST
在理解XGBOOST时,需要借助GBDT的理解,并对两者进行对比,这样的方法更有效率。
XGBOOST与GBDT第一个区别:牛顿法
GBDT 在函数空间中利用梯度下降法进行优化
XGBoost 在函数空间中用牛顿法进行优化
牛顿法看似比较陌生,在GBDT中梯度下降只是一阶泰勒公式展开,牛顿法就是二阶的泰勒公式展开,在参数空间中:
为了简化分析过程,假设参数是标量(即 只有一维),则可将一阶和二阶导数分别记为 g 和 h:
博客上写公式还不会,直接截图,牛顿法比较好理解。
在函数空间中的牛顿法boosting与梯度boosting思想一样,只是f(x)函数变了:
之后还是最终函数等于每次迭代的增量的累加和:
XGBOOST的目标函数
样本进行预测:
这里k是树的数量,是全部的回归树集合,而f就是其中一个回归树。
接下来看XGBOOST的目标函数,
误差函数可以是square loss,logloss等,正则项可以是L1正则,L2正则等。
将y的预测值带入:
模型要学习的只有第t棵树
对进行二阶泰勒展开,
其中:
去掉其中的常数项:
接下来讲公式的后面那个东西:正则项。
XGBOOST与GBDT的第二个区别:正则项
上面对GBDT中的分析可以知道它是没有正则项的,在XGBOOST中加入了正则项,但是正则项也不是XGBOOST首先加入的,并不是开创了先河。
正则项有什么作用呢?简单来说就是可以是的模型不容易过拟合。正则项对每棵回归树的复杂度进行了惩罚,而复杂度可以用树的深度,内部节点个数,叶子节点个数(T),叶节点分数(w)等来衡量。
首先对f(x)定义为:
这个定义也要代入在整个目标函数中。在XGBOOST中,复杂度可以用:
来表示,对叶子节点个数进行惩罚,相当于在训练过程中做了剪枝。
简化目标函数
带入正则项和f(x):
其中第一个约等于式子里:
将它们统一起来,定义每个叶节点j上的样本集合:
就成了后面的式子。定义:
可以得到:
如果确定了树的结构(即q(x)确定),为了使目标函数最小,可以令其导数为0,解得每个叶节点的最优预测分数为:
带入目标函数,得到最小损失函数,也就是我们的目标函数为:
XGBOOST的打分函数
标红部分衡量了每个叶子节点对总体损失的的贡献,我们希望损失越小越好,则标红部分的值越大越好。
Gain函数:
这里是摘自其他博客的对这个公式的解释,比较形象:
这个公式可以分解为:1)新左叶上的得分2)新右叶上的得分3)原叶上的得分4)附加叶上的正则化
所以当对一个叶节点分割时,计算所有候选(feature,value)对应的gain,选取gain最大的进行分割。
XGBOOST与GBDT的其他区别
- 对每颗子树增加一个参数,使得每颗子树的权重降低,防止过拟合,增加这个参数叫shrinkage方法。对特征进行降采样,灵感来源于随机森林,除了能降低计算量外,还能防止过拟合。
- 增加处理缺失值的方案(通过枚举所有缺失值在当前节点是进入左子树,还是进入右子树更优来决定一个处理缺失值默认的方向)。
- 对每个特征进行分块(block)并排序,使得在寻找最佳分裂点的时候能够并行化计算.这个结构加速了split finding的过程,只需要在建树前排序一次,后面节点分裂时直接根据索引得到梯度信息。这是xgboost比一般GBDT更快的一个重要原因。
- out-of-core 通过将block压缩(block compressoin)并存储到硬盘上,并且通过将block分区到多个硬盘上(block Sharding)实现了更大的IO 读写速度,因此,因为加入了硬盘存储block读写的部分不仅仅使得xgboost处理大数据量的能力有所提升,并且通过提高IO的吞吐量使得xgboost相比一般实利用这种技术实现大数据计算的框架更快。
参考文献
- wepon.me/files/gbdt.pdf
- http://www.52cs.org/?p=429
- http://xgboost.readthedocs.io/en/latest/model.html
- https://www.cnblogs.com/LeftNotEasy/archive/2011/01/02/machine-learning-boosting-and-gradient-boosting.html
- http://blog.kaggle.com/2017/01/23/a-kaggle-master-explains-gradient-boosting/
- https://www.zhihu.com/question/41354392