文章目录
背景
讲XGBoost之前,先引入一个实际问题,即预测一家人每个人玩游戏的意愿值:

如果我们用XGBoost解决这个问题,步骤是:首先要训练出来第一棵决策树, 预测了一下小男孩想玩游戏的意愿是2, 然后发现离标准答案差一些,再训练出第二棵决策树, 预测了一下小男孩想玩游戏的意愿是0.9, 最后两个相加就是最终的答案2.9。也就是说,XGBoost是把训练出来的弱分类结果进行累加当作最终的结论。
XGBoost的思想和GBDT有相似之处,比较大的不同就是目标函数的定义,即XGBoost聚焦与标准答案的残差。准确来说它是一种GBDT的工业级实现。其主要原理是在GBDT的基础上,在损失函数加入正则化部分,并且每一轮迭代对损失函数做二阶泰勒展开,加快对损失函数的优化速度。
XGBoost算法也是采用分步前向加性模型,只不过与GBDT不同,在每次迭代中生成弱学习器后不再需要计算一个系数。XGBoost 是由 k 个基模型组成的一个加法运算式:
y
^
i
=
∑
t
=
1
k
f
t
(
x
i
)
\hat y_i = \sum^k_{t=1}f_t(x_i)
y^i=t=1∑kft(xi)
其中,
f
t
f_t
ft是第t个模型,损失函数可以由真实值
y
i
y_i
yi和预测值
y
^
i
\hat y_i
y^i表示:
L
=
∑
t
=
1
N
l
(
y
i
,
y
^
i
)
L = \sum^N_{t=1}l(y_i, \hat y_i)
L=t=1∑Nl(yi,y^i)
其中N是样本个数。
定义损失函数
对于第m棵树而言,XGBoost的损失函数在GBDT损失函数中加入了如下的正则化:
Ω
(
f
t
)
=
γ
T
t
+
λ
2
∑
j
=
1
T
ω
j
2
\Omega(f_t)=\gamma T_t+\frac{\lambda}{2}\sum_{j=1}^{T}\omega_{j}^2
Ω(ft)=γTt+2λj=1∑Tωj2
T
t
T_t
Tt:叶子节点数,
w
j
w_j
wj:叶子上的节点权重,
γ
,
λ
\gamma,\lambda
γ,λ是超参数。当正则化为零时,目标回归到传统的梯度提升树。正则化起到了抑制模型复杂度的作用,从而避免了过拟合。
这里的
ω
j
\omega_{j}
ωj 和在GBDT里面使用的
c
j
c_{j}
cj 其实是一个意思,只是XGBoost论文里面使用
ω
\omega
ω 符号表示叶子区域的值,这里为了和论文保持一致。最终,XGBoost的目标(损失)函数可以表示为:
O
b
j
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
)
+
∑
t
=
1
k
Ω
(
f
t
)
Obj =\sum_{i=1}^n l(y_i,\hat y_i) + \sum_{t=1}^k \Omega(f_t)
Obj=i=1∑nl(yi,y^i)+t=1∑kΩ(ft)
我们要极小化上面这个损失函数,得到第决策树最优的所有
J
J
J个叶子节点对应的区域和每个叶子节点区域的最优解
ω
j
\omega_{j}
ωj。
接下来一步一步的对这个目标函数 O b j Obj Obj 进行化简。主要由以下四步组成:
(1)原始目标函数Obj
(2)原始目标函数Obj的泰勒展开
(3)具体化目标函数的泰勒展开细节
(4)求解目标函数中的 w j w_j wj,并带入目标函数,得到最终版目标函数
(1)原始目标函数Obj
XGBoost在损失函数优化方面做了一些优化,基于损失函数的二阶泰勒展开式来求解。XGBoost算法也是采用分步前向加性模型,设第
t
−
1
t-1
t−1步模型的输出为
y
^
i
t
−
1
\hat y_i^{t-1}
y^it−1,则第
t
t
t步模型的第
i
i
i个样本
x
i
x_i
xi 的预测为:
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^it=y^it−1+ft(xi)
f
t
(
x
i
)
f_t(x_i)
ft(xi)是需要新加入的模型。将
y
^
i
t
\hat y_i ^t
y^it 带入Obj,则有:
O
b
j
t
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
)
+
∑
i
=
1
t
Ω
(
f
t
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
−
1
+
f
t
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
t
)
\begin{aligned} Obj^{t} &=\sum_{i=1}^n l(y_i,\hat y_i^t) + \sum_{i=1}^t \Omega(f_t) \\ &=\sum_{i=1}^n l\left(y_i, \hat y_i^{t-1} + f_t(x_i)\right) + \sum_{i=1}^t \Omega(f_t) \end{aligned}
Objt=i=1∑nl(yi,y^it)+i=1∑tΩ(ft)=i=1∑nl(yi,y^it−1+ft(xi))+i=1∑tΩ(ft)
我们的目标是:求解当前的树
f
t
(
x
i
)
f_t(x_i)
ft(xi),使得Obj 最小。 也就是:新生成的决策树要不断地拟合残差。
(2)原始目标函数Obj的泰勒展开
将
y
^
i
t
−
1
\hat y_i^{t-1}
y^it−1 视为参数,将
f
t
(
x
i
)
f_t(x_i)
ft(xi)视为参数的增量,现在来看看这个损失函数的二阶泰勒展开式:
O
b
j
(
t
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
t
−
1
+
f
t
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
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
)
)
+
∑
i
=
1
t
Ω
(
f
t
)
\begin{aligned} Obj^{(t)} &=\sum_{i=1}^n l\left(y_i, \hat y_i^{t-1} + f_t(x_i)\right) + \sum_{i=1}^t \Omega(f_t)\\ & \approx \sum_{i=1}^{N}\left(l(y_i,\hat y_i^{t-1})+g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)\right)+\sum_{i=1}^t \Omega(f_t) \end{aligned}
Obj(t)=i=1∑nl(yi,y^it−1+ft(xi))+i=1∑tΩ(ft)≈i=1∑N(l(yi,y^it−1)+gift(xi)+21hift2(xi))+i=1∑tΩ(ft)
其中:
g
i
=
∂
l
(
y
i
,
y
^
i
t
−
1
)
∂
y
^
i
t
−
1
h
i
=
∂
2
l
(
y
i
,
y
^
i
t
−
1
)
∂
(
y
^
i
t
−
1
)
2
g_i =\frac{\partial l(y_i,\,\hat y_i^{t-1})}{\partial \hat y_i^{t-1}} \\ h_i =\frac{\partial^2 l(y_i,\,\hat y_i^{t-1})}{\partial (\hat y_i^{t-1})^2}
gi=∂y^it−1∂l(yi,y^it−1)hi=∂(y^it−1)2∂2l(yi,y^it−1)
由于
y
i
,
y
^
i
t
−
1
y_i,\hat y_i^{t-1}
yi,y^it−1 均为已知值,所以
l
(
y
i
,
y
^
i
t
−
1
)
l(y_i,\hat y_i^{t-1})
l(yi,y^it−1) 是常数。对优化目标函数的优化不会产生影响,所以舍去这一项:
O
b
j
(
t
)
≈
∑
i
=
1
N
(
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
t
)
\begin{aligned} Obj^{(t)} & \approx \sum_{i=1}^{N}\left(g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)\right)+\sum_{i=1}^t \Omega(f_t) \end{aligned}
Obj(t)≈i=1∑N(gift(xi)+21hift2(xi))+i=1∑tΩ(ft)
(3)具体化目标函数的泰勒展开细节
目标函数为:
O
b
j
(
t
)
≈
∑
i
=
1
N
(
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
)
+
∑
i
=
1
t
Ω
(
f
t
)
\begin{aligned} Obj^{(t)} & \approx \sum_{i=1}^{N}\left(g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)\right)+\sum_{i=1}^t \Omega(f_t) \end{aligned}
Obj(t)≈i=1∑N(gift(xi)+21hift2(xi))+i=1∑tΩ(ft)
这里的变量
f
t
(
x
i
)
f_t(x_i)
ft(xi) 表示训练样本
x
i
x_i
xi 经过决策树模型的预测值。把决策树模型定义成
f
t
(
x
i
)
=
w
q
(
x
)
f_t(x_i)=w_q(x)
ft(xi)=wq(x),其中
q
(
x
)
q(x)
q(x) 代表了该样本在哪个叶子节点上,
w
w
w 表示叶子结点的权重(也就是决策树的预测值:小男孩想玩游戏的意愿 2),所以
w
q
(
x
)
w_q(x)
wq(x)代表每个样本的预测值。
若定义
I
j
=
{
i
∣
q
(
x
i
)
=
j
}
I_j=\{i|q(x_i)=j\}
Ij={i∣q(xi)=j}为第
j
j
j个叶子节点的样本集合,经过
t
t
t轮迭代完毕以后每个决策树(弱学习器)的第
j
j
j个叶子节点的取值(也就是该叶子节点对应的预测值)最终会是同一个值
ω
j
\omega_{j}
ωj。由于一个叶子结点有多个样本,所以每一个叶子结点的一阶导数之和为
∑
i
∈
I
j
g
i
\sum _{i \in I_j}g_i
∑i∈Ijgi,同理有二阶导之和
∑
i
∈
I
j
h
i
\sum _{i \in I_j}h_i
∑i∈Ijhi 。因此,可以对损失函数继续化简:
O
b
j
(
t
)
≈
∑
i
=
1
N
(
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
)
+
Ω
(
f
t
)
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
i
)
w
j
+
1
2
(
∑
i
∈
I
j
h
i
)
w
j
2
]
+
γ
T
+
λ
2
∑
j
=
1
J
ω
j
2
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
i
)
w
j
+
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
w
j
2
]
+
γ
T
\begin{aligned} Obj^{(t)} & \approx \sum_{i=1}^{N}\left(g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)\right)+ \Omega(f_t) \\ &=\sum_{j=1}^{T}\left[(\sum _{i \in I_j}g_i)w_{j}+\frac{1}{2}(\sum _{i \in I_j}h_i)w_{j}^2\right] + \gamma T+\frac{\lambda}{2}\sum_{j=1}^{J}\omega_{j}^2 \\ &= \sum_{j=1}^{T}\left[(\sum _{i \in I_j}g_i)w_{j}+\frac{1}{2}(\sum _{i \in I_j}h_i + \lambda)w_{j}^2\right] + \gamma T \end{aligned}
Obj(t)≈i=1∑N(gift(xi)+21hift2(xi))+Ω(ft)=j=1∑T⎣⎡(i∈Ij∑gi)wj+21(i∈Ij∑hi)wj2⎦⎤+γT+2λj=1∑Jωj2=j=1∑T⎣⎡(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2⎦⎤+γT
同样为了方便表示,记:
G
j
=
∑
i
∈
I
j
g
j
H
j
=
∑
i
∈
I
j
h
j
G_{j}=\sum_{i \in I_{j}} g_{j}\\ H_{j}=\sum_{i \in I_{j}} h_{j}
Gj=i∈Ij∑gjHj=i∈Ij∑hj
于是有:
O
b
j
(
t
)
=
∑
j
=
1
T
[
G
j
w
j
+
1
2
(
H
j
+
λ
)
w
j
2
]
+
γ
T
Obj^{(t)}= \sum_{j=1}^{T}\left[G_jw_{j}+\frac{1}{2}(H_j + \lambda)w_{j}^2\right] + \gamma T
Obj(t)=j=1∑T[Gjwj+21(Hj+λ)wj2]+γT
好,现在简化的损失函数有了,现在又多了两个问题。
(4)求解目标函数中的 w j w_j wj
第一个问题,前面我们假设树的第 j 个叶子节点的输出值为 ω j \omega_{j} ωj,这个数值具体是多少,怎么求?
因为
G
j
、
H
j
G_{j}、H_{j}
Gj、Hj 求解均为已知值。只有最后一颗树的叶子节点
w
j
w_j
wj 的值是不确定的。要求得
w
j
w_j
wj 使得目标函数
O
b
j
Obj
Obj 极小化。所以将目标函数对
w
j
w_j
wj 求一阶导,令其等于0,即:
w
j
∗
=
−
G
j
H
j
+
λ
w_j^* = -\frac{G_j}{H_j+\lambda}
wj∗=−Hj+λGj
将上面的式子代入目标函数,又能进一步化简得到:
O
b
j
=
−
1
2
∑
j
=
1
T
[
G
j
2
H
j
+
λ
]
+
γ
T
Obj =-\frac{1}{2}\sum_{j=1}^{T}[\frac{G_{j}^2}{H_{j}+\lambda}]+\gamma T
Obj=−21j=1∑T[Hj+λGj2]+γT
式子中的G和H,就需要明确的给出损失函数才能求得。给定不同的树,
O
b
j
Obj
Obj的值也会不同,也就是说,我们可以用
O
b
j
Obj
Obj 来给一颗树打分,作用类似于CART算法中的Gini值。
有了这个,我们就知道这棵树建的好不好了。回到预测每个人玩游戏意愿预测的例子:

假设建了右边的那棵树,那么每个样本都对应到了叶子节点上去,每一个样本都会对应一个g和h, 那么我们遍历叶子节点,就会得到每个叶节点的G和H,然后累加就可以得到这棵树的结构分数。
注意两个细节:
(1)每个样本的 h i , g i h_i,g_i hi,gi 之间没有关系,可以并行计算。加快了训练速度。
(2) h i , g i h_i,g_i hi,gi 的计算要求损失函数二阶可导。
最优切分点算法
第二个问题,如何建立一颗树呢?XGBoost采用二叉树, 用了贪心策略。在开始的时候, 全部样本在一个叶子节点上, 然后叶子节点不断通过二分裂,逐渐生成一棵树。
叶节点分裂成树的过程中最关键的是应该在哪个特征的哪个点上进行分裂,也就是寻找最优切分点的过程。对XGBoost算法而言,分列前的目标函数可以写为:
O
b
j
1
=
−
1
2
[
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
]
+
γ
Obj_1 =-\frac{1}{2}[\frac{(G_{L}+G_R)^2}{H_{L}+H_{R+}\lambda}]+\gamma
Obj1=−21[HL+HR+λ(GL+GR)2]+γ
令
I
L
,
I
R
I_L,\,I_R
IL,IR分别表示分裂点之后加入左右叶子节点的样本集合,且有
G
L
=
∑
i
∈
I
L
g
i
,
G
R
=
∑
i
∈
I
R
g
i
,
H
L
=
∑
i
∈
I
L
h
i
,
H
R
=
∑
i
∈
I
R
h
i
G_L=\sum_{i\in I_L}g_{i},G_R=\sum_{i\in I_R}g_{i},H_L=\sum_{i\in I_L}h_{i},H_R=\sum_{i\in I_R}h_{i}
GL=i∈IL∑gi,GR=i∈IR∑gi,HL=i∈IL∑hi,HR=i∈IR∑hi,则分裂后的目标函数可以写为:
O
b
j
2
=
−
1
2
[
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
]
+
2
γ
Obj_2 =-\frac{1}{2}[\frac{G_{L}^2}{H_{L}+ \lambda} +\frac{G_{R}^2}{H_{R}+ \lambda} ] +2\gamma
Obj2=−21[HL+λGL2+HR+λGR2]+2γ
则分裂后的收益(分裂前后损失的差)表示为
O
b
j
1
−
O
b
j
2
Obj_1 - Obj_2
Obj1−Obj2:
G
a
i
n
=
1
2
[
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
−
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
]
−
γ
Gain = \frac{1}{2}\left[\frac{G_{L}^2}{H_{L}+ \lambda} +\frac{G_{R}^2}{H_{R}+ \lambda}-\frac{(G_{L}+G_R)^2}{H_{L}+H_{R+}\lambda}\right] - \gamma
Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
基于上面的描述,得到最优切分点的划分算法:
- 从深度为 0 的树开始,对每个叶节点枚举所有的可用特征;
- 针对每个特征,把属于该节点的训练样本根据该特征值进行升序排列,通过线性扫描的方式来决定该特征的最佳分裂点,并记录该特征的分裂收益;(这个过程每个特征的收益计算是可以并行计算的)
- 选择收益最大的特征作为分裂特征,用该特征的最佳分裂点作为分裂位置,在该节点上分裂出左右两个新的叶节点,并为每个新节点关联对应的样本集
- 回到第 1 步,递归执行到满足特定条件为止
如下图所示:

回到玩游戏的那个例子,假设我有每个人有性别,年龄,兴趣等几个特征,我想用XGBoost建立一棵树预测玩游戏的意愿值。首先,五个人都聚集在根节点上,现在就考虑根节点分叉,我们就遍历每个特征,对于当前的特征,我们要去寻找最优切分点以及带来的最大收益。比如当前特征是年龄,我们需要知道两点:
- 按照年龄分是否有效,即,是否减少了损失值 ?
- 如果真的可以分,特征收益比较大, 那么我们从哪个年龄点分开呢?
对于这两个问题,首先是要对年龄排序,5个人可以找到4个切分点。对于每个切分点,计算 H L , H R , G L , G R H_L,H_R,G_L,G_R HL,HR,GL,GR,取出使得$Gain $最大的划分点a:

然后哪个最大,就是年龄特征的最优切分点,而最大值就是年龄这个特征的最大信息收益。
遍历完所有特征后,我们就可以确定应该在哪个特征的哪个点进行切分。对切分出来的两个节点,递归地调用这个过程,我们就能获得一个相对较好的树结构, 有了树结构就比较容易找最优的叶子节点,这样就能对上面的样本进行预测了。当然,特征与特征之间的收益计算是互不影响的,所以这个遍历特征的过程其实可以并行运行。
要注意的是XGBoost在切分的时候就已经考虑了树的复杂度 λ ,所以,它不需要进行单独的剪枝操作。
基于分桶的划分策略
当数据量大,特征又多的时候,数据划分的计算量太大了。于是作者采用近似分割的方式(可以理解为分割点分桶的思路),选出一些候选的分裂点,然后再遍历这些较少的分裂点来找到最佳分裂点。
具体而言,作者这里采用了一种对loss的影响权重的等值percentiles(百分比分位数)划分算法(Weight Quantile Sketch)。也就是说,进行候选点选取的时候,考虑的是想让loss在左右子树上分布的均匀一些,而不是样本数量的均匀。
我们知道每个样本对于降低loss的贡献可能不-样,所以我们可以根据这个贡献程度给每个样本加上权值,这里用 hi 表示(即 hi 表示每个样本对降低 Ioss 的贡献程度) 。这样我们就可以画出下面这个图来,第一行表示每个样本在某个特征上取值情况,第二行代表每个样本对于降低loss的贡献程度:

划分的时候,我们是根据这个 hi 的取值进行分箱的,就是尽量的每个桶里面的 hi 分布相对均匀些,不让某些节点重要的样本多而且还大。比如最右边的子树,只要一 个权重特别大的样本就够了,而左边的子树,样本权值偏低,所以咱可以多给些样本,这样loss在树结构中才比较均匀,这样每个bin的贡献度都是0.6了,比较均匀。
现在又用了两个问题:
- hi 是啥,它为啥就能代表样本对降低loss的贡献程度?
- 这个bin是怎么分的,为啥是0.6一个箱?
对于第一个问题,hi 其实就是前面说到的二阶导,也就是下面损失函数中的
h
i
h_{i}
hi:
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ Obj^{(t)} …
对于上面的式子,可以凑完全平方项,并化简为如下的形式(这里不展开讲,有兴趣看看参考文章1):
O
b
j
≈
∑
i
=
1
N
1
2
h
i
(
f
t
(
x
i
)
−
(
−
g
i
h
i
)
)
2
+
Ω
(
f
t
)
+
C
o
n
s
t
a
n
t
Obj \approx \sum_{i=1}^{N} \frac{1}{2}h_{i} \left( f_t(x_i)-(-\frac{g_{i}}{h_{i}}) \right)^2 +\Omega(f_t)+Constant
Obj≈i=1∑N21hi(ft(xi)−(−higi))2+Ω(ft)+Constant
也就是
h
i
h_{i}
hi是表示残差
−
g
i
h
i
-\frac{g_{i}}{h_{i}}
−higi的重要程度,即每个样本对loss减少的贡献程度。
对于第二个如何分箱的问题,定义数据集
D
k
=
{
(
x
1
k
,
h
1
)
,
(
x
2
k
,
h
2
)
,
.
.
.
,
(
x
n
k
,
h
n
)
}
D_k=\{(x_{1k}, h_1), (x_{2k}, h_2),...,(x_{nk}, h_n)\}
Dk={(x1k,h1),(x2k,h2),...,(xnk,hn)}代表每个训练样本第k个特征的取值和二阶梯度值,定义一个排名函数
r
k
(
z
)
=
1
∑
(
x
,
h
)
∈
D
k
h
∑
(
x
,
h
)
∈
D
k
,
x
<
z
h
r_k(z) = \frac{1}{\sum_{(x,h)\in D_k} h}\; \sum_{(x,h)\in D_k, x<z}h
rk(z)=∑(x,h)∈Dkh1(x,h)∈Dk,x<z∑h
上面的式子中,z表示特征值小于z的样本的贡献度占比。

对于上图的第一个划分点,
r
k
(
z
)
=
1
3
r_k(z)=\frac{1}{3}
rk(z)=31。加入有
l
l
l个划分点,我们用
{
s
k
1
,
s
k
2
,
.
.
,
s
k
l
}
\{s_{k1}, s_{k2},..,s_{kl}\}
{sk1,sk2,..,skl}表示,目标是让相邻的划分点的贡献度都差不多,即指定ε,使得:
∣
r
k
(
s
k
j
)
−
r
k
(
s
k
j
+
1
)
∣
<
ε
|r_k(s_{kj})-r_k(s_{kj+1})|<ε
∣rk(skj)−rk(skj+1)∣<ε
例如,设定
ε
=
1
3
ε=\frac{1}{3}
ε=31,那么划分点就是1/3分位点,2/3分位点。上面样本贡献度总和是1.8, 那么每个箱贡献度就是 1.8 * 1/3 = 0.6。
上面这些公式看起来挺复杂,可计算起来很简单,就是计算一下总的贡献度, 然后指定ε,两者相乘得到每个桶的贡献度进行分桶即可。这样我们就可以确定合理的候选切分点,然后进行分箱了。
过程如下图所示:

在树生成的过程中,每一个特征的划分点
{
s
k
1
,
s
k
2
,
.
.
,
s
k
l
}
\{s_{k1}, s_{k2},..,s_{kl}\}
{sk1,sk2,..,skl}是否会发生改变呢?有如下两种情况:
- 全局近似:在构造树的初始阶段提出所有的候选分裂点,然后对各个层次采用相同的候选分裂点 { s k 1 , s k 2 , . . , s k l } \{s_{k1}, s_{k2},..,s_{kl}\} {sk1,sk2,..,skl}。候选分裂点不更新,每次的候选分裂点数目多。
- 局部近似:在每次分裂都重新提出候选分裂点,对层次较深的树更适合,候选分裂点的数目不需要太多
总结下,前面那一部分是围绕着如何建立一棵树进行的,即采用贪心的方式从根节点开始一层层的建立树结构(每一层争取最优),然后就是建树过程中一个关键的问题:如何寻找最优切分点,给出了最优切分点算法,基于这个算法就可以建立树了。
后面这一部分是一个优化的过程,提出了一种Weight Quantile Sketch的算法,这个算法可以将原来的分割点进行分桶,然后找到合适的候选分裂点,这样可以减少遍历时尝试的分裂点的数量,是XGBoost相比于GBDT做出的切分点优化策略,现在知道为啥XGBoost要快了吧,因为XGBoost寻找切分点的时候不用遍历所有的,而是只看候选点就可以了。而且在特征上,XGBoost是可以并行处理的。这样XGBoost的建树过程及优化策略基本上就是这些了。
正则化
总体上讲,XGBoost抵抗过拟合的方法与GBDT类似,主要也是从以下几方面考虑:
模型复杂度
正则化项的表达式:
Ω
(
f
t
)
=
γ
T
t
+
λ
2
∑
j
=
1
T
ω
j
2
\Omega(f_t)=\gamma T_t+\frac{\lambda}{2}\sum_{j=1}^{T}\omega_{j}^2
Ω(ft)=γTt+2λj=1∑Tωj2
其中,
T
t
T_t
Tt 是叶节点个数,
ω
j
\omega_{j}
ωj是棵树第j个叶节点的最优值。第一项是用于对过多的叶节点个数进行惩罚,第二项对过于大的最优值(预测值)进行惩罚。
Shrinkage
Shrinkage的思想是,每次走一小步逼近最优值,比每次迈一大步很快逼近结果的方式更容易避免过拟合。因为这个过程不完全信任每一个棵残差树,它认为每棵树只学到了真理的一小部分,累加的时候只累加一小部分,通过多学几棵树弥补不足。用方程来看更清晰,即给每棵数的输出结果乘上一个步长
α
\alpha
α(learning rate),于是原来的弱学习器迭代:
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^it=y^it−1+ft(xi)
由于Shrinkage思想的加入,变成了:
y
^
i
t
=
y
^
i
t
−
1
+
α
f
t
(
x
i
)
\hat y_i ^t = \hat y_i^{t-1} + \alpha f_t(x_i)
y^it=y^it−1+αft(xi)
其中,
α
\alpha
α的取值范围为(0,1]。对于同样的训练集学习效果,较小
α
\alpha
α的意味着需要更多的弱学习器的迭代次数。通常我们用步长
α
\alpha
α和迭代最大次数一起决定算法的拟合效果。
特征采样和样本采样
XGBoost借鉴RF的思想,对特征和特征进行采样以达到降低过拟合的目的,根据用户反馈,特征采样比起样本采样效果更优(特征采样率:[0.5,0.8])。当然,XGBoost同时支持以上两种降低过拟合的采样方式。
Early Stopping
对于每一次分离后的增益,即前面的:
G
a
i
n
=
1
2
[
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
−
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
]
−
γ
Gain = \frac{1}{2}\left[\frac{G_{L}^2}{H_{L}+ \lambda} +\frac{G_{R}^2}{H_{R}+ \lambda}-\frac{(G_{L}+G_R)^2}{H_{L}+H_{R+}\lambda}\right] - \gamma
Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
在sklearn接口当中,如果
G
a
i
n
Gain
Gain出现负值,则提前停止;但是,被提前终止掉的分裂可能其后续的分裂会带来好处。在XBGoost原生接口当中是采用过后剪枝策略:将树分裂到最大深度,然后再基于上述增益计算剪枝。在具体实现时,还有learning rate等其它参数一起控制,给
G
a
i
n
Gain
Gain出现负值的后续轮留机会。
缺失值处理
XGBoost支持缺失值的处理,没有简单的假设缺失值一定进入左子树还是右子树,而是尝试通过枚举所有缺失值在当前节点是进入左子树,还是进入右子树更优,来决定一个处理缺失值默认的方向,这样处理起来更加的灵活和合理。如下图所示:

优缺点
xgboost相比于GBDT有以下的优点:
- 精度更高:GBDT只用到一阶泰勒, 而xgboost对损失函数进行了二阶泰勒展开, 一方面为了增加精度, 另一方面也为了能够自定义损失函数,二阶泰勒展开可以近似大量损失函数
- 灵活性更强:GBDT以CART作为基分类器,而Xgboost不仅支持CART,还支持线性分类器,另外,Xgboost支持自定义损失函数,只要损失函数有一二阶导数。
- 正则化:xgboost在目标函数中加入了正则,用于控制模型的复杂度。有助于降低模型方差,防止过拟合。正则项里包含了树的叶子节点个数,叶子节点权重的L2范式。
- Shrinkage(缩减):相当于学习速率。这个主要是为了削弱每棵树的影响,让后面有更大的学习空间,学习过程更加的平缓
- 列抽样:这个就是在建树的时候,不用遍历所有的特征了,可以进行抽样,一方面简化了计算,另一方面也有助于降低过拟合
- 缺失值处理:这个是xgboost的稀疏感知算法,加快了节点分裂的速度
- 并行化操作:块结构可以很好的支持并行计算
缺点:
- 时间复杂度高:虽然利用了预排序和近似算法可以降低寻找最优分裂点的计算量,但在节点分裂过程中仍需要遍历整个数据集。
- 空间复杂度高:预排序过程的空间复杂度过高,不仅需要存储特征值,还需要存储特征对应样本梯度统计值的索引,相当于消耗了两倍的内存。
总结
简单的回顾一下本文内容:
XGBoost是好多弱分类器的集成,训练弱分类器的策略就是尽量的减小残差,使得答案越来越接近正确答案。XGBoost的精髓部分是目标函数的Taylor化简,这样就引入了损失函数的一阶和二阶导数。然后又把样本的遍历转成了对叶子节点的遍历,得到了最终的目标函数。这个函数就是衡量一棵树好坏的标准。在建树过程中,XGBoost采用了贪心策略,并且对寻找分割点也进行了优化。基于这个,才有了后面的最优点切分建立一棵树的过程。XGBoost训练的时候,是通过加法进行训练,也就是每一次只训练一棵树出来, 最后的预测结果是所有树的加和表示。
参考文章:
XGBoost是基于梯度提升决策树的机器学习算法,它在GBDT基础上进行了优化,特别是目标函数的定义和正则化处理。通过损失函数的二阶泰勒展开,XGBoost可以快速优化模型,并通过正则项控制模型复杂度,防止过拟合。此外,XGBoost还采用了特征采样、样本采样、早停策略等手段来提高效率和泛化能力。
3279

被折叠的 条评论
为什么被折叠?



