本来觉得xgboost已经弄懂了,但听了AI Lab陈博士的讲座之后,又有了更深入的认知,本文将详细解释一些细节,帮助大家理解。顺便表示对陈博的膜拜,讲的太清楚了👍。
首先呢,Xgboost是一个究极进化体,它的祖先其实是棵树。我们来看下它的进化史:
本文为了让你真正懂Xgboost,将追根溯源,从他的祖先开始一层一层的讲解Xgboost是怎么进化来的😝
But!本文默认你是懂DecisionTree的,不清楚决策树是啥的请自行百度😌
But,but!还是要提一嘴决策树的表示形态,这个很重要,因为Xgboost的进化与决策树的形态变化密不可分。
决策树有哪些表示形态啊?
- 树形结构
- if-else判断结构
- 图像区域划分
- 。。。
前三种是我们常见的形式了,点点点我们后面再说😝
好!我们正式开始研究Xgboost的进化史,首先是Boosting。
Boosting
提升方法(boosting)就是集成方法中的一员(另一员是Bagging,其中差别不在本文讨论范围内,请自行百度),它将所有臭皮匠加起来,最后赛过一个诸葛亮。所以boosting本质是一个加法模型。
加法模型:
其中,为基函数,为基函数的参数,为基函数的系数。基函数是什么?只要保证函数可加性就可以,比如决策树。
有了这个加法模型,那么我们的目标就是:在给定训练数据及损失函数的条件下,学习加法模型成为经验风险极小化问题:
稍微解释下经验风险极小化是什么鬼,其实就是损失函数。还有一个结构风险极小化,指的是正则化项。
式(1.2)这个模型看上去有点麻烦,但我们可以从前向后,每一步只学习一个基函数及其系数,逐步逼近优化目标函数式(1.2),这样就可以简化优化复杂度。具体地,每步只需优化如下损失函数:
于是有了前向分布算法:
算法1:前向分步算法
输入:训练数据集; 损失函数;基函数集合;
输出:加法模型
(1)初始化
(2)对
(a)极小化损失函数
得到参数,
(b)更新
(3)得到加法模型
前向分步算法将同时求解从到所有参数的优化问题简化为逐次求解各个的优化问题。
以上就是Boosting
好啦,接下来要开始进化了:boosting->boosting+decision tree
BDT
我们把boosting的基函数设置成决策树,那么boosting就变成了BDT,提升决策树。
提升决策树模型可以表示为决策树的加法模型:
其中,表示决策树;为决策树的参数;为树的个数。
还记得前文说的决策树形式吗?这下又多了一种表示形式。下面是对这种表示形式的解释:
已知训练数据集,,为输入空间,,为输出空间。如果将输入空间划分为个互不相交的区域,并且在每个区域上确定输出的常量,那么决策树可表示为
其中,参数表示决策树的区域划分和各区域上的常量值。是决策树的复杂度即叶子结点个数。
我们回到BDT上来,提升决策树使用以下前向分步算法:
在前向分步算法的第步,给定当前模型,需要求解
得到,即第棵树的参数。
当采用平方误差损失函数时,
其损失变为
其中,
是当前模型拟合数据的残差(residual)。对回归问题的提升决策树,只需要简单地拟合当前模型的残差。
算法2:回归问题的提升决策树算法
输入:训练数据集;
输出:提升决策树
(1)初始化
(2)对
(a)按照式(2.5)计算残差
(b)拟合残差学习一个回归树,得到
(c)更新
(3)得到回归提升决策树
简单总结下,BDT其实就是把boosting的基函数设定成了决策树而已。
形象的说说Boosting和BDT:
诸葛亮的智力值为100,有个智力值为40的臭皮匠A想打败他。两者差了60点智力值,实力太悬殊,于是A又找了个智力值为30的臭皮匠B,这下A、B加起来智力值就是70了,只差诸葛亮30点了。于是他们又找到了智力值为20的臭皮匠C。。。好吧,你们慢慢找,总有一天能够打败诸葛亮。
好啦,又要进化了:BDT->GBDT
GBDT
前文的加法模型中,新的基模型都是在拟合真实残差,既
如何加速这个拟合过程呢?我们知道负导数是函数下降最快的方向,那么对损失函数求偏导就能得到极值。
GBDT使用损失函数的负梯度在当前模型的值
作为回归问题提升决策树算法中残差的近似值,拟合一个回归树。
算法3: 梯度提升算法
输入:训练数据集; 损失函数
输出:梯度提升决策树
(1)初始化
(2)对
(a)对,计算
(b)对拟合一个回归树,得到第棵树的叶结点区域
(c)对,计算
(d)更新
(3)得到回归梯度提升决策树
这里和BDT有几处不同:
- 第一步的基分类器不再是粗暴的设置为,而是拟合了一个当前最优解c。
- 拟合的残差是损失函数的负梯度。
我们观察式(3.1),可以发现它是一个二元函数。与常见的逻辑回归、svm中的梯度下降不太一样的是,前者求得是参数,而这里我们直接把作为变量求解。 - 决策树的表示形式又不一样了,这次变成了 ,这样表示的意思是以树的叶节点作为考量,样本放入树模型后,被划分到哪个叶节点?而该叶节点又将输出何值?
终于进化到究极体了!GBDT->Xgboost
Xgboost
在Xgboost里,我们又双叒叕重新改变了决策树的表示形式:
训练数据集,其中,则决策树表示为:
其中,,为决策树叶子节点数。
这里是第一个重点,需要解释下。
首先我们对决策树来个功能上的认知:给树模型一个样本,树模型就输出一个预测值,对吧?这个预测值实际上就是叶子节点的分数,也就是说一个叶子节点对应一个预测值。
那么这里的函数表示的是叶子节点的序号,的意思就是样本给到函数之后,函数将样本分配到某个叶子节点上,并输出这个叶子节点的序号。
而表示的是叶子节点的分数,用下标(就是)的形式来表示是哪一个叶子节点的分数。
理解了究极体的树模型表示形式,接下来我们来看看Xgboost模型:
其中,为第棵决策树。
这种模型跟前文的BDT、GBDT看起来并没有什么区别,但是基于全新的树模型表示方式,我们可以把正则化项加到目标函数中去:
其中,。
这样第轮目标函数就是这样:
这里是第二个重点,在目标函数里加入了GBDT没有考虑的正则项,降低了模型过拟合风险。
接下来是第三个重点,GBDT采用损失函数负梯度(一阶求导)作为残差,Xgboost嫌他慢了,采用二阶求导。为了让损失函数具有一般性,这里要应用泰勒展开公式,什么是泰勒公式自行百度😈。
这里的损失函数是二元的,我们只对做变换。
第轮目标函数在处的二阶泰勒展开得到:
其中,。
第轮目标函数的二阶泰勒展开并移除关于常数项,得到:
这里稍微解释下,我们的目标是求出让损失函数最小的,而是在上一轮已经求出的已知项,因此在这里是常数项。
式(4.6)左边是用样本点的标号来遍历,而右边是用叶子节点的标号遍历,能不能统一一下呢?可以啊,就全部以叶子标号来遍历好了:
定义叶结点上的样本的下标集合,则目标函数可表示为按叶结点累加的形式
这一步是第四个重点,一个很有技巧的操作,也是很多人看不明白的地方,我们来理解下:
我们的本来目的是要遍历所有的样本点对吧?而样本点又无重复的分布在所有叶子节点上对吧?那我们遍历所有的叶子节点不也能遍历到所有的样本吗?
想像上课时老师点名,可以按学号从1到N来点名,也可以按小组序号来点名啊,那么这里的学号就对应这样本点标号,小组序号就对应着叶子节点标号。
有了上面的解释,相信大家应该能理解式(4.7)是怎么得来的了吧?😝
好啦,最困难的一步理解之后,就是喜闻乐见的求导了:
由于
可令
得到每个叶结点的最优分数为
至此,我们求出了每个叶子节点的分数,但树长什么样还没有完全得出。
最开始有个假设被我们忽略了,就是叶子节点个数,这个条件一直被我们当做常量来计算了,也就是说我们已经人为设定好了树的形状就是个叶子的树,然后通过各种神操作得到了每个叶子的输出值。
那么接下来的问题是,哪些样本放在1号叶子里,哪些放2号叶子里呢?
我们回想下决策树是怎么做的?依照信息增益、信息增益比、gini系数对数据集进行分裂对吧?那我们是不是可以创造一种新的分裂方法呢?
代入每个叶结点的最优分数,得到最优化目标函数值:
我们发现这个目标函数仅与叶节点分数的表达式(4.8)非常像呀。是不是可以认为,叶节点的输出衡量了最终的模型性能?
ok,那我们假设和分别为分裂后左右结点的实例集,令,则分裂后损失减少量由下式得出:
式(4.10)表示的是数据集分裂前后,目标函数究竟提升了多少,这种操作是不是起到了跟gini系数一样的作用呢?显然答案是肯定的呀。
那我们就可以用(4.10)贪婪的遍历数据集的每一个特征,每一个特征下的数据类别:
算法4 分裂查找的精确贪婪算法
输入:当前结点实例集;特征维度
输出:根据最大分值分裂
(1)
(2),
(3)for to do
(3.1),
(3.2)for in sorted(, by ) do
(3.2.1),
(3.2.2),
(3.2.3)
(3.3)end
(4)end
至此,Xgboost打完收功😽
附送陈天奇老师的图片
参考
七月在线课程
《XGBoost A Scalable Tree Boosting System》-陈天奇
《统计学习方法》-李航