XGBoost原理

这篇博客是我在学习XGBoost的过程中看到的最好的一篇博客,对于直接看陈天奇论文有压力的同学这篇博客绝对是最好的选择。我会在个人认为不好理解的地方加写自己的理解,其他说明部分在修改了一些原作者的笔误后搬运了过来。
原文地址:http://djjowfy.com/2017/08/01/XGBoost的原理/
————————————————————————————————————

这篇博客的由来(瞎扯)

我在学习机器学习的时候,发现网上很少有对XGBoost原理探究的文章。而XGBoost用途是很广泛的。据kaggle在2015年的统计,在29只冠军队中,有17只用的是XGBoost,其中有8只只用了XGBoost。于是只能自己在网上找资料,幸而XGBoost的作者陈天奇在arixv上发布了一篇关于XGBoost的论文,于是就有了这篇博客。这篇博客首先将回顾监督学习,给出它的通用的优化函数。然后介绍回归树,它是XGBoost里的得到的最终模型的基本组成单元,许多棵回归树组成的回归森林就是XGBoost最终的学习模型。进而为了构造回归树,介绍了gradient tree boosting。从而引出了两种算法,一种是用于单线程的贪婪算法,一种是可以并行的近似算法,并作了结果的对比,显示出近似算法比较高的精确性。最后将介绍XGBoost的用法。

监督学习的回顾(背景知识)

概念

符号含义
R d R^{d} Rd特征数目为 d d d的数据集
x i ∈ R d x_{i}∈R^{d} xiRd i i i个样本
w j w_{j} wj j j j个特征的权重
y ^ i ŷ_{i} y^i x i x_{i} xi的预测值
y i y_{i} yi i i i个训练集的对应的标签
Θ Θ Θ特征权重的集合, Θ = w j ∥ j = 1 , ⋯ , d Θ={wj \| j=1,⋯,d} Θ=wjj=1,,d

模型

基本上相关的所有模型都是在下面这个线性式子上发展起来的
y ^ i = ∑ j = 0 d w j x i j ŷ_{i}=∑_{j=0}^{d}w_{j}x_{ij} y^i=j=0dwjxij
上式中 x 0 = 1 x_0=1 x0=1,就是引入了一个偏差量,或者说加入了一个常数项。由该式子可以得到一些模型:
  - 线性模型,最后的得分就是 y ^ i ŷ_{ i} y^i
  - logistic模型,最后的得分是 1 / ( 1 + e x p ( − y ^ i ) ) 1/(1+exp(−ŷ_{i})) 1/(1+exp(y^i))。然后设置阀值,转为正负实例。
  - 其余的大部分也是基于 y ^ i ŷ _{i} y^i做了一些运算得到最后的分数

参数

参数就是 Θ Θ Θ,这也正是我们所需要通过训练得出的。

训练时的目标函数

训练时通用的目标函数如下:
O b j ( Θ ) = L ( Θ ) + Ω ( Θ ) Obj(Θ)=L(Θ)+Ω(Θ) Obj(Θ)=L(Θ)+Ω(Θ)在上式中 L ( Θ ) L(Θ) L(Θ)代表的是训练误差,表示该模型对于训练集的匹配程度。 Ω ( Θ ) Ω(Θ) Ω(Θ)代表的是正则项,表明的是模型的复杂度。
  训练误差可以用 L = ∑ i = 1 n l ( y i , y ^ i ) L=∑^{n}_{i=1}l(y_{i},ŷ_{ i}) L=i=1nl(yi,y^i)来表示,一般有方差和logistic误差。

  • 方差: l ( y i , y ^ i ) = ( y i − y ^ i ) 2 l(y_{i},ŷ_{i})=(y_{i}−ŷ_{i})^{2} l(yi,y^i)=(yiy^i)2
  • logstic误差: l ( y i , y ^ i ) = y i l n ( 1 + e − y ^ i ) + ( 1 − y i ) l n ( 1 + e y ^ i ) l(y_{i},ŷ_i)=y_iln(1+e^{−ŷ_i})+(1−y_i)ln(1+e^{ŷ_i}) l(yi,y^i)=yiln(1+ey^i)+(1yi)ln(1+ey^i)

正则项按照Andrew NG的话来说,就是避免过拟合的。为什么能起到这个作用呢?正是因为它反应的是模型复杂度。模型复杂度,也就是我们的假设的复杂度,按照奥卡姆剃刀的原则,假设越简单越好。所以我们需要这一项来控制。

  • L2 范数: Ω ( w ) = λ ∣ ∣ w ∣ ∣ 2 Ω(w)=λ||w||^2 Ω(w)=λw2
  • L1 范数(lasso): Ω ( w ) = λ ∣ ∣ w ∣ ∣ 1 Ω(w)=λ||w||_1 Ω(w)=λw1

常见的优化函数有有岭回归,logstic回归和Lasso,具体的式子如下

  • 岭回归,这是最常见的一种,由线性模型,方差和L2范数构成。具体式子为 ∑ i = 1 n ( y i − w T x i ) 2 + λ ∣ ∣ w ∣ ∣ 2 ∑^n_{i=1}(y_i−w^Tx_i)^2+λ||w||^2 i=1n(yiwTxi)2+λw2
  • logstic回归,这也是常见的一种,主要是用于二分类问题,比如爱还是不爱之类的。由线性模型,logistic 误差和L2范数构成。具体式子为 ∑ i = 1 n [ y i l n ( 1 + e − w T x i ) + ( 1 − y i ) l n ( 1 + e w T x i ) ] + λ ∣ ∣ w ∣ ∣ 2 ∑^n_{i=1}[y_iln(1+e^{−w^Tx_i})+(1−y_i)ln(1+e^{w^Tx_i})]+λ||w||^2 i=1n[yiln(1+ewTxi)+(1yi)ln(1+ewTxi)]+λw2
  • lasso比较少见,它是由线性模型,方差和L1范数构成的。具体式子为 ∑ i = 1 n ( y i − w T x i ) 2 + λ ∣ ∣ w ∣ ∣ 1 ∑^n_{i=1}(y_i−w^Tx_i)^2+λ||w||_1 i=1n(yiwTxi)2+λw1

我们的目标的就是让 O b j ( Θ ) Obj(Θ) Obj(Θ)最小。那么由上述分析可见,这时必须让 L ( Θ ) L(Θ) L(Θ) Ω ( Θ ) Ω(Θ) Ω(Θ)都比较小。而我们训练模型的时候,根据Andrew Ng的课程,要在bias和variance中间找平衡点。bias由 L ( Θ ) L(Θ) L(Θ)控制,variance由 Ω ( Θ ) Ω(Θ) Ω(Θ)控制。欠拟合,那么 L ( Θ ) L(Θ) L(Θ) Ω ( Θ ) Ω(Θ) Ω(Θ)都会比较大,过拟合的话 Ω ( Θ ) Ω(Θ) Ω(Θ)会比较大,因为模型的扩展性不强,或者说稳定性不好。

回归树的介绍(基础学习模型)

概述

回归树,也叫做分类与回归树,我认为就是一个叶子节点具有权重的二叉决策树。它具有以下两点特征
     - 决策规则与决策树的一样
     - 每个叶子节点上都包含了一个权重,也有人叫做分数
  下图就是一个回归树的示例:
在这里插入图片描述
 回归树有以下四个优点:
    1. 使用范围广,像GBM,随机森林等。(PS:据陈天奇大神的统计,至少有超过半数的竞赛优胜者的解决方案都是用回归树的变种)
    2. 对于输入范围不敏感。所以并不需要对输入归一化
    3. 能学习特征之间更高级别的相互关系
    4. 很容易对其扩展

模型

假设我们有K棵树,那么
y ^ i = ∑ k = 1 K f k ( x i ) , f k ∈ F ŷ_ i=∑_{k=1}^Kf_k(x_i), f_k∈F y^i=k=1Kfk(xi),fkF
  上式中 F F F表示的是回归森林中的所有函数空间。 f k ( x i ) f_k(x_i) fk(xi)表示的就是第 i i i个样本在第 k k k棵树中落在的叶子的权重。以下图为例
在这里插入图片描述
可见小男孩落在第一棵树的最左叶子和第二棵树的最左叶子,所以它的得分就是这两片叶子的权重之和,其余也同理。
那么现在我们需要求的参数就是每棵树的结构和每片叶子的权重,或者简单的来说就是求 f k f_k fk。那么为了和上一节所说的通用结构统一,可以设
Θ = f 1 , f 2 , f 3 , . . . , f k Θ={f1,f2,f3,...,fk} Θ=f1,f2,f3,...,fk
如果我们只看一棵回归树,那么它可以绘成分段函数如下
在这里插入图片描述可见分段函数的分割点就是回归树的非叶子节点,分段函数每一段的高度就是回归树叶子的权重。那么就可以直观地看到欠拟合和过拟合曲线所对应的回归树的结构。根据我们上一节的讨论, Ω ( f ) Ω(f) Ω(f)表示模型复杂度,那么在这里就对应着分段函数的琐碎程度。 L ( f ) L(f) L(f)表示的就是函数曲线和训练集的匹配程度。
在这里插入图片描述
综上所述,我们可以得出该模型的表达式如下

y ^ i = ∑ k = 1 K f k ( x i ) , f k ∈ F ŷ_i=∑_{k=1}^Kf_k(x_i), f_k∈F y^i=k=1Kfk(xi),fkF

训练时的目标函数

训练误差如下

L ( Θ ) = ∑ i = 1 n l ( y i , y ^ i ) = ∑ i = 1 n l ( y i , ∑ k = 1 K f k ( x i ) ) L(Θ)=∑_{i=1}^nl(y_i,ŷ_i)=∑_{i=1}^nl(y_i,∑_{k=1}^Kf_k(x_i)) L(Θ)=i=1nl(yi,y^i)=i=1nl(yi,k=1Kfk(xi))
模型复杂度如下

Ω ( Θ ) = ∑ k = 1 K Ω ( f k ) Ω(Θ)=∑_{k=1}^KΩ(f_k) Ω(Θ)=k=1KΩ(fk)
如果训练误差

  • l ( y , y ^ i ) = ( y i − y ^ i ) 2 l(y,ŷ_i)=(y_i−ŷ_i)^2 l(y,y^i)=(yiy^i)2,那么这就叫做gradient boosted machine

  • l ( y , y ^ i ) = y i l n ( 1 + e − y ^ i + ( 1 − y i ) l n ( 1 + e y ^ i ) ) l(y,ŷ_i)=y_iln(1+e^{−ŷ_i}+ (1−y_i)ln(1+e^{ŷ_i})) l(y,y^i)=yiln(1+ey^i+(1yi)ln(1+ey^i)),那么这就叫做logistBosst

对于 Ω ( f k ) Ω(f_k) Ω(fk)来说,可以用树的节点个数,树的深度,树叶权重的 L 2 L2 L2范数等等来进行描述。

参数

于是现在未知的就是 f k f_k fk,这就是我们下一节所要解决的问题

Θ = f 1 , f 2 , f 3 , ⋅ ⋅ ⋅ , f k Θ={f_1,f_2,f_3,⋅⋅⋅,f_k} Θ=f1,f2,f3,,fk

Gradient Boosting(如何构造回归树)

上一节说明来回归树长啥样,也就是我们的模型最后长啥样。但是该模型应该怎么去求出 Θ Θ Θ呢?这一节就介绍两种算法,一种是贪心算法,一种是近似算法。

贪心算法

完善目标函数的定义

这个算法的思想很简单,一棵树一棵树地往上加,一直到K棵树停止。过程可以用下式表达:
y ^ i ( 0 ) = 0 ŷ_i^{(0)}=0 y^i(0)=0 y ^ i ( 1 ) = y ^ i ( 0 ) + f 1 ( x i ) ŷ_i^{(1)}=ŷ^{(0)}_i+f_1(x_i) y^i(1)=y^i(0)+f1(xi) y ^ i ( 2 ) = y ^ i ( 0 ) + f 1 ( x i ) + f 2 ( x i ) ŷ_i^{(2)}=ŷ^{(0)}_i+f_1(x_i)+f_2(x_i) y^i(2)=y^i(0)+f1(xi)+f2(xi) . . . ... ... y ^ t ( t ) = ∑ k = 1 t f k ( x i ) = y ^ i ( t − 1 ) + f t ( x i ) ŷ^{(t)}_t=∑_{k=1}^tf_k(x_i)=ŷ^{(t−1)}_i+f_t{(x_i)} y^t(t)=k=1tfk(xi)=y^i(t1)+ft(xi)
y ^ i ( t ) ŷ^{(t)}_i y^i(t)表示的是第 i i i次循环后,对 x i x_i xi所得到的得分。(明确来说是第i棵树对样本的得分)于是带入目标函数可得
O b j ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t ) ) + ∑ i = 1 t Ω ( f i ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) + c o n s t a n t Obj(t)=∑_{i=1}^nl(y_i,ŷ^{(t)}_i)+∑_{i=1}^tΩ(f_i)\\=∑_{i=1}^nl(y_i,ŷ^{ (t−1)}_i+f_t(x_i))+Ω(f_t)+constant Obj(t)=i=1nl(yi,y^i(t))+i=1tΩ(fi)=i=1nl(yi,y^i(t1)+ft(xi))+Ω(ft)+constant
可由泰勒公式得到下式
f ( x + Δ x ) ≈ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+Δx)≈f(x)+f′(x)Δx+\frac{1}{2}f′′(x)Δx^2 f(x+Δx)f(x)+f(x)Δx+21f(x)Δx2
那么现在可以把 l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) l(y_i,ŷ^{ (t−1)}_i+f_t(x_i)) l(yi,y^i(t1)+ft(xi))看成上式中的 f ( x + Δ x ) f(x+Δx) f(x+Δx) l ( y i , y ^ i ( t − 1 ) ) l(y_i,ŷ^{(t−1)}_i) l(yi,y^i(t1))就是 f ( x ) f(x) f(x) f t ( x i ) f_t(x_i) ft(xi) Δ x Δx Δx。然后设 g i g_i gi代表 f ′ ( x ) f′(x) f(x),也就是 g i = ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) g_i=∂_{ŷ^{(t−1)}} l(y_i,ŷ^{(t−1)}) gi=y^(t1)l(yi,y^(t1)), 用 h i h_i hi代表 f ′ ′ ( x ) f′′(x) f(x), 于是 h i = ∂ y ^ ( t − 1 ) 2 l ( y i , y ^ ( t − 1 ) ) h_i=∂^2_{ŷ^{(t−1)}} l(y_i,ŷ^{(t−1)}) hi=y^(t1)2l(yi,y^(t1)),于是现在目标函数就为下式:
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 ) + c o n s t a n t = ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + [ ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) ) + c o n s t a n t ] Obj(t)≈∑_{i=1}^n[l(y_i,ŷ^{(t−1)}_i)+g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i)]+Ω(f_t)+constant\\=∑_{i=1}^n[g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i)]+Ω(f_t)+[∑_{i=1}^nl(y_i,ŷ^{(t−1)}_i)+constant] Obj(t)i=1n[l(yi,y^i(t1))+gift(xi)+21hift2(xi)]+Ω(ft)+constant=i=1n[gift(xi)+21hift2(xi)]+Ω(ft)+[i=1nl(yi,y^i(t1))+constant]
很明显,上式中后面那项 [ ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) ) + c o n s t a n t ] [∑^n_{i=1}l(y_i,ŷ^{(t−1)}_i)+constant] [i=1nl(yi,y^i(t1))+constant]对于该目标函数我们求最优值点的时候并无影响,所以,现在可把优化函数写为
O b j ( t ) ≈ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) Obj(t)≈∑_{i=1}^n[g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i)]+Ω(f_t) Obj(t)i=1n[gift(xi)+21hift2(xi)]+Ω(ft)
上一节讨论了 f t ( x ) f_t(x) ft(x)的物理意义,现在我们对其进行数学公式化。设 w ∈ R T w∈R^T wRT w w w为树叶的权重序列, q : R d → 1 , 2 , ⋅ ⋅ ⋅ , T q:R^d→{1,2,⋅⋅⋅,T} q:Rd1,2,,T q q q为树的结构。那么 q ( x ) q(x) q(x)表示的就是样本 x x x所落在树叶的位置。可以用下图形象地表示
在这里插入图片描述
于是 f t ( x ) f_t(x) ft(x)可以用下式进行表示
f t ( x ) = w q ( x ) , w ∈ R T q : R d → 1 , 2 , ⋅ ⋅ ⋅ , T f_t(x)=wq(x),w∈R^T q:R^d→{1,2,⋅⋅⋅,T} ft(x)=wq(x),wRTq:Rd1,2,,T
现在对训练误差部分的定义已经完成。那么对模型的复杂度应该怎么定义呢?
树的深度?最小叶子权重?叶子个数?叶子权重的平滑程度?等等有许多选项都可以描述该模型的复杂度。为了方便,现在用叶子的个数和叶子权重的平滑程度来描述模型的复杂度。可以得到下式:
Ω ( f t ) = γ T + 1 2 λ ∑ j = 1 T w j 2 Ω(f_t)=γT+\frac{1}{2}λ∑_{j=1}^Tw^2_j Ω(ft)=γT+21λj=1Twj2
上式中前一项用叶子的个数乘以一个收缩系数,后一项用L2范数来表示叶子权重的平滑程度。下图就是计算复杂度的一个示例:
在这里插入图片描述
 最后再增加一个定义,用 I j I_j Ij来表示第 j j j个叶子里的样本集合。也就是图四中,每第 i i i个圈,就用 I j I_j Ij来表示。
I j = { i ∣ q ( x i ) = j } I_j=\{i|q(x_i)=j\} Ij={iq(xi)=j}
好了,最后把优化函数重新按照每个叶子组合,并舍弃常数项:
O b j ( t ) ≈ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) = ∑ i = 1 n [ g i w q ( x i ) + 1 2 h i w q ( x ) 2 ] + γ T + 1 2 λ ∑ j = 1 T w j 2 = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ i ∈ I j h i + λ ) w j 2 ] + γ T Obj(t)≈∑_{i=1}^n[g_if_t(x_i)+\frac{1}{2}h_if^2_t(x_i)]+Ω(f_t)\\=∑_{i=1}^n[g_iw_{q(x_i)}+\frac{1}{2}h_iw^2_{q(x)}]+γT+\frac{1}{2}λ∑_{j=1}^Tw^2_j\\=∑_{j=1}^T[(∑_{i∈I_j}g_i)w_j+\frac{1}{2}(∑_{i∈I_j}h_i+λ)w^2_j]+γT Obj(t)i=1n[gift(xi)+21hift2(xi)]+Ω(ft)=i=1n[giwq(xi)+21hiwq(x)2]+γT+21λj=1Twj2=j=1T[(iIjgi)wj+21(iIjhi+λ)wj2]+γT
(这里最后两步是从遍历样本转换成了遍历叶子结点,其实就是换了个方法遍历所有样本,这样表达起来会更清晰)

求最优值

初中时所学的二次函数的最小值可以推广到矩阵函数里
m i n x G x + 1 2 H x 2 = − 1 2 G 2 H , H > 0 min_x Gx+\frac{1}{2}Hx^2=−\frac{1}{2}\frac{G^2}{H} , H>0 minxGx+21Hx2=21HG2,H>0
G j = ∑ i ∈ I j g i , H j = ∑ i ∈ I j h i G_j=∑_{i∈I_j}g_i, H_j=∑_{i∈I_j}h_i Gj=iIjgi,Hj=iIjhi,那么
O b j ( t ) = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ i ∈ I j h i + λ ) w j 2 ] + γ T = ∑ j = 1 T [ G j w j + 1 2 ( H j + λ ) w j 2 ] + γ T Obj(t)=∑_{j=1}^T[(∑_{i∈I_j}g_i)w_j+\frac{1}{2}(∑_{i∈I_j}h_i+λ)w^2_j]+γT\\=∑_{j=1}^T[G_jw_j+\frac{1}{2}(H_j+λ)w^2_j]+γT Obj(t)=j=1T[(iIjgi)wj+21(iIjhi+λ)wj2]+γT=j=1T[Gjwj+21(Hj+λ)wj2]+γT
因此,若假设我们的树的结构已经固定,就是 q ( x ) q(x) q(x)已经固定,那么
W j ∗ = − G j H j + λ W^∗_j=-\frac{G_j}{H_j+λ} Wj=Hj+λGj
O b j = − 1 2 ∑ j = 1 T G j H j + λ + γ T Obj=-\frac{1}{2}∑_{j=1}^T\frac{G_j}{H_j+λ}+γT Obj=21j=1THj+λGj+γT
为了形象地理解,下图就是一个示例:
在这里插入图片描述

求树结构

现在只要知道树的结构,就能得到一个该结构下的最好分数。可是树的结构应该怎么确定呢?没法用枚举,毕竟可能的状态基本属于无穷种。

这种情况,贪婪算法是个好方法。从树的深度为0开始,每一节点都遍历所有的特征。对于某个特征,先按照该特征里的值进行排序,然后线性扫描该特征来决定最好的分割点,最后在所有特征里选择分割后, G a i n Gain Gain最高的那个特征。
O b j s p l i t = − 1 2 [ G L 2 H L + λ + G R 2 H R + λ ] + γ T s p l i t Obj_{split}=-\frac{1}{2}[\frac{G^2_L}{H_L+λ}+\frac{G^2_{R}}{H_R+λ}]+γT_{split} Objsplit=21[HL+λGL2+HR+λGR2]+γTsplit
O b j n o s p l i t = 1 2 ( G L + G R ) 2 H L + H R + λ + γ T n o s p l i t Obj_{nosplit}=\frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+λ}+γT_{nosplit} Objnosplit=21HL+HR+λ(GL+GR)2+γTnosplit
G a i n = O b j n o s p l i t − O b j s p l i t = 1 2 [ G L 2 H L + λ + G R 2 H R + λ − ( G L + G R ) 2 H L + H R + λ ] − γ ( T s p l i t − T n o s p l i t ) Gain=Obj_{nosplit}-Obj_{split}\\=\frac{1}{2}[\frac{G^2_L}{H_L+λ}+\frac{G^2_{R}}{H_R+λ}-\frac{(G_L+G_R)^2}{H_L+H_R+λ}]-γ(T_{split}-T_{nosplit}) Gain=ObjnosplitObjsplit=21[HL+λGL2+HR+λGR2HL+HR+λ(GL+GR)2]γ(TsplitTnosplit)
上式中的L和R代表分开的二叉树的左边和右边
这时,就有两种后续。一种是当最好的分割的情况下,Gain为负时就停止树的生长,这样的话效率会比较高也简单,但是这样就放弃了未来可能会有更好的情况。另外一种就是一直分割到最大深度,然后进行修剪,递归得把划分叶子得到的Gain为负的收回。一般来说,后一种要好一些,于是我们采用后一种,完整的算法如下(没有写修剪)
在这里插入图片描述

算法复杂度

  1. 按照某特征里的值进行排序,复杂度是O(nlog n)
  2. 扫描一遍该特征所有值得到最优分割点,因为该层(兄弟统一考虑)一共有n个样本,所以复杂度是O(n)
  3. 一共有d个特征,所以对于一层的操作,复杂度是O(d(nlog n+n))=O(d nlog n)
  4. 该树的深度为k。所以总复杂度是O(k d nlog n)

注意事项

对某个节点的分割时,是需要按某特征的值排序,那么对于无序的类别变量,就需要进行one-hot化。不然的话,假设某特征有1,2,3三种变量。我们进行比较时,就会只比较左子树为1,2或者右子树为2,3,或者不分割,哪个更好。这样的话就没有考虑,左子树为1,3的分割。
  因为 G a i n Gain Gain于特征的值范围是无关的,它采用的是已经生成的树的结构与权重来计算的。所以不需要对特征进行归一化处理。

回顾和完善

  1. 每次循环增加一棵树
  2. 在每次循环的开始时计算 g i = ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) , h i = ∂ 2 y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) g_i=∂ŷ^{(t−1)} l(y_i,ŷ^{(t−1)}), h_i=∂^2ŷ^{(t−1)} l(y_i,ŷ^{(t−1)}) gi=y^(t1)l(yi,y^(t1)),hi=2y^(t1)l(yi,y^(t1))
  3. 采用贪婪算法生长树 f t ( x ) , O h j = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T f_t(x),Ohj=−\frac{1}{2}∑^T_{j=1}G^2_jH_j+λ+γT ft(x),Ohj=21j=1TGj2Hj+λ+γT
  4. f t ( x ) f_t(x) ft(x)加在模型之中 y ^ i ( t ) = y ^ i ( t − 1 ) + ϵ f t ( x ) ŷ^{(t)}_i=ŷ^{(t−1)}_i+ϵf_t(x) y^i(t)=y^i(t1)+ϵft(x)。注意,这里多了个 ϵ ϵ ϵ算子,这个是作为一个收缩系数,或者叫做步进。加上它的好处就是每一步我们都不是做一个完全的最优化,留下余地给未来的循环,这样能防止过拟合。
      以上就是贪婪算法。显而易见,这种算法并行的效率很低,我们常用的scikit-learn等用的就是这个算法。XGBoost在单线程版本的时候,用的也是这种算法。

近似算法

根据前面的讨论,我们可以发现我们的模型对特征中的值的范围不敏感,只对顺序敏感。举个例子,假设一个样本集中某特征出现的值有1,4,6,7,那么把它对应的换成1, 2,3,4。生成的模型里树的结构是一样的,只不过对应的判断条件变了,比如把小于6换成了小于3而已。这也给我们一个启示,我们完全可以用百分比作为基础来构造模型。
  我们用 D k = { ( x 1 k , h 1 ) , ( x 2 k , h 2 ) , ( x 3 k , h 3 ) , . . . , ( x n k , h n ) } D_k=\{(x_{1k},h_1),(x_{2k},h_2),(x_{3k},h_3),...,(x_{nk},h_n)\} Dk={(x1k,h1),(x2k,h2),(x3k,h3),...,(xnk,hn)}代表每个样本的第 k k k个特征和其对应的二阶梯度所组成的集合。那么我们现在就能用百分比来定义下面的这个排名函数 r k : R → [ 0 , 1 ] r_k:ℝ→[0,1] rk:R[0,1]
r k ( z ) = 1 ∑ ( x , h ) ∈ D k h ∑ ( x , h ) ∈ D k , x &lt; z h r_k(z)=\frac{1}{∑_{(x,h)∈D_k^h}}∑_{(x,h)∈D_k,x&lt;z}h rk(z)=(x,h)Dkh1(x,h)Dk,x<zh
上式表示的就是该特征的值小于z的样本所占总样本的比例。是我们就能用下面这个不等式来寻找分离点 { s k 1 , s k 2 , s k 3 , ⋅ , ⋅ , ⋅ , s k l } \{sk1,sk2,sk3,⋅,⋅,⋅,skl\} {sk1,sk2,sk3,,,,skl}
‖ r k ( s k , j ) − r k ( s k , j + 1 ) ‖ &lt; ϵ , m i n i x i k , m a x i x i k ‖r_k(s_k,j)−r_k(s_k,j+1)‖&lt;ϵ, \underset{i}{min} x_{ik}, \underset{i}{max} x_{ik} rk(sk,j)rk(sk,j+1)<ϵ,iminxik,imaxxik
上式中 ϵ ϵ ϵ表示的是一个近似比例,或者说一个扫描步进。就从最小值开始,每次增加 ϵ ∗ m i n i x i k , m a x i x i k ϵ∗\underset{i}{min} x_{ik}, \underset{i}{max} x_{ik} ϵiminxik,imaxxik作为分离点。然后在这些分离点中选择一个最大分数作为最后的分离点。
  很明显 D k D_k Dk有两种选择,或者说 m i n i x i k \underset{i}{min} x_{ik} iminxik m a x i x i k \underset{i}{max} x_{ik} imaxxik有两种选择。一种是一开始选好,然后每次分离都不变,也就是说是在总体样本里选最大值和最小值。另外一种就是每次分离后,在分离出来的样本里选,也就是在以前的所定义的 I j I_j Ij里选。很容易就觉得后面这种选择方式虽然会繁琐一点,但是效果会比前面的那种好。现在我们定义前面的那种里的叫做全局选择,后面的这种叫做局部选择。陈天奇做了一个比较,曲线如下图:
在这里插入图片描述
由此可见,局部选择的近似算法的确比全局选择的近似算法优秀的多,所得出的结果和贪婪算法几乎不相上下。

算法的伪代码如下图所示
在这里插入图片描述

一些进一步优化

在机器学习中,one-hot后,经常会得到的是稀疏矩阵,于是XGBoost也对这个作出了优化。还可以处理缺失值,毕竟这也是树模型一贯的优点。但这里就不细表了,毕竟太过于细节了。下一节我们就来看XGBoost这种强大的模型应该怎么使用吧。

使用

例程

官方例程如下:

import xgboost as xgb
# read in data
dtrain = xgb.DMatrix('demo/data/agaricus.txt.train')
dtest = xgb.DMatrix('demo/data/agaricus.txt.test')
# specify parameters via map
param = {'max_depth':2, 'eta':1, 'silent':1, 'objective':'binary:logistic' }
num_round = 2
bst = xgb.train(param, dtrain, num_round)
# make prediction
preds = bst.predict(dtest)

参数

很明显,上面重要的就是param,这个参数应该怎么设。在官网上有整整一个网页的说明。在这里我们只挑选一些重要常用的说一下。

与过拟合有关的参数

在机器学习中,欠拟合很少见,但是过拟合却是一个很常见的东西。XGBoost与其有关的参数也不少。

增加随机性
  • eta 这个就是学习步进,也就是上面中的 ϵ ϵ ϵ
  • subsample 这个就是随机森林的方式,每次不是取出全部样本,而是有放回地取出部分样本。有人把这个称为行抽取,subsample就表示抽取比例
  • colsample_bytree和colsample_bylevel 这个是模仿随机森林的方式,这是列抽取。colsample_bytree是每次准备构造一棵新树时,选取部分特征来构造,colsample_bytree就是抽取比例。colsample_bylevel表示的是每次分割节点时,抽取特征的比例。
  • max_delta_step 这个是构造树时,允许得到 f t ( x ) f_t(x) ft(x)的最大值。如果为0,表示无限制。也是为了后续构造树留出空间,和 e t a eta eta相似
控制模型复杂度
  • max_depth 树的最大深度
  • min_child_weight 如果一个节点的权重和小于这玩意,那就不分了
  • gamma每次分开一个节点后,造成的最小下降的分数。类似于上面的Gain
  • alpha和lambda就是目标函数里的表示模型复杂度中的L1范数和L2范数前面的系数
其他参数
  • booster 表示用哪种模型,一共有gbtree, gbline, dart三种选择。一般用gbtree。
  • nthread 并行线成数。如果不设置就是能采用的最大线程。
  • sketch_eps 这个就是近似算法里的ϵ。
  • scale_pos_weight 这个是针对二分类问题时,正负样例的数量差距过大。
    (以上是原作者的介绍,以上应用部分给出的是XGBoost原生接口的使用方法,后续我会补上sklearn接口的使用方法)
  • 11
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值