其他内容见上一篇博文。
GBDT(梯度提升决策树):
GBDT属于提升树,所以也是决策树的加法模型:
,其中
表示第m颗树对输入数据 x 的预测值
如何来学习得到这个加法模型?
从前往后一步一步学习,每一步学习得到一个模型即可:
初始化预测值为0,
1)使用训练数据建立第一颗树,输出为初始化值 + 第1个决策树的预测值
第2步t=2,使用新的训练数据建立第二颗树,模型输出为第一颗树的预测值 + 第2个决策树的预测值
....
第m步,使用新的训练数据建立第m颗树,模型输出为上一颗树的预测值 + 第m个决策树的预测值
所以最终的输出是所有树预测值总和。
从第二颗树开始其实拟合的数据都改变了,所以上文说的新的训练数据是什么?
假设训练数据为,一共有n个样本
构建损失函数,这里采用平方差损失函数:
现在要使损失函数不断减小,如何减小损失函数呢,沿着损失函数的负梯度方向不就可以使损失函数不断减小了嘛。
GBDT沿着负梯度方向减小损失函数的方法:
简写损失函数为,对损失函数求导(即求梯度),我们令这个负梯度为
得到下式:
这样我们就得到了n个 (因为对于每一个样本都会有一个
,所以会有n个)称之为残差,作为第m颗树需要进行拟合的数据,可以看出第m颗树的训练数据就是损失函数对预测值的导数在第m-1颗树时的取值。
为什么称 为残差呢:当损失函数为平方差损失函数时,看看求导之后变成:
上式化简之后得:
结果就是实际值与上一个模型的预测值的差,上一个模型就是第m-1个树,所以当损失函数时平方差时,这个
就是残差。但是当损失函数不是平方差时,就不能说
是残差了。所以可以总结为GBDT是用损失函数的一阶导数近似来残差,当损失函数是平方差时,这个近似残差就是真正的残差(实际值与前一个模型的输出值的差值);当损失函数是其他损失函数时,这个近似残差是伪残差。
其实上面红字部分的新的数据指的就是损失函数的一阶导数在前一个模型下的取值,下面对比下原始训练数据和新的训练数据:
合的数据 | ... |
合的数据 | ... | |||||||
... | ... |
可以看出下一个模型拟合的其实是上一个模型的预测残差,只不过这个残差是用损失函数的一阶导近似得到的为残差。
到这儿,每一颗树拟合的是什么已经知道了,下面就得看如何得到这些树,也就是如何建树。上一篇博文已经介绍了如何建树,只不过给出的例子只有一个特征。可以看出GBDT是个串行的过程,唯一能够实现并行的地方也就是建立回归树这个地方,比如可以同时进行多个特征的选择以及分裂点的分裂。
XGBoost:
跟GBDT一样是一个提升的过程:
(a)XGBoost中把模型的复杂度加入到目标函数中作为正则化项,降低了模型的过拟合风险:
这里的损失函数可以是任意的损失函数。因为,所以损失函数等价为:
上式中除了,其他都是已知的,所以优化目标函数就等于求
。
(b)XGBoost采用二阶泰勒展开式逼近目标函数。二阶泰勒展开式为:
把(1)式中的看成是
,
看成是
按泰勒二阶展开得:
其中为损失函数的一阶导数,
为损失函数的二阶导数,而且这个损失函数是第 t-1 个模型的损失。如果此时的损失函数是平方损失即
则
因为在第 t 步建树时,前一颗树的预测结果是已知的,即是一个常数,常数项对目标函数的优化不会产生影响,所以去掉(3)式中的常数项之后为:
由(4)式可知,只要知道第 t-1 个模型对应的 和
,就能求得第 t 个模型
。
所以对于每一步求得的模型,求出这个模型损失函数对应的一阶导和二阶导,带入目标函数并优化目标函数,就能求得下一个模型。
如何用决策树表示目标函数?
由于Boosting的基模型是由决策树实现,则一颗生成好的决策树,其结构是确定的,也就是说树的叶子结点是确定的。假设这棵树 个叶子节点,而每个叶子节点对应的值
,
即
个叶子对应就会有
个权值。每一片叶子结点中样本的预测值都会是一样的,在分类问题中,是某一类,在回归问题中,是某一个值(在用最小二乘法构建回归树时,这个值就是这个叶节点包含的所有样本值的平均值,但XGBoost中这个值其实就是我们要预测的值)那么肯定存在这样一个函数
,
是样本空间,一共有
个样本,
为映射函数,能够将
中的每个样本映射到各自对应的叶子结点上,每一个叶子节点都有自己的权值
,即
如果决策树的复杂度可以由正则项来定义,即
即决策树模型的复杂度由生成的树的叶子节点数量和叶子节点对应的值向量的L2范数决定,就是叶子数量的惩罚因子。
假设 为第
个叶子节点的样本集合,
,则(5),(6)式带入(4)式得:
此时我们就把所有的样本都映射到各自对应的叶节点了,每一个叶节点都含有一个或多个样本,因此才有了 和
这两项。
定义 ,
则等式(7)可写为:
如果树的结构是固定的,那么说明树的叶子节点是固定的,那么把样本映射到这些节点的映射函数 也是固定的,这说明一旦树的结构固定,
就是固定的,进而我们就能知道每个节点上对应有多少样本了,所以
和
也是固定的, 权值
使我们想要预测的值,所以此时对(8)式求一阶导,令导数为 0 可求得叶子节点
对应的值为(星号代表最优解):
将(9)式带入(8)式中得
上面的(10)式即为最终的目标函数,如何优化这个目标函数?
最笨的方法就是对于每一颗决策树,枚举所有可能的树结构,每一颗树每一颗树的验证:
a、首先枚举所有可能的树结构,即 ;
b、计算每种树结构下的目标函数值,即等式(10)的值;
c、取目标函数最小(大)值为最佳的树结构,根据等式(9)求得每个叶子节点的 取值,即对应样本的预测值。
但上面的方法肯定是不可行的,因为树的结构千千万,所以一般用贪心策略来优化:
a、从深度为0的树开始,对每个叶节点枚举所有的可用特征
b、 针对每个特征,把属于该节点的训练样本根据该特征值升序排列,通过线性扫描的方式来决定该特征的最佳分裂点,并记录该特征的最大收益(采用最佳分裂点时的收益)
c、 选择收益最大的特征作为分裂特征,用该特征的最佳分裂点作为分裂位置,把该节点生长出左右两个新的叶节点,并为每个新节点关联对应的样本集
d、回到第1步,递归执行到满足特定条件为止
那么如何计算上面的收益呢?
仍然紧扣目标函数就可以了。假设我们在某一节点上二分裂成两个节点,分别是左(L)右(R),则分列前的目标函数是 ,分裂后则是
,则对于来目标函数说,分裂后的收益是
,即:
所以GBDT的算法可以总结为:
a、算法在拟合的每一步都新生成一颗决策树;
b、在拟合这棵树之前,需要计算损失函数在每个样本上的一阶导和二阶导,即 和
;
c、通过上面的贪心策略生成一颗树,计算每个叶子结点的的 和
,利用等式6计算预测值
;
d、把新生成的决策树 加入
,其中
为学习率,主要为了抑制模型的过拟合
与GBDT相比,XGBoost的优点:
1. 显示的把树模型复杂度作为正则项加到优化目标中(上文中的(a))。
2. 公式推导中用到了二阶导数,用了二阶泰勒展开(上文中的(b))。
3. 实现了分裂点寻找近似算法。
4. 利用了特征的稀疏性。
5. 在建立回归树的时候,训练数据事先排序并且以 block 形式存储,有利于并行计算。
6. 基于分布式通信框架 rabit,可以运行在 MPI 和 yarn 上。(最新已经不基于 rabit 了)
7. 实现做了面向体系结构的优化,针对 cache 和内存做了性能优化
参考资料:
3. 李航《统计学习方法》