xgboost是机器学习目前使用最广、效率最高的工具。之前大概了解过xgboost的原理,说实话并没有真正的理解,甚至连它和Lightgbm的优势劣势在哪里都不知道。因此这次我打算来仔细研读陈天奇大神的Xgboost PPT,逐页翻译并加备注,这样也能帮助自己更好的理解这个算法。

    原PPT链接:https://homes.cs.washington.edu/~tqchen/pdf/BoostedTree.pdf

    一、监督学习的几点关键概念

    image.png
    xi属于实数空间,表示第i个训练集
    模型:常见的即为线性模型,包括了线性回归和逻辑回归。线性回归是用来预测连续型y值的,如房价;逻辑回归是用来判断类别的,如是否离职。
    参数w,即为我们需要通过数据来计算得到的值,学习的结果即为找到合适参数w,使得计算结果y ^ \hat{y}
    y
    ^

    接近最接近yi。

    image.png
    目标函数也是机器学习中最重要的函数,学习的目的是让目标函数打到最小值。常见的目标函数由两部分构成:损失函数,一般为预测值和实际值的差距,衡量的是模型有多适配训练集。另一部分为正则项,通常利用正则项来调节模式,避免过拟合。

    对于损失函数,常见的有:

    平方差,用于一般回归
    逻辑差,也叫交叉熵损失(Cross Entropy Loss),常用于逻辑回归
    对于正则项,常见的也有两种:

    L2 模,即参数w的平方和再乘以系数λ
    L1模,即参数w乘以系数λ
    image.png
    将以上几种分类合并起来,我们就能得到以下几种模型:

    岭回归:线性回归模型+L2正则项,利用的是平方差损失函数
    Lasso回归:线性回归模型+L1正则项,同样用平方差损失函数
    逻辑回归:线性模型,交叉熵损失函数,L2正则项
    将模型,参数,目标函数的概念分开讨论,在工程上也有益处,因为我们可以修改部分代码,就能部署不同的回归函数了。

    image.png

    这页继续强调了一下损失函数和正则项对于模型学习的不同作用。前者倾向于生成更具预测性的模型,避免欠拟合;而后者倾向于生成更简易的模型,避免过拟合。二者相辅相成。
    二、 回归树集成

    image.png
    image.png
    回归树模型是区别于线性模型的另一种模型,也叫CART(Classification And Regression Tree),特点是利用将特征分隔成不同的特征空间,每一个特征空间为一个叶子,每一片叶子有一个大粪。如图中所示,按照年龄分成了两组,两个叶子的得分分别为+2和-1分
    image.png
    案例继续,现在有另一棵决策树,以是否用电脑为分界线,分割成两组,那么新的叶子又有两个得分0.9和-0.9。那么将两棵树合在一起看,就能得到具体某个元素(即xi)的得分,即为不同决策树所在叶子的得分之和。

    image.png
    树模型目前广泛适用于数据挖掘竞赛中,常见的有GBM(gradient boosting machine,一般叫做梯度提升树算法),和Random Forest(随机森林)
    对输入不需要进行特征正则化
    可以在不同变量中学习到高阶关系(如两个参数的乘积这类关系)
    可规模化,并能使用于工业中。
    image.png
    现在开始详解树模型。
    上文提到,可以将第i个数据,在不同的树中的得分累加起来,得到一个总得分,就是这页公式的含义。
    y ^ \hat{y}
    y
    ^

    :样本在不同树模型中的总得分,也为模型预测值。
    f k f{k}f
    k

    : 第k棵树的得分表,如在上文中,小于20岁得2分,大于20岁得0.9分,就是一种f函数。k棵树共有k种函数。
    这里我们可以将f k f
    {k}f
    k

    视为一个参数来训练,好比线性模型中的w

    image.png
    那我们应该如何学习这个参数(函数)呢?其实跟线性模型也是一样的,设置目标函数并通过训练优化它。
    这里举了一个例子,如何预测我对浪漫音乐的喜爱程度(回归模型)。从散点图我们可以清晰的看到,可以将数据点分成三个阶段,即三个样本空间,可以最大程度拟合这个喜爱程度。
    但是如果用到机器学习的手段,就需要一些优化过程了。

    image.png
    首先,需要确定的有两点,一个是需要多少个分裂点,这里可以理解为可以有多少棵树;其次,需要确定的是每一层的高度是多少,这里相当于是每一片叶子的得分。

    image.png
    右上的图,分裂过多,虽然每一层的得分基本可以反应实际的结果,但是会造成过拟合。
    左下的图,只有一棵分裂树,模型简易,但是由于分裂点位置错误,也导致了模型错误。
    只有右下的图既保证了模型的准确性,又保证了简易型,防止过拟合。

    image.png
    根据以上案例,我们再回到模型建立的一般步骤中。
    模型函数已知,现在来考虑目标函数。前文提到,目标函数分为了损失函数和正则项,这两项都与模型函数fk有关。
    但是对于树模型,对正则项的定义也能简单一点,例如使用树的节点数、深度作为正则项,或者用叶深度的L2模作为正则项。
    image.png

    当我们在讨论决策树时,它通常是启发式的(heuristics):
    根据信息增益分裂树。关于信息增益,简单来说是代表了在一个条件下,信息复杂度(不确定性)减少的程度。具体展开可以百度。
    修剪树枝
    设置最大深度
    顺滑叶得分

    启发式的学习方法其实和目标函数也是息息相关的,例如:
    信息增益对应的是训练损失
    剪枝对应了根据节点的正则化修正
    最大深度对应了函数空间的最大限制
    顺滑叶得分则对应了对叶权重的L2正则化。
    image.png

    既然是CART,顾名思义,它技能用于预测回归,也能用以预测分类,这与你的目标函数的选择相关。
    例如,如果用平方差作为损失函数,那就能获得一个回归树,也就是常见的GBM(Gradient Boosting Machine)。
    而如果选用交叉熵作为损失函数,就能得到LogitBoost。

    三、梯度提升(Gradient Boosting)算法详解
    image.png
    再次强调了机器学习的几点特性:

    偏差-方差平衡
    损失函数+正则项所构成的目标函数应用于回归树训练
    我们想要的是具有预测性和建议的函数

    image.png
    第一行为目标函数,其中l ( y i , y i ^ ) l(y{i},\hat{y{i}})l(y
    i

    ,
    y
    i


    ^

    )表示损失函数,第一项需要将对所有样本求和。Ω ( f k ) \Omega (f_{k})Ω(f
    k

    )表示正则项,需要对k个特征的正则项进行求和。

    为了降低目标函数,既需要让损失函数降低,即拉近预测值与实际值之间的距离,又需要让正则项降低,即降低各个特征函数的权重。

    在这里,我们没办法使用SGD(Stochastic Gradient Decent,随机梯度下降法)。因为SGD常用来寻找的是数值解,需要有已定的目标函数;但是在这里,我们需要在确定是一个函数,而非数值解,因此无法使用。

    解决方案是使用加法模型提升。具体来说,是将每一次的预测值,转换为前一次的预测值加上一个函数f t ( x i ) f{t}(x{i})f
    t

    (x
    i

    )。注意这里的f t ( x i ) f{t}(x{i})f
    t

    (x
    i

    )和上文的Ω ( f k ) \Omega (f{k})Ω(f
    k

    )中的f k f
    {k}f
    k

    不是同一个函数。

    这里对GBDT在做多一些解释,方便下文的理解。

    GBDT的思想是累加多个弱学习器,以达到强学习器的效果。
    每一棵树,就是一个弱分类器。例如大于10岁和小于10岁是一个弱分类器,性别是另一个弱分类器,把二者的得分相加起来,就能得到一个预测值y。
    加法模型是GBDT的常见模型,他的思路是先训练一棵树,即弱分类器,计算弱分类器与实际值的差值。接着,将该差值传到下一个弱分类器中,再计算差值与下一个分类器的差值,以此迭代。如果换个方向讲,就是每一次迭代,预测值都等于了前一次的预测值,加上了这次的树。
    对于加法模型,正则项也与SGD算法中的L1范式L2范式有区别。在GBDT中,常见的正则项处理方式有三种(摘自百度):
    1. 第一种,是对步长的调节,即每一轮迭代中对新增的树乘上一个系数v,v属于[0,1]。对于同样的训练集学习效果,较小的步长意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。
    2. 第二种正则化的方式是通过子采样比例(subsample)。取值为(0,1]。注意这里的子采样和随机森林不一样,随机森林使用的是放回抽样,而这里是不放回抽样。如果取值为1,则全部样本都使用,等于没有使用子采样。如果取值小于1,则只有一部分样本会去做GBDT的决策树拟合。选择小于1的比例可以减少方差,即防止过拟合,但是会增加样本拟合的偏差,因此取值不能太低。推荐在[0.5, 0.8]之间。
    3. 第三种利用了决策树的剪枝,即正则项为α*T,T为叶子树,显然T无法过大,否则目标函数会过高。
    image.png
    那么我们要如何确定这个加法项ft呢,其实也是利用训练优化它。

    这里先明确几个概念:

    y ^ i ( t ) \hat{y}{i}^{(t)}
    y
    ^


    i
    (t)

    表示,第t次迭代的第i个变量所对应的预测值。
    将所有变量的预测值求和,才是目标函数的第一项。
    将加法公式y ^ i ( t ) = y ^ i ( t − 1 ) + f t ( x i ) \hat{y}
    {i}^{(t)}=\hat{y}{i}^{(t-1)}+f{t}(x{i})
    y
    ^


    i
    (t)

    =
    y
    ^


    i
    (t−1)

    +f
    t

    (x
    i

    )代入到目标函数中,可以将目标函数的第一项——损失函数变为:
    ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) \sum
    {i=1}^{n}l(y{i},\hat{y}{i}^{(t-1)}+f{t}(x{i}))∑
    i=1
    n

    l(y
    i

    ,
    y
    ^


    i
    (t−1)

    +f
    t

    (x
    i

    ))

    其次,对于正则项,在原公式中,是对每一次迭代的残差函数f i f{i}f
    i

    求和。但是假如我们使用了上一页中提到的步长控制法,这一项其实仅与本次迭代有关。因此作者这里就将求和符号去掉了,仅留下了本次迭代所增加的决策树f t f
    {t}f
    t

    接着,作者举了一个例子,当损失函数为平方差时,即:
    l ( y i , y ^ i ( t ) ) = ( y i − y ^ i ( t ) ) 2 = ( y i − y ^ i ( t − 1 ) − f t ( x i ) ) 2 l(y{i},\hat{y}{i}^{(t)})=(y{i}-\hat{y}{i}^{(t)})^2=(y{i}-\hat{y}{i}^{(t-1)}-f{t}(x{i}))^2l(y
    i

    ,
    y
    ^


    i
    (t)

    )=(y
    i


    y
    ^


    i
    (t)

    )
    2
    =(y
    i


    y
    ^


    i
    (t−1)

    −f
    t

    (x
    i

    ))
    2

    l ( y i , y ^ i ( t ) ) = ( y i − y ^ i ( t − 1 ) ) 2 − 2 ( y i − y ^ i ( t − 1 ) ) f t ( x i ) + f t ( x i ) 2 l(y{i},\hat{y}{i}^{(t)})=(y{i}-\hat{y}{i}^{(t-1)})^2-2(y{i}-\hat{y}{i}^{(t-1)})f{t}(x{i})+f{t}(x{i})^2l(y
    i

    ,
    y
    ^


    i
    (t)

    )=(y
    i


    y
    ^


    i
    (t−1)

    )
    2
    −2(y
    i


    y
    ^


    i
    (t−1)

    )f
    t

    (x
    i

    )+f
    t

    (x
    i

    )
    2

    在这里,由于y i y{i}y
    i

    和y i ( t − 1 ) y
    {i}^{(t-1)}y
    i
    (t−1)

    都是在上一轮算出来的,并且在这个公式中,仅有f t f{t}f
    t

    是变量,所以我们将第一项视为一个常数项,搬到最后,则原公式就变成了:
    l ( y i , y ^ i ( t ) ) = 2 ( y ^ i ( t − 1 ) − y i ) f t ( x i ) + f t ( x i ) 2 l(y
    {i},\hat{y}{i}^{(t)})=2(\hat{y}{i}^{(t-1)}-y{i})f{t}(x{i})+f{t}(x{i})^2l(y
    i

    ,
    y
    ^


    i
    (t)

    )=2(
    y
    ^


    i
    (t−1)

    −y
    i

    )f
    t

    (x
    i

    )+f
    t

    (x
    i

    )
    2

    而y i − y i ( t − 1 ) y
    {i}-y_{i}^{(t-1)}y
    i

    −y
    i
    (t−1)

    也可以视为上一轮的残差。

    image.png

    在上一页的PPT中,我们将平方差的损失函数进行了简化。而在这一页中,我们需要证明,对任意的损失函数,我们都能有类似的简化。
    主要用到的方法,就是泰勒展开。
    对于损失函数l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) l(y{i},\hat{y}{i}^{(t-1)}+f{t}(x{i}))l(y
    i

    ,
    y
    ^


    i
    (t−1)

    +f
    t

    (x
    i

    )),我们将( y i , y ^ i ( t − 1 ) ) (y{i},\hat{y}{i}^{(t-1)})(y
    i

    ,
    y
    ^


    i
    (t−1)

    )视为x,将f t ( x i ) f{t}(x{i})f
    t

    (x
    i

    )视为Δ x \Delta xΔx,那么f ( x ) f(x)f(x)就为l ( y i , y ^ i ( t − 1 ) ) l(y{i},\hat{y}{i}^{(t-1)})l(y
    i

    ,
    y
    ^


    i
    (t−1)

    )。
    在这里,y i y{i}y
    i

    也是一个常数。因此自变量x中,实际只有一个y ^ i ( t − 1 ) \hat{y}
    {i}^{(t-1)}
    y
    ^


    i
    (t−1)

    。那么我们对x求导数,就能得到一个偏微分:
    ∂ ( l ( y i , y ^ ( t − 1 ) ) ) ∂ ( y ^ ( t − 1 ) ) \frac{\partial (l(y{i},\hat{y}{(t-1)}))}{\partial (\hat{y}{(t-1)})}
    ∂(
    y
    ^


    (t−1)

    )
    ∂(l(y
    i

    ,
    y
    ^


    (t−1)

    ))


    或者简化为:∂ ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) ) \frac{\partial }{\partial \hat{y}
    {(t-1)}} l(y{i},\hat{y}{(t-1)}))

    y
    ^


    (t−1)




    l(y
    i

    ,
    y
    ^


    (t−1)

    )),即为PPT中的g i g{i}g
    i


    同理,二次求导之后也能得到h i h
    {i}h
    i

    在这里,尽管gi和hi都是用偏导数表示,但是只要已知损失函数,一阶导数和二阶导数都能很容易求得。

    如果仍然以平方差损失函数为例,我们可以很容易求得一阶导数和二阶导数,恰好就是我们在上一页PPT中计算出来的结果。
    image.png

    经过一系列的努力,我们将目标函数简化到了三个变量上:g i g{i}g
    i

    , h i h
    {i}h
    i

    ,f t f_{t}f
    t

    。其中,前两项都能轻易计算得出,并且不随着树的变化而变化。

    这在工程上对我们的帮助也极大。
    image.png

    现在我们来对树的定义做一些调整。
    我们定义了两个变量,叶分数,和叶指数。所谓叶分数,就是该叶子,或者该群组的得分;而叶指数,就是对叶子的排序,如图中的leaf1, leaf2,1和2就是叶指数。

    这里还定义了上文中的函数f t ( x ) = w q ( x ) f{t}(x)=w{q(x)}f
    t

    (x)=w
    q(x)

    。在这里,q(x)表示第x个元素的叶指数,也就是第x个元素所在的叶子的位置。如q(男孩)=1,表示男孩是在第一个叶子中,q(奶奶)=3,表示奶奶在第三个叶子中。由于指数一定是正整数,所以q一定是在正整数集中。
    而w,就是叶分数,即这个叶子对应的得分。如第一个叶子的得分是+2,则w1=+2。由于这个评分可正可负,因此属于实数集。

    image.png
    定义完了f t f{t}f
    t

    ,我们再来定义Ω ( f t ) \Omega(f
    {t})Ω(f
    t

    )。
    上文提到,在加法模型中,正则项主要有三类:1. 步长调节,2. 采样比例,3. 剪枝。在这里,作者设计的正则项用到了第一种和第三种。

    我们看第一项,γ T \gamma TγT,这项实际就是第三种正则法:剪枝。对叶子数量乘上一个系数,可以有效防止叶子过多。
    再看第二项,w是上文中的f t f_{t}f
    t

    的值,也就是每一次迭代的增加值。那么对这个值的平方再乘上系数,实际上也是第一种正则法,步长调节。取平方求和的方法也类似SGD中的L2模,能够有效控制梯度增长的速度,以提高模型精度。

    接着,作者把T=3,w1=+2, w2=0.1, w3=-0.1代入到原公式中,就可以得到Ω的值了,也就是本轮迭代的正则项(γ和λ都视为常数)。
    image.png

    好了,我们现在将正则项再代回原目标函数中,做合并同类项,可以把w也放到求和项中。
    这里需要注意,在正则项中,是T个w 2 w^2w
    2
    累加,而再损失函数中,是n个自变量w 2 w^2w
    2
    累加。因此,我们需要将求和号Σ统一成T个累加。在前面的图中,我们可以知道,一个叶子中可能会有多个自变量。把一个叶子中所有自变量的gi和hi累加起来,就是公式里Σ i ∈ I j \Sigma{i \in I{j}}Σ
    i∈I
    j



    的含义。
    那么我们可以看到,经过不懈的努力我们将求和号中的公式,变成了关于w j w_{j}w
    j

    的一元二次方程。

    image.png
    在这一页,我们复习了一下高中里一元二次方程的最值求解。对于G x + 1 / 2 H x 2 Gx+1/2Hx^2Gx+1/2Hx
    2
    ,当x = − G / H x=-G/Hx=−G/H时,该方程能取到最小值− 1 2 G 2 H -\frac {1} {2} \frac {G^2} {H}−
    2
    1


    H
    G
    2



    那只要我们把上文的一元二次方程做类似的处理,就能知道:
    当每一个叶子中的w ww都能取到− G j H j + λ -\frac {G{j}} {H{j}+\lambda}−
    H
    j


    G
    j



    时,目标函数能够取到最小值。
    不过这里实际上有两个假设:

    树的结构已经定下来了。即每一片叶子中分配了哪些元素,都已经定好了。
    我们对每一片叶子取到损失函数最小值,以此达到整体的最小值。实际上,这个最小值有可能仅是局部最小值,不一定就是全局最小值。
    image.png
    这一页就是对上一页的公式进行数值展示,此处不再赘述。

    image.png
    这里再次总结了饿一下,我们已经把目标函数变成了仅与G, H, γ,λ和T这五项已知参数有关的函数,把之前的变量f t f_{t}f
    t

    消灭掉了,也就不需要对每一个叶子进行打分了!
    那么现在问题来,刚才我们提到,以上这些是假设树结构确定的情况下得到的结果。但是树的结构有好多种,我们应该如何确定呢?
    image.png
    这里作者用的方法是贪婪法。

    我们假设现在迭代到了第i次,此时一共有T个树叶。此时,我们要对其中一个叶子进行分裂,把里面的群组分成了左边部分和右边部分。那么分裂后,目标函数就变成了:
    O b j 分 裂 后 = l 左 + l 右 + γ ( T + 1 ) Obj{分裂后}=l{左}+l{右}+\gamma (T+1)Obj
    分裂后

    =l


    +l


    +γ(T+1)
    O b j 分 裂 后 = − 1 2 G 左 2 H 左 + λ − 1 2 G 右 2 H 右 + λ + γ ( T + 1 ) Obj
    {分裂后}=-\frac {1} {2} \frac {G{左}^2} {H{左}+\lambda}-\frac {1} {2} \frac {G{右}^2} {H{右}+\lambda}+\gamma (T+1)Obj
    分裂后

    =−
    2
    1


    H



    G

    2




    2
    1


    H



    G

    2



    +γ(T+1)
    假如没有分裂,那之前的应该是:
    O b j 分 裂 前 = − 1 2 G 总 2 H 总 + λ + γ T Obj{分裂前}=-\frac {1} {2} \frac {G{总}^2} {H{总}+\lambda}+\gamma TObj
    分裂前

    =−
    2
    1


    H



    G

    2



    +γT
    其中,G 总 = G 左 + G 右 G
    {总}=G{左}+G{右}G


    =G


    +G


    , H 总 = H 左 + H 右 H{总}=H{左}+H{右}H


    =H


    +H



    因此,将分裂前的目标函数,减去分裂后的目标函数,就能得到分裂带来的目标函数增益:
    G a i n = 1 2 [ G 左 2 H 左 + λ + G 右 2 H 右 + λ − ( G 左 + G 右 ) 2 H 左 + H 右 + λ ] − γ Gain=\frac {1} {2}[\frac {G
    {左}^2} {H{左}+\lambda}+\frac {G{右}^2} {H{右}+\lambda}-\frac {(G{左}+G{右})^2} {H{左}+H_{右}+\lambda}]-\gammaGain=
    2
    1

    [
    H



    G

    2



    +
    H



    G

    2




    H


    +H



    (G


    +G


    )
    2


    ]−γ

    增益越大,意味着分裂前比分裂后的差距越大,也就意味着梯度下降得越明显。
    image.png
    这里举了个例子,当xj并且我们意识到,只要从左往右遍历一遍,就可以确定出分裂增益最大的分裂方法。

    寻找分裂的算法:

    对每一个节点,即已有的叶,遍历所有特征
    对每一个特征,按特征所对应的叶值进行排序
    用线性扫描,决定最佳分裂位置。
    用最佳分裂生成新的节点(叶)
    生成K深度树的时间复杂度:

    对于每一层,需要O时间去做一次排序,并且要做K层。
    后期可以继续优化
    适用于非常大的数据集
    image.png

    有些算法会将数值型变量和排序型变量分开处理。
    但是我们的算法是用打分来往下分裂的,所以同样适用于排序型变量。
    实际上,我们还能用独热编码,将排序型变量转换为0-1数值变量,就可以直接进行分类处理。
    这样操作可以形成稀疏矩阵,方便学习。
    image.png
    我们再回顾一下之前的公式,对于分裂增益,实际上是有可能为负值的,即当损失函数的增益小于正则项参数γ时,增益即为负。
    当遇到这种情况时,我们就要停止继续分裂,这种叫做“提前停止”
    不过实际上,一个负增益的分裂有可能是会在未来产生一个更佳的分裂

    或者,我们也可以先将树生长到最佳深度,再将所有负增益的叶子剪掉,这叫“后剪枝”。

    image.png
    我们最后整理一下整个算法逻辑:

    在每一次迭代中新增一棵树。
    在迭代开始前,先计算两个参数:g i g{i}g
    i

    和h i h
    {i}h
    i

    ,这两个参数仅与上一轮所得的y ^ \hat{y}
    y
    ^

    ,这一轮的yi,以及损失函数的设置有关。
    利用遍历法找到最佳分裂点,生成一个新的树。此时f t ( x ) = − G H + λ f{t}(x)=-\frac {G} {H+\lambda}f
    t

    (x)=−
    H+λ
    G


    把f t ( x ) f
    {t}(x)f
    t

    (x)加到加法模型中,就可以获得这轮迭代的y ^ \hat{y}
    y
    ^


    不过通常我们会在f t ( x ) f_{t}(x)f
    t

    (x)前再加上一个参数ϵ \epsilonϵ,用来调整步长,通常设置为0.1.
    这样的话,我们每一步就不是充分优化,这样可以防止过拟合。
    四、总结
    image.png
    提出了两个问题:

    我们如果用上述分类器是解决权重回归问题。
    对于时间序列,我们是否有其他方法去学习时间分裂的方法?
    image.png
    对于第一个问题,就是在损失函数(平方差公式)前面加上一个参数a,就可以解决问题了。
    image.png

    对于时间序列问题,除了自上而下的贪婪法之外,还能用自下而上的贪婪法,即从独立点出发,考虑合并其他独立点,是否可以使梯度增加。

    image.png

    最后总结,XGBoost其实是巧妙设置了损失函数和正则项,使得计算过程得到了充分的简化,效率大大提高。

    之后我会再看一下lightGBM, RF等其他算法,再写一份对比的文章出来。

    不,是你的杰宝
    关注

    9