0 引言
XGBoost是在GBDT、RGF等算法的基础上改进而来,其性能优异,已经在各大竞赛中广泛使用,尤其这是陈天奇大神主导的研究成果,不管是算法原理还是算法的实现都值得仔细研读。
1 建模
给定一个数据集,该数据集中有n个样例,每个样例有m个feature和1个output,用集合表达如下:
D
=
{
(
X
i
,
y
i
)
∣
x
i
∈
R
m
,
y
i
∈
R
,
i
∈
N
+
}
D=\{(X_i,y_i)|x_i \in \mathbb{R}^m,y_i\in \mathbb{R},i\in\mathbb{N}^+\}
D={(Xi,yi)∣xi∈Rm,yi∈R,i∈N+}
基于该数据集做预测我们首先需要建立合适的预测模型,对于分类和回归问题常用的方法就是将训练任务转化成优化问题的求解;xgboost算法也不例外。
xgboost算法使用了模型集成,确切地说就是使用K个决策树模型依据输入
x
i
\bold x_i
xi输出结果,将这K个输出结果求和后得到
y
i
^
\widehat {y_i}
yi
,,用公式表达如下:
(1)
y
^
i
=
ϕ
(
x
i
)
=
∑
k
=
1
K
f
k
(
x
i
)
,
f
k
∈
F
\widehat{y}_i=\phi(\bold x_i)=\sum_{k=1}^Kf_k(\bold x_i),f_k \in \mathcal{F} \tag{1}
y
i=ϕ(xi)=k=1∑Kfk(xi),fk∈F(1)
其中,
- F = { f ( x ) = w q ( x ) } ( q : R m → T , w ∈ R T ) \mathcal{F}=\{ f(x)=w_{q(x)}\} (q:\mathbb{R}^m\to T,w\in \mathbb{R}^T) F={f(x)=wq(x)}(q:Rm→T,w∈RT)是回归树集合,常见的回归树如CART等;
- q代表每一个回归树的结构;
- T T T是回归树的叶子数量;
- f k f_k fk代表第k个回归树的输入输出函数关系,每个 f k f_k fk与第k个回归树的结构q和叶子节点权重w相对应;
- w i w_i wi表示第i个叶子节点的score;
公式1的含义也可以用如下图表示:
xgboost模型为了学习出每一个
f
k
f_k
fk函数,建立了如下面公式2的带正则化项的目标函数,优化的方向是最小化该目标函数:
(2)
L
(
ϕ
)
=
∑
l
(
y
i
,
y
i
^
)
+
∑
k
Ω
(
f
k
)
,
w
h
e
r
e
Ω
(
f
)
=
γ
T
+
1
2
λ
∥
w
∥
2
L(\phi)=\sum l(y_i, \widehat {y_i})+\sum_{k}\Omega(f_k) ,\\ where\ \ \Omega(f)=\gamma T+\frac{1}{2}\lambda \lVert w\rVert^2 \tag{2}
L(ϕ)=∑l(yi,yi
)+k∑Ω(fk),where Ω(f)=γT+21λ∥w∥2(2)
其中,
- l l l是可微的凸函数,代表预测值 y i ^ \widehat {y_i} yi 和实际值 y i y_i yi之间的差距;
- Ω ( f k ) \Omega(f_k) Ω(fk)是每一个模型 f k f_k fk的复杂度的惩罚项,该惩罚项用来平滑模型最终学习到的权重防止过拟合和限制叶子节点总数,因为该惩罚项不仅包含了L2正则化项,还包含了对叶子节点数的惩罚;
根据Boosting算法,对于前面的公式1进行迭代:
y
^
i
=
ϕ
(
x
i
)
=
∑
k
=
1
K
f
k
(
x
i
)
,
f
k
∈
F
\widehat{y}_i=\phi(\bold x_i)=\sum_{k=1}^Kf_k(\bold x_i),f_k \in \mathcal{F}
y
i=ϕ(xi)=k=1∑Kfk(xi),fk∈F
考虑到迭代次数t时,该公式可以写成如下形式:
y
^
i
(
k
)
=
ϕ
(
k
)
(
x
i
)
=
∑
k
=
1
t
f
k
(
x
i
)
,
f
k
∈
F
\widehat{y}_i^{(k)}=\phi^{(k)}(\bold x_i)=\sum_{k=1}^tf_k(\bold x_i),f_k \in \mathcal{F}
y
i(k)=ϕ(k)(xi)=k=1∑tfk(xi),fk∈F
第0次迭代,初始化:
y
^
i
(
0
)
=
0
\widehat{y}_i^{(0)}=0
y
i(0)=0
第1次迭代,
y
^
i
(
1
)
=
f
1
(
x
i
)
\widehat{y}_i^{(1)}=f_1(\bold x_i)
y
i(1)=f1(xi)
第2次迭代,
y
^
i
(
2
)
=
f
1
(
x
i
)
+
f
2
(
x
i
)
=
y
^
i
(
1
)
+
f
2
(
x
i
)
\widehat{y}_i^{(2)}=f_1(\bold x_i)+f_2(\bold x_i)=\widehat{y}_i^{(1)}+f_2(\bold x_i)
y
i(2)=f1(xi)+f2(xi)=y
i(1)+f2(xi)
第t次迭代,
(3)
y
^
i
(
t
)
=
∑
k
=
1
t
f
k
(
x
i
)
=
y
^
i
(
t
−
1
)
+
f
t
(
x
i
)
\widehat{y}_i^{(t)}=\sum_{k=1}^tf_k(\bold x_i)=\widehat{y}_i^{(t-1)}+f_t(\bold x_i) \tag{3}
y
i(t)=k=1∑tfk(xi)=y
i(t−1)+ft(xi)(3)
我们将公式3带入公式2可以进一步改进目标函数,在第t次迭代的目标函数:
(4)
L
(
ϕ
)
(
t
)
=
∑
l
(
y
i
(
t
)
,
y
^
i
(
t
−
1
)
+
f
t
(
x
i
)
)
+
Ω
(
f
t
)
,
w
h
e
r
e
Ω
(
f
)
=
γ
T
+
1
2
λ
∥
w
∥
2
L(\phi)^{(t)}=\sum l(y_i^{(t)},\widehat{y}_i^{(t-1)}+f_t(\bold x_i))+\Omega(f_t) ,\\ where\ \ \Omega(f)=\gamma T+\frac{1}{2}\lambda \lVert w\rVert^2 \tag{4}
L(ϕ)(t)=∑l(yi(t),y
i(t−1)+ft(xi))+Ω(ft),where Ω(f)=γT+21λ∥w∥2(4)
值得一提的是 ,更早出现的GBDT算法就是一个Boosting算法,它由多个回归树(CART)组合起来进行预测的模型。
但是GBDT有不足的地方:
- 每一次的迭代中的唯一目标就是学习出 一颗决策树,从而将单棵树的学习与整个森林的学习分隔开,没有很好地利用决策树本身的性质。新增的决策树只改变了本身的参数而没有改变老树的参数,实际上相当于只做了一个局部的搜索;
- 为了避免训练出来过拟合的模型,GBDT需要控制学习步长 s,这种情况可能会造成需要无穷多棵决策树才能很好的完成拟合;
现在再回到我们的目标函数处理中,结合式子3和式子4我们可以发现目标函数在迭代计算中
f
t
(
x
i
)
f_t(x_i)
ft(xi)可以贪心地加入到其中。由于二阶导数能够更快的优化目标函数,作者将损失函数
l
(
y
i
(
t
)
,
y
^
i
(
t
−
1
)
+
f
t
(
x
i
)
)
l(y_i^{(t)},\widehat{y}_i^{(t-1)}+f_t(x_i))
l(yi(t),y
i(t−1)+ft(xi))分解为二阶泰勒级数使目标函数变成了如下形式:
(5)
L
(
ϕ
)
(
t
)
≃
∑
[
l
(
y
i
(
t
)
,
y
^
i
(
t
−
1
)
)
+
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
Ω
(
f
t
)
,
w
h
e
r
e
Ω
(
f
)
=
γ
T
+
1
2
λ
∥
w
∥
2
,
g
i
=
∂
y
^
(
t
−
1
)
l
(
y
i
,
y
^
(
t
−
1
)
)
,
h
i
=
∂
y
^
(
t
−
1
)
2
l
(
y
i
,
y
^
(
t
−
1
)
)
L(\phi)^{(t)} \simeq \sum [l(y_i^{(t)},\widehat{y}_i^{(t-1)})+g_i f_t(\bold x_i)+\frac{1}{2}h_i f_t^2(\bold x_i)]+\Omega(f_t) ,\\ where\ \ \Omega(f)=\gamma T+\frac{1}{2}\lambda \lVert w\rVert^2 , \\ g_i = \partial _{\widehat{y}^{(t-1)}} l(y_i,\widehat{y}^{(t-1)}) ,\\ h_i = \partial _{\widehat{y}^{(t-1)}}^2 l(y_i,\widehat{y}^{(t-1)}) \tag{5}
L(ϕ)(t)≃∑[l(yi(t),y
i(t−1))+gift(xi)+21hift2(xi)]+Ω(ft),where Ω(f)=γT+21λ∥w∥2,gi=∂y
(t−1)l(yi,y
(t−1)),hi=∂y
(t−1)2l(yi,y
(t−1))(5)
其中,
g
i
,
h
i
g_i, h_i
gi,hi分别是损失函数
l
(
y
i
(
t
)
,
y
^
i
(
t
−
1
)
+
f
t
(
x
i
)
)
l(y_i^{(t)},\widehat{y}_i^{(t-1)}+f_t(x_i))
l(yi(t),y
i(t−1)+ft(xi))关于
f
t
(
x
i
)
f_t(x_i)
ft(xi)的一阶导数和二阶导数;
从公式4到公式5的推导看起来不是那么好理解,我们先看二阶泰勒展开式的原始形式,即当
Δ
x
\Delta x
Δx足够小时,有以下近似等式成立:
f
(
x
+
Δ
x
)
≃
f
(
x
)
+
f
′
(
x
)
Δ
x
+
f
′
′
(
x
)
Δ
x
2
f(x+\Delta x) \simeq f(x)+f^{'}(x) \Delta x+f^{''}(x) \Delta x^2
f(x+Δx)≃f(x)+f′(x)Δx+f′′(x)Δx2
如何应用这个等式到公式5的呢?
我们可以把x看作
y
^
i
(
t
−
1
)
\widehat{y}_i^{(t-1)}
y
i(t−1),把
Δ
x
\Delta x
Δx看作
f
t
(
x
i
)
f_t(\bold x_i)
ft(xi),把
f
(
x
+
Δ
x
)
f(x+\Delta x)
f(x+Δx)看作损失函数
l
(
y
i
(
t
)
,
y
^
i
(
t
−
1
)
+
f
t
(
x
i
)
)
l(y_i^{(t)},\widehat{y}_i^{(t-1)}+f_t(\bold x_i))
l(yi(t),y
i(t−1)+ft(xi)),带入二阶泰勒展开式就能得到上面的公式5了。
由于在第t次迭代时
y
^
i
(
t
−
1
)
\widehat{y}_i^{(t-1)}
y
i(t−1)的值已经在t-1次迭代计算出来了,所以
l
(
y
i
(
t
)
,
y
^
i
(
t
−
1
)
)
l(y_i^{(t)},\widehat{y}_i^{(t-1)})
l(yi(t),y
i(t−1))是已知常数项。将目标函数中的常数项移除,优化方向没有改变,第t次迭代的目标函数简化为如下形式:
(6)
L
(
ϕ
)
(
t
)
=
∑
[
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
Ω
(
f
t
)
=
∑
[
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
γ
T
+
1
2
λ
∥
w
∥
2
L(\phi)^{(t)} = \sum [g_i f_t(\bold x_i)+\frac{1}{2}h_i f_t^2(\bold x_i)]+\Omega(f_t) \tag{6} \\ = \sum [g_i f_t(\bold x_i)+\frac{1}{2}h_i f_t^2(\bold x_i)]+\gamma T+\frac{1}{2}\lambda \lVert w\rVert^2
L(ϕ)(t)=∑[gift(xi)+21hift2(xi)]+Ω(ft)=∑[gift(xi)+21hift2(xi)]+γT+21λ∥w∥2(6)
那么,我们如何评价单个决策树结构的优劣呢?
单个决策树结构的评价方法如下图所示:
定义
I
j
=
{
i
∣
q
(
x
i
)
=
j
}
I_j=\{ i|q(x_i)=j\}
Ij={i∣q(xi)=j} 为分到叶子结点
j
j
j的样本数据集合;
定义
w
i
=
f
(
x
i
)
w_i=f(\bold x_i)
wi=f(xi)
第t次迭代的目标函数(6)可以进一步归纳为如下形式:
(7)
L
(
ϕ
)
(
t
)
=
∑
i
=
1
n
[
g
i
f
t
(
x
i
)
+
1
2
h
i
f
t
2
(
x
i
)
]
+
γ
T
+
1
2
λ
∥
w
j
∥
2
=
∑
j
=
1
T
[
∑
i
∈
I
j
g
i
w
j
+
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
w
j
2
]
+
γ
T
=
∑
j
=
1
T
[
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
(
w
j
+
∑
i
∈
I
j
g
i
∑
i
∈
I
j
h
i
+
λ
)
2
−
(
∑
i
∈
I
j
g
i
)
2
2
(
∑
i
∈
I
j
h
i
+
λ
)
]
+
γ
T
L(\phi)^{(t)} = \sum_{i=1}^{n} [g_i f_t(\bold x_i)+\frac{1}{2}h_i f_t^2(\bold x_i)]+\gamma T+\frac{1}{2}\lambda \lVert w_j\rVert^2 \tag{7} \\ = \sum_{j=1}^{T}[\sum_{i \in I_j}g_i w_j +\frac{1}{2}(\sum_{i \in I_j}h_i + \lambda)w_j^2] + \gamma T \\ = \sum_{j=1}^{T}[\frac{1}{2}(\sum_{i \in I_j}h_i + \lambda) (w_j + \frac{\sum_{i \in I_j}g_i}{\sum_{i \in I_j}h_i + \lambda})^2-\frac{(\sum_{i \in I_j}g_i)^2}{2(\sum_{i \in I_j}h_i + \lambda)}]+ \gamma T
L(ϕ)(t)=i=1∑n[gift(xi)+21hift2(xi)]+γT+21λ∥wj∥2=j=1∑T[i∈Ij∑giwj+21(i∈Ij∑hi+λ)wj2]+γT=j=1∑T[21(i∈Ij∑hi+λ)(wj+∑i∈Ijhi+λ∑i∈Ijgi)2−2(∑i∈Ijhi+λ)(∑i∈Ijgi)2]+γT(7)
由上述公式的推导可以看出,在固定决策树结构
q
(
x
)
时
,
q(x)时,
q(x)时,叶子结点
j
j
j的最优权值
w
j
∗
w_j^*
wj∗为如下:
(8)
w
j
∗
=
−
∑
i
∈
I
j
g
i
∑
i
∈
I
j
h
i
+
λ
w_j^* =- \frac{\sum_{i \in I_j}g_i} {\sum_{i \in I_j}h_i + \lambda} \tag{8}
wj∗=−∑i∈Ijhi+λ∑i∈Ijgi(8)
此时的目标函数最优值
L
(
t
)
(
q
)
L^{(t)}(q)
L(t)(q)为:
(9)
L
(
t
)
(
q
)
=
−
1
2
∑
j
=
1
T
(
∑
i
∈
I
j
g
i
)
2
∑
i
∈
I
j
h
i
+
λ
+
γ
T
L^{(t)}(q) = - \frac{1}{2}\sum_{j=1}^{T} \frac{(\sum_{i \in I_j}g_i)^2}{\sum_{i \in I_j}h_i + \lambda}+ \gamma T \tag{9}
L(t)(q)=−21j=1∑T∑i∈Ijhi+λ(∑i∈Ijgi)2+γT(9)
在实践中,我们不可能遍历所有可能结构的决策树
q
(
x
)
q(\bold x)
q(x),所以可采用贪心算法在叶子上迭代式添加分支节点扩展决策树。
假设一个叶子结点被分裂出左右两个子节点,这两个子节点的样本集
I
L
,
I
R
I_L,I_R
IL,IR和父节点的样本集
I
I
I满足如下关系:
I
=
I
L
∪
I
R
I=I_L \cup I_R
I=IL∪IR
那么,如何找到最佳的样本集分裂点呢?换句话说就是什么样的分割点是最佳分裂点呢?
首先,我们用分裂点的损失减少量
L
s
p
l
i
t
L_{split}
Lsplit评价一个分割点的优劣,损失减少量
L
s
p
l
i
t
L_{split}
Lsplit计算公式如下:
(10)
L
s
p
l
i
t
=
1
2
[
(
∑
i
∈
I
L
g
i
)
2
∑
i
∈
I
L
h
i
+
λ
+
(
∑
i
∈
I
R
g
i
)
2
∑
i
∈
I
R
h
i
+
λ
−
(
∑
i
∈
I
g
i
)
2
∑
i
∈
I
h
i
+
λ
]
−
γ
L_{split} = \frac {1}{2}[\frac{(\sum_{i \in I_L}g_i)^2}{\sum_{i \in I_L}h_i + \lambda}+\frac{(\sum_{i \in I_R}g_i)^2}{\sum_{i \in I_R}h_i + \lambda}-\frac{(\sum_{i \in I}g_i)^2}{\sum_{i \in I}h_i + \lambda}] - \gamma \tag{10}
Lsplit=21[∑i∈ILhi+λ(∑i∈ILgi)2+∑i∈IRhi+λ(∑i∈IRgi)2−∑i∈Ihi+λ(∑i∈Igi)2]−γ(10)
然后,我们把父节点中每一个样本都作为分裂点计算一次,找到使损失减少量最大的那个分裂点就是样本集最佳分裂点。
1.1 小结
经过上面推导式地叙述,我们搞清了两个非常关键的问题:
(1)模型每次迭代都产生新的决策树,损失函数也是迭代式地改变。模型第t次迭代时的损失函数是上述公式9,这里再写一遍:
L
(
t
)
(
q
)
=
−
1
2
∑
j
=
1
T
(
∑
i
∈
I
j
g
i
)
2
∑
i
∈
I
j
h
i
+
λ
+
γ
T
L^{(t)}(q) = - \frac{1}{2}\sum_{j=1}^{T} \frac{(\sum_{i \in I_j}g_i)^2}{\sum_{i \in I_j}h_i + \lambda}+ \gamma T
L(t)(q)=−21j=1∑T∑i∈Ijhi+λ(∑i∈Ijgi)2+γT
(2)第
t
t
t次迭代时,在决策树的结构确定后,每个叶子结点
j
j
j的输出值
w
j
w_j
wj就是固定不变了,且
w
j
=
−
∑
i
∈
I
j
g
i
∑
i
∈
I
j
h
i
+
λ
w_j =- \frac{\sum_{i \in I_j}g_i} {\sum_{i \in I_j}h_i + \lambda}
wj=−∑i∈Ijhi+λ∑i∈Ijgi,这个
w
j
w_j
wj就是预测值,将所有决策树的对于某个样本
x
i
x_i
xi的输出值按公式1求和就是这个样本
x
i
x_i
xi的最终预测值。
2 最小化目标函数的方法
在第1节建模完成后,这个带正则化的损失函数(即目标函数)优化有哪些难点呢?
首先,想一想我们是怎么构造一颗决策树的呢,一种方法是根据特征枚举所有的的决策树,逐个计算目标函数值,选择使目标函数最小的决策树结构作为新增决策树,但这是个“NP完全”问题,这种求解方法不现实。
另一种方法采用贪心算法,就是通过一步步贪心地分裂节点获得完整决策树,但是这个最佳分裂点是怎么选定的呢?过程是这样的:
(1)从深度为0的树开始,对每个叶节点枚举所有的可用特征;
(2)针对每个特征,把属于该节点的训练样本根据该特征值升序排列,通过线性扫描的方式来决定该特征的最佳分裂点,用公式10计算每个分裂点的损失减少量
L
s
p
l
i
t
L_{split}
Lsplit,并记录该特征的损失减少量的最大值(采用最佳分裂点时的损失减少量);
(3)选择损失减少量最大的特征作为分裂特征,用该特征的最佳分裂点作为分裂位置,把该节点生长出左右两个新的叶节点,并为每个新节点关联对应的样本集;
(4)回到第1步,递归执行到满足特定条件为止(达到树的结点数限制,或者迭代刺次数限制,或者迭代loss不再下降)。
上述过程的算法伪代码描述如下图所示:
2.1 小结
XGBoost算法可以总结为:
(1) 算法在迭代的每一步都生成一颗新的决策树;
(2) 在构造每一颗决策树时,需要在每一次节点分裂时计算损失函数在每个样本上的一阶导和二阶导,即 g i g_{i} gi和 h i h_{i} hi,并找出最佳分裂点;
(3) 通过上面的贪心策略生成一颗树,计算每个叶子结点的的 G j G_{j} Gj和 H j H_{j} Hj,利用等式8计算预测值 w j w_j wj;
(4) 把新生成的决策树 f t ( x i ) f_t(x_i) ft(xi)加入 y ^ i t = y ^ i t − 1 + ϵ f t ( x i ) \hat{y}_i^t = \hat{y}_i^{t-1} + \epsilon f_t(x_i) y^it=y^it−1+ϵft(xi) ,其中 ϵ \epsilon ϵ为学习率,主要为了抑制模型的过拟合。
3 参考资料
[1].陈天奇大神的论文:XGBoost: A Scalable Tree Boosting System。
[2].https://blog.youkuaiyun.com/notHeadache/article/details/83146982
[3].https://zhuanlan.zhihu.com/p/29765582