理解Kaggle神器——Xgboost

题记

  • Xgboost作为集成模型的一个代表,一直以优异的性能著称,很多Kaggle比赛的获奖者都非常偏爱使用这个模型。然而,这个模型背后的原理,却比一般的集成模型更为复杂和难以理解。 这次对Xgboost的推导过程和重点内容做一些整理,尽量通俗易懂,希望帮助大家更好地理解Xgboost。

学习残差

  • Xgboost是一个由多个基模型构成的集成模型,每一个模型都在学习上一个模型的残差。因此,每多一棵树我们就离真实值更近了一步。最后,将所有模型的预测结果求和,作为最终的预测结果。
    在这里插入图片描述
  • 以上图为例,这次我们只看小boy,假设小boy的真实值为3,tree1的学习结果为2,则第二颗树是学习目标是真实值和预测值的差值(3-2)=1,而tree2学到了0.9。则最终对boy的预测值为2+0.9 = 2.9。是不是和真实值3非常接近?
  • 关键点:
    1.最终的预测值w,是由每颗树的结果求和得到的。
    2.我们可以将一个预测结果拆分成 前面所有树的预测结果+新增树的预测结果
    y ^ i ( t ) = k = 1 t f k ( x i ) = y ^ i ( t 1 ) + f t ( x i ) \hat { y } _ { i } ^ { ( t ) } = \sum _ { k = 1 } ^ { t } f _ { k } \left( x _ { i } \right) = \hat { y } _ { i } ^ { ( t - 1 ) } + f _ { t } \left( x _ { i } \right)

正则化项

  • 为了避免过拟合的现象,我们从两个方面去约束Xgboost。
    1.叶子节点个数 (避免树过于庞大)
    2.对w进行L2正则化 (避免叶子节点上的值过分大和过分小)
    Ω ( f t ) = γ T + 1 2 λ j = 1 T w j 2 \Omega \left( f _ { t } \right) = \gamma T + \frac { 1 } { 2 } \lambda \sum _ { j = 1 } ^ { T } w _ { j } ^ { 2 }
    其中: T T 为一颗树上的叶子节点个数
    w w 为叶子节点的权重值(可以理解成预测)
    γ \gamma λ \lambda 为代表约束力度的系数
  • 举例 计算正则化项 Ω \Omega
    在这里插入图片描述

目标函数

  • 首先,我们想要优化的 目标函数 = 损失函数 l l +正则化项 Ω \Omega
    O b j ( t ) = i = 1 n l ( y i , y ^ i ( t ) ) + i = 1 t Ω ( f i ) O b j ^ { ( t ) } = \sum _ { i = 1 } ^ { n } l \left( y _ { i } , \hat { y } _ { i } ^ { ( t ) } \right) + \sum _ { i = 1 } ^ { t } \Omega \left( f _ { i } \right)
  • 将t棵树的总预测分解成前面t-1棵的总预测和最新的t棵的预测两部分,并且补充上常数项,得到
    O b j ( t ) = i = 1 n l ( y i , y ^ i ( t 1 ) + f t ( x i ) ) + Ω ( f t ) +  constant  O b j ^ { ( t ) } = \sum _ { i = 1 } ^ { n } l \left( y _ { i } , \hat { y } _ { i } ^ { ( t - 1 ) } + f _ { t } \left( x _ { i } \right) \right) + \Omega \left( f _ { t } \right) + \text { constant }
  • 根据泰勒展开 f ( x + Δ x ) f ( x ) + f ( x ) Δ x + 1 2 f ( x ) Δ x 2 f ( x + \Delta x ) \simeq f ( x ) + f ^ { \prime } ( x ) \Delta x + \frac { 1 } { 2 } f ^ { \prime \prime } ( x ) \Delta x ^ { 2 }
    我们将 l l 展开 f t ( x ) f _ { t }(x) 看成 Δ x \Delta x ,得到下面式子

O b j ( t ) i = 1 n [ l ( y i , y ^ i ( t 1 ) ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) +  constant  O b j ^ { ( t ) } \simeq \sum _ { i = 1 } ^ { n } \left[ l \left( y _ { i } , \hat { y } _ { i } ^ { ( t - 1 ) } \right) + g _ { i } f _ { t } \left( x _ { i } \right) + \frac { 1 } { 2 } h _ { i } f _ { t } ^ { 2 } \left( x _ { i } \right) \right] + \Omega \left( f _ { t } \right) + \text { constant }

其中gi 为一阶导 g i = y ^ ( t 1 ) l ( y i , y ^ ( t 1 ) ) g _ { i } = \partial _ { \hat { y } ^ { ( t - 1 ) } } l \left( y _ { i } , \hat { y } ^ { ( t - 1 ) } \right) ,hi为二阶导 h i = y ^ ( t 1 ) 2 l ( y i , y ^ ( t 1 ) ) h _ { i } = \partial _ { \hat { y } ^ { ( t - 1 ) } } ^ { 2 } l \left( y _ { i } , \hat { y } ^ { ( t - 1 ) } \right)

  • 对于boosting模型,前面t-1棵树的预测结果已经固定,因此损失函数也就固定,所以可以把 l ( y i , y ^ i ( t 1 ) ) l \left( y _ { i } , \hat { y } _ { i } ^ { ( t - 1 ) } \right) 看做一个常数,融合进常数项  constant  \text { constant } ,同时,因为常数项不会影响我们的优化过程,所以我们将目标函数中的常数部分先暂时忽略,并且将正则化项展开,得到下式
    i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + λ 1 2 j = 1 T w j 2 + γ T \sum _ { i = 1 } ^ { n } \left[ g _ { i } f _ { t } \left( x _ { i } \right) + \frac { 1 } { 2 } h _ { i } f _ { t } ^ { 2 } \left( x _ { i } \right) \right] + \lambda \frac { 1 } { 2 } \sum _ { j = 1 } ^ { T } w _ { j } ^ { 2 }+ \gamma T
  • 将f(x)改写为w
    = i = 1 n [ g i w q ( x i ) + 1 2 h i w q ( x i ) 2 ] + λ 1 2 j = 1 T w j 2 + γ T = \sum _ { i = 1 } ^ { n } \left[ g _ { i } w _ { q \left( x _ { i } \right) } + \frac { 1 } { 2 } h _ { i } w _ { q \left( x _ { i } \right) } ^ { 2 } \right] + \lambda \frac { 1 } { 2 } \sum _ { j = 1 } ^ { T } w _ { j } ^ { 2 }+ \gamma T
    注意:
    1.此时的求和符号∑,代表的是对每一个样本计算,n代表样本个数。
    2.正则化项中的w可以融合进前面的部分

对样本的遍历 —> 对叶子节点的遍历

因为我们每一个样本,最终都落到了某一个叶子节点上,所以这里我们就可以把对样本的遍历转化成对叶子节点的遍历。
j = 1 T [ ( i I j g i ) w j + 1 2 ( i I j h i + λ ) w j 2 ] + γ T \sum _ { j = 1 } ^ { T } \left[ \left( \sum _ { i \in I _ { j } } g _ { i } \right) w _ { j } + \frac { 1 } { 2 } \left( \sum _ { i \in I _ { j } } h _ { i } + \lambda \right) w _ { j } ^ { 2 } \right] + \gamma T
注意:此时的求和符号∑,代表的是对每一个节点计算,T代表节点个数, I j j I _ { j }代表第j个节点上的样本集合

  • 我们用G代表某一叶子节点上的一阶导数的和, H代表二阶导数的和
    G j = i I j g i G _ { j } = \sum _ { i \in I _ { j } } g _ { i } H j = i I j h i H _ { j } = \sum _ { i \in I _ { j } } h _ { i }
    带入上面的式子,得到:
    O b j ( t ) = j = 1 T [ G j w j + 1 2 ( H j + λ ) w j 2 ] + γ T O b j ^ { ( t ) }= \sum _ { j = 1 } ^ { T } \left[ G _ { j } w _ { j } + \frac { 1 } { 2 } \left( H _ { j } + \lambda \right) w _ { j } ^ { 2 } \right] + \gamma T
    看下中括号内的部分,变成了一个关于w的二次方程,二次方程求最小,x=-b/2a
    w j = G j H j + λ w _ { j } = - \frac { G _ { j } } { H _ { j } + \lambda }
    我们把能让目标函数最小的这个w带回原式,求得Obj为: O b j = 1 2 j = 1 T G j 2 H j + λ + γ T O b j = - \frac { 1 } { 2 } \sum _ { j = 1 } ^ { T } \frac { G _ { j } ^ { 2 } } { H _ { j } + \lambda } + \gamma T

属性划分

目标函数求出来了,我们如何对属性去进行划分呢?就是利用上面的目标函数。如何划分能让我们的目标函数的值降低更多,我们就如何划分。因此这里,用划分前的Obj减去划分后左右子树的Obj之和的差值,作为我们的属性划分准则,提1/2, 融负号,化简后结果如下:
γ \gamma 可以视为加入新叶子结点引入的复杂度变化)
Gain = 1 2 [ G L 2 H L + λ + G R 2 H R + λ ( G L + G R ) 2 H L + H R + λ ] γ \operatorname { Gain } = \frac { 1 } { 2 } \left[ \frac { G _ { L } ^ { 2 } } { H _ { L } + \lambda } + \frac { G _ { R } ^ { 2 } } { H _ { R } + \lambda } - \frac { \left( G _ { L } + G _ { R } \right) ^ { 2 } } { H _ { L } + H _ { R } + \lambda } \right] - \gamma
这样,找到了使Gain最大的子树结构,我们就可以在此时进行分割了。
另外,Xgboost就是在这一步可以并行化,同时去算不同的分割方式带来的Gain值变化。

Xgboost重点总结

  • 权重值w是通过最优化目标函数求出来的,而不是平均数众数等规则,因此预测效果更好,但容易过拟合。
  • 为了解决上述过拟合问题引入正则化项,包括限制叶子节点个数,和对w的L2正则化,保证树的模型不会过于深入,w的预测值会相对平均。
  • 支持自定义损失函数,由于利用泰勒展开将目标函数中的 l l 变成一阶导和二阶导的形式,因此只要能提供损失函数的一阶导和二阶导,就能带入Xgboost 。
  • 支持并行化,在选择最佳分裂点时,不同分裂点的信息增益可以并行计算(得益于不同分裂点对G和H明确的影响),极大的提高了模型的训练速度。

对比 Xgboost / GBDT

  • 损失函数:GBDT是一阶,XGB是二阶泰勒展开
  • XGB的损失函数可以自定义,具体参考 objective 这个参数
  • XGB的目标函数进行了优化,有正则项,减少过拟合,控制模型复杂度
    预剪枝:预防过拟合
  • GBDT:分裂到负损失,分裂停止 XGB:一直分裂到指定的最大深度(max_depth),然后回过头剪枝。如某个点之后不再正值,去除这个分裂。优点是,当一个负损失(-2)后存在一个正损失(+10),(-2+10=8>0)求和为正,保留这个分裂。
  • XGB有列抽样/column sample,借鉴随机森林,减少过拟合
  • 缺失值处理:XGB内置缺失值处理规则,用户提供一个和其它样本不同的值,作为一个参数传进去,作为缺失值取值。
  • XGB在不同节点遇到缺失值采取不同处理方法,并且学习未来遇到缺失值的情况。
  • XGB内置交叉检验(CV),允许每轮boosting迭代中用交叉检验,以便获取最优 Boosting_n_round 迭代次数,可利用网格搜索grid search和交叉检验cross validation进行调参。
    GBDT使用网格搜索。
  • XGB运行速度快:data事先安排好以block形式存储,利于并行计算。在训练前,对数据排序,后面迭代中反复使用block结构。
  • 关于并行,不是在tree粒度上的并行,并行在特征粒度上,对特征进行Importance计算排序,也是信息增益计算,找到最佳分割点。
  • 灵活性:XGB可以深度定制每一个子分类器
    易用性:XGB有各种语言封装
    扩展性:XGB提供了分布式训练,支持Hadoop实现
  • 共同优点:
    当数据有噪音的时候,树Tree的算法抗噪能力更强
    树容易对缺失值进行处理
    树对分类变量Categorical feature更友好

猜你喜欢

转载自blog.csdn.net/qq_26413541/article/details/84351598