对XGBoost来说,真正难度较大的部分并不是梳理以上算法流程,而是证明这一流程可以让模型向着目标函数最小化的方向运行。在这个流程中包括如下很明显的问题:
建树时拟合的 r i k = − g i k h i k r_{ik} = -\frac{g_{ik}}{h_{ik}} rik=−hikgik究竟是什么?拟合它有什么意义?
结构分数和结构分数增益的公式是如何推导出来的?为什么这样建树可以提升模型的效果?
为什么叶子节点的输出值 w j w_j wj是 − ( ∑ i ∈ j g i k ) ∑ i ∈ j h i k + λ -\frac{(\sum_{i \in j} g_{ik})}{\sum_{i \in j} h_{ik} + \lambda} −∑i∈jhik+λ(∑i∈jgik)?这样输出有什么意义?
课程的第一部分说XGBoost拟合的也是残差,残差在哪里?
首先,根据之前对目标函数的定义,XGBoost中目标函数是针对一棵树的目标函数,而不是针对一个样本或一整个算法的目标函数。并且,任意树的目标函数都包括三大部分:损失函数
l
l
l、叶子数量
T
T
T以及正则项。具体地来说:
假设单一树 f k f_k fk的目标函数为 O k O_k Ok,总共有 T T T片叶子,该树上任意样本 i i i的损失函数为 l ( ( y i , H ( x i ) ) l((y_i,H(x_i)) l((yi,H(xi)),其中 H ( x i ) H(x_i) H(xi)是 i i i号样本在集成算法上的预测结果。树上总共有M个样本,目标函数中使用L2正则化( λ \lambda λ不为0, α \alpha α为0),并且 γ \gamma γ不为0,则该树的目标函数为:
O k = ∑ i = 1 M l ( y i , H k ( x i ) ) + γ T + 1 2 λ ∑ j = 1 T w j 2 O_k = \sum_{i=1}^Ml(y_i,H_k(x_i)) + \gamma T + \frac{1}{2}\lambda\sum_{j=1}^Tw_j^2 Ok=i=1∑Ml(yi,Hk(xi))+γT+21λj=1∑Twj2
我们的目标是令目标函数最小,并找出令目标函数最小的某个自变量。对使用普通损失函数的Boosting算法来说,算法的输出值 H ( x ) H(x) H(x)是在迭代过程中不断变化的,损失函数 l ( y , H ( x ) ) l(y,H(x)) l(y,H(x))也是在迭代中不断变小的:
H k ( x i ) = H k − 1 ( x i ) + f k ( x i ) H_k(x_i) = H_{k-1}(x_i) + f_k(x_i) Hk(xi)=Hk−1(xi)+fk(xi)
l k = l ( y i , H k − 1 ( x i ) + f k ( x i ) ) l_k = l(y_i,H_{k-1}(x_i) + f_k(x_i)) lk=l(yi,Hk−1(xi)+fk(xi))
当迭代到第 k k k次时,损失函数中的 y i y_i yi与 H k − 1 ( x i ) H_{k-1}(x_i) Hk−1(xi)都是常数,只有 f k ( x i ) f_k(x_i) fk(xi)是变量,因此我们只需要在损失函数上对 f k ( x i ) f_k(x_i) fk(xi)求导,并找到令整体损失函数最小的预测值 f k ( x i ) f_k(x_i) fk(xi)即可。在GBDT当中,我们提到过,无论弱评估器 f k f_k fk是什么结构、什么规则、如何建立、如何拟合,只要其最终的输出值 f k ( x i ) f_k(x_i) fk(xi)是令整体损失函数 L L L最小化的 f k ( x i ) f_k(x_i) fk(xi),那随着算法逐步迭代,损失函数必然会越来越小。因此,一个适合的 f k ( x i ) f_k(x_i) fk(xi)不仅能保证损失持续减小,还可以指导单个评估器的建立。
在XGBoost当中,我们也可以对目标函数求导、并找出令目标函数最小的某个自变量,但问题在于,XGBoost的目标函数中存在多个自变量:
O
k
=
∑
i
=
1
M
l
(
y
i
,
H
k
(
x
i
)
)
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
=
∑
i
=
1
M
l
(
y
i
,
H
k
−
1
(
x
i
)
+
f
k
(
x
i
)
)
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
其中, T T T是第 k k k棵树上的叶子总量, f k ( x i ) f_k(x_i) fk(xi)与 w j w_j wj都是模型输出的预测值(叶子上的输出值),不过表现形式不同,对任意位于叶子 j j j上的样本 i i i而言,数值上 f k ( x i ) = w j f_k(x_i) = w_j fk(xi)=wj。对XGBoost来说,只能选择一个变量作为自变量,考虑到 f k ( x i ) f_k(x_i) fk(xi)只与单个样本的精确程度有关,而 T T T只与树结构有关,XGBoost论文最终选择了即与精确度有关、又与树结构有关的变量 w j w_j wj。同时,如果知道叶子的最佳输出值 w j w_j wj就可以引导树成长为合理的结构,但只知道叶子的总量 T T T是无法指导建树的。
因此,求解XGBoost目标函数的第一步,就是将目标函数尽量整理成以 w j w_j wj表示的形式。
在我们的目标函数 O k O_k Ok中,可以被泰勒展开的是第一部分损失函数 L L L:
O k = ∑ i = 1 M l ( y i , H k − 1 ( x i ) + f k ( x i ) ) + γ T + 1 2 λ ∑ j = 1 T w j 2 O_k = \sum_{i=1}^Ml \left( y_i,H_{k-1}(x_i) + f_k(x_i) \right) + \gamma T + \frac{1}{2}\lambda\sum_{j=1}^T w_j^2 Ok=i=1∑Ml(yi,Hk−1(xi)+fk(xi))+γT+21λj=1∑Twj2
由于损失函数 l l l中只有唯一变量 H k − 1 ( x i ) + f k ( x i ) H_{k-1}(x_i) + f_k(x_i) Hk−1(xi)+fk(xi),因此可以将函数简写为 l ( H k − 1 ( x i ) + f k ( x i ) ) l(H_{k-1}(x_i) + f_k(x_i)) l(Hk−1(xi)+fk(xi))。
根据二阶泰勒展开,已知:
f
(
x
)
≈
∑
n
=
0
2
f
(
n
)
(
a
)
n
!
(
x
−
a
)
n
≈
f
(
a
)
+
f
′
(
a
)
1
!
(
x
−
a
)
+
f
′
′
(
a
)
2
!
(
x
−
a
)
2
令泰勒展开中的 x = H k − 1 ( x i ) + f k ( x i ) x = H_{k-1}(x_i) + f_k(x_i) x=Hk−1(xi)+fk(xi),令泰勒展开中的 a = H k − 1 ( x i ) a = H_{k-1}(x_i) a=Hk−1(xi),则 ( x − a ) = f k ( x i ) (x-a) = f_k(x_i) (x−a)=fk(xi)。据此,损失函数 l ( H k − 1 ( x i ) + f k ( x i ) ) l(H_{k-1}(x_i) + f_k(x_i)) l(Hk−1(xi)+fk(xi))可以被表示为:
l
(
H
k
−
1
(
x
i
)
+
f
k
(
x
i
)
)
≈
l
(
H
k
−
1
(
x
i
)
)
+
∂
l
(
H
k
−
1
(
x
i
)
)
∂
H
k
−
1
(
x
i
)
∗
f
k
(
x
i
)
+
∂
2
l
(
H
k
−
1
(
x
i
)
)
2
∂
H
k
−
1
2
(
x
i
)
∗
f
k
2
(
x
i
)
在XGBoost中我们定义过损失函数的一阶导数与二阶导数:
g i k = ∂ l ( y i , H k − 1 ( x i ) ) ∂ H t − 1 ( x i ) g_{ik} = \frac{\partial{l(y_i,H_{k-1}(x_i))}}{\partial{H_{t-1}(x_i)}} gik=∂Ht−1(xi)∂l(yi,Hk−1(xi))
h i k = ∂ 2 l ( y i , H k − 1 ( x i ) ) ∂ H t − 1 2 ( x i ) h_{ik} = \frac{\partial^2{l(y_i,H_{k-1}(x_i))}}{\partial{H^2_{t-1}(x_i)}} hik=∂Ht−12(xi)∂2l(yi,Hk−1(xi))
在XGBoost原论文中,为了公式简洁, g i g_i gi和 h i h_i hi并没有呈现下标 k k k,但我们已经很清楚: g g g与 h h h是在每一轮迭代时需要被重新计算的。在这里我们也参照原论文中的做法去掉下标 k k k。因此,经过泰勒展开后的式子可以化简为:
l
(
H
k
−
1
(
x
i
)
+
f
k
(
x
i
)
)
≈
l
(
H
k
−
1
(
x
i
)
)
+
g
i
f
k
(
x
i
)
+
1
2
h
i
f
k
2
(
x
i
)
≈
常数
+
g
i
f
k
(
x
i
)
+
1
2
h
i
f
k
2
(
x
i
)
不难发现,该式子中 H k − 1 ( x i ) H_{k-1}(x_i) Hk−1(xi)是常数,因此第一部分 l ( H t − 1 ( x i ) ) l(H_{t-1}(x_i)) l(Ht−1(xi))也是一个常数,常数无法被最小化,因此我们可以将常数从该目标函数中剔除。经过泰勒展开,目标函数变为:
O
~
k
=
∑
i
=
1
M
(
g
i
f
k
(
x
i
)
+
1
2
h
i
f
k
2
(
x
i
)
)
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
=
∑
i
=
1
M
g
i
f
k
(
x
i
)
+
1
2
∑
i
=
1
M
h
i
f
k
2
(
x
i
)
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
现在目标函数的前两项分别代表所有样本的 g i f k ( x i ) g_if_k(x_i) gifk(xi)之和,以及所有样本的 h i f k 2 ( x i ) h_if^2_k(x_i) hifk2(xi)之和乘1/2。别忘记,我们选择的唯一的自变量是 w j w_j wj,因此我们希望能够将 f k f_k fk以某种方式转化为 w j w_j wj。之前已经提到过多次,对任意位于叶子 j j j上的样本 i i i而言,数值上 f k ( x i ) = w j f_k(x_i) = w_j fk(xi)=wj,我们可以尝试着从一个样本开始进行转化:
对于单一样本 i i i,假设这个样本位于叶子 j j j上,应该有:
g i f k ( x i ) = g i w j g_if_k(x_i) = g_iw_j gifk(xi)=giwj
对于一片叶子 j j j,我们可以计算这片叶子上所有样本的 g i w j g_iw_j giwj之和:
∑ i ∈ j g i w j \sum_{i \in j} g_iw_j i∈j∑giwj
而一片叶子上所有样本的 w j w_j wj都是一致的,因此一片叶子上的 g i w j g_iw_j giwj之和可以转变为:
∑
i
∈
j
g
i
w
j
=
g
1
w
j
+
g
2
w
j
+
.
.
.
+
g
n
w
j
,其中
1
,
2...
n
是叶子
j
上的样本
=
w
j
∑
i
∈
j
g
i
假设现在一共有 T T T片叶子,则整棵树上所有样本的 g i w j g_iw_j giwj之和为:
∑ j = 1 T ( w j ∑ i ∈ j g i ) \sum_{j=1}^T \left( w_j\sum_{i \in j} g_i \right) j=1∑T(wji∈j∑gi)
所以:
∑ i = 1 M g i f k ( x i ) = ∑ j = 1 T ( w j ∑ i ∈ j g i ) \sum_{i=1}^Mg_if_k(x_i) = \sum_{j=1}^T \left( w_j\sum_{i \in j} g_i \right) i=1∑Mgifk(xi)=j=1∑T(wji∈j∑gi)
同理,单一样本 i i i的 h i f k 2 ( x i ) h_if^2_k(x_i) hifk2(xi)也可以以相同方式转化。对单一样本:
h i f k 2 ( x i ) = h i w j 2 h_if^2_k(x_i) = h_iw^2_j hifk2(xi)=hiwj2
对一片叶子:
∑
i
∈
j
h
i
w
j
2
=
h
1
w
j
2
+
h
2
w
j
2
+
.
.
.
+
h
n
w
j
2
,其中
1
,
2...
n
是叶子
j
上的样本
=
w
j
2
∑
i
∈
j
h
i
对整棵树:
∑ i = 1 M h i f k 2 ( x i ) = ∑ j = 1 T ( w j 2 ∑ i ∈ j h i ) \sum_{i=1}^Mh_if^2_k(x_i) = \sum_{j=1}^T \left( w^2_j\sum_{i \in j} h_i \right) i=1∑Mhifk2(xi)=j=1∑T(wj2i∈j∑hi)
因此对整个目标函数有:
O
~
k
=
∑
i
=
1
M
g
i
f
k
(
x
i
)
+
1
2
∑
i
=
1
M
h
i
f
k
2
(
x
i
)
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
=
∑
j
=
1
T
(
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
∑
i
∈
j
h
i
)
+
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
不难发现,现在正则项可以与原来损失函数的部分合并了:
=
∑
j
=
1
T
(
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
∑
i
∈
j
h
i
+
1
2
λ
w
j
2
)
+
γ
T
=
∑
j
=
1
T
(
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
(
∑
i
∈
j
h
i
+
λ
)
)
+
γ
T
合并之后,整个目标函数变为两项,一项是所有叶子上的(损失+正则)之和,另一项是叶子总量。现在,我们可以开始求解最小目标函数以及对应的最优自变量 w j w_j wj了。
首先,令目标函数中的叶子总量最小是不可能的,过度降低叶子总量会大幅度伤害模型的学习能力,因此我们只能考虑令所有叶子上的(损失+正则)之和最小。
其次,当树建好之后,叶子与叶子之间是相互独立的,因此每片叶子上的(损失+正则)也是相互独立的。我们只要令每片叶子的(损失+正则)都最小,就可以保证全部叶子的(损失+正则)之和最小。故而,我们要令式子中标注为红色的部分最小:
O
~
k
=
∑
j
=
1
T
(
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
(
∑
i
∈
j
h
i
+
λ
)
)
+
γ
T
叶子权重
w
j
w_j
wj
将标注为红色的部分命名为
μ
j
\mu_j
μj,表示叶子
j
j
j上的损失+正则。则有:
μ j = w j ∑ i ∈ j g i + 1 2 w j 2 ( ∑ i ∈ j h i + λ ) \mu_j = w_j\sum_{i \in j} g_i + \frac{1}{2}w^2_j(\sum_{i \in j} h_i + \lambda) μj=wji∈j∑gi+21wj2(i∈j∑hi+λ)
现在,对叶子 j j j而言,在 μ j \mu_j μj上对唯一自变量 w j w_j wj求导,则有:
∂
μ
j
∂
w
j
=
∂
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
(
∑
i
∈
j
h
i
+
λ
)
∂
w
j
=
∑
i
∈
j
g
i
+
w
j
(
∑
i
∈
j
h
i
+
λ
)
令一阶导数为0,则有:
∑
i
∈
j
g
i
+
w
j
(
∑
i
∈
j
h
i
+
λ
)
=
0
w
j
(
∑
i
∈
j
h
i
+
λ
)
=
−
∑
i
∈
j
g
i
w
j
=
−
∑
i
∈
j
g
i
∑
i
∈
j
h
i
+
λ
你应该发现了,对一片叶子来说,令目标函数最小的 w j w_j wj就是我们之前提过的叶子权重,也就是XGBoost数学流程当中叶子上的输出值。如果要令叶子的输出非常接近叶子权重公式,那应该如何拟合每个样本呢?
对任意位于叶子 j j j上的样本 i i i来说:
μ i = w j g i + 1 2 w j 2 h i \mu_i = w_jg_i + \frac{1}{2}w^2_jh_i μi=wjgi+21wj2hi
将一片叶子上的 μ j \mu_j μj转变成 μ i \mu_i μi时,原则上需要将 μ j \mu_j μj中的每一项都转换为单个样本所对应的项,然而在转换正则项时则存在问题:与 ∑ i ∈ j g i \sum_{i \in j} g_i ∑i∈jgi这样可以直接指向单个样本的项不同, λ \lambda λ是针对与一片叶子设置的值,如果要将 λ \lambda λ转变为针对单一样本的正则项,则需要知道当前叶子上一共有多少样本。然而,拟合发生在建树之前,因此在这一时间点不可能知道一片叶子上的样本总量,因此在xgboost的实际实现过程当中,拟合每一片叶子时不涉及正则项,只有在计算结构分数与叶子输出值时才使用正则项。
对 μ i \mu_i μi上唯一的自变量 w j w_j wj求导,则有:
∂
μ
i
∂
w
j
=
∂
(
w
j
g
i
+
1
2
w
j
2
h
i
)
∂
w
j
=
g
i
+
w
j
h
i
令一阶导数为0,则有:
g
i
+
w
j
h
i
=
0
w
j
h
i
=
−
g
i
w
j
=
−
g
i
h
i
对任意样本 i i i而言,令目标函数最小的最优 w j w_j wj就是我们的伪残差 r i r_i ri,也就是XGBoost数学流程当中用于进行拟合的拟合值。
现在,我们把令目标函数最小的最优 w j w_j wj带回到 μ j \mu_j μj中,查看 μ j \mu_j μj如何变化:
μ
j
=
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
(
∑
i
∈
j
h
i
+
λ
)
=
−
∑
i
∈
j
g
i
∑
i
∈
j
h
i
+
λ
∗
∑
i
∈
j
g
i
+
1
2
(
−
∑
i
∈
j
g
i
∑
i
∈
j
h
i
+
λ
)
2
∗
∑
i
∈
j
h
i
+
λ
=
−
(
∑
i
∈
j
g
i
)
2
∑
i
∈
j
h
i
+
λ
+
1
2
(
∑
i
∈
j
g
i
)
2
∑
i
∈
j
h
i
+
λ
=
−
1
2
(
∑
i
∈
j
g
i
)
2
∑
i
∈
j
h
i
+
λ
因此,目标函数(所有叶子上的损失)就可以变为:
O
~
k
=
∑
j
=
1
T
(
w
j
∑
i
∈
j
g
i
+
1
2
w
j
2
(
∑
i
∈
j
h
i
+
λ
)
)
+
γ
T
=
∑
j
=
1
T
(
−
1
2
(
∑
i
∈
j
g
i
)
2
∑
i
∈
j
h
i
+
λ
)
+
γ
T
因此,一片叶子上的目标函数就是:
O j = − 1 2 ( ∑ i ∈ j g i ) 2 ∑ i ∈ j h i + λ + γ O_j = -\frac{1}{2}\frac{(\sum_{i \in j} g_i)^2}{\sum_{i \in j} h_i + \lambda} + \gamma Oj=−21∑i∈jhi+λ(∑i∈jgi)2+γ
对任意一片叶子来说,目标函数可以衡量叶子的质量,其中 γ \gamma γ是可以设定的超参数, 1 2 \frac{1}{2} 21为常数,因此对任意叶子,我们希望标注为红色的部分越小越好:
O j = 1 2 ( − ( ∑ i ∈ j g i ) 2 ∑ i ∈ j h i + λ ) + γ O_j = \frac{1}{2}\left( \boldsymbol{\color{red}{-\frac{(\sum_{i \in j} g_i)^2}{\sum_{i \in j} h_i + \lambda}}} \right)+ \gamma Oj=21(−∑i∈jhi+λ(∑i∈jgi)2)+γ
故而,我们希望以下式子越大越好:
( ∑ i ∈ j g i ) 2 ∑ i ∈ j h i + λ \frac{(\sum_{i \in j} g_i)^2}{\sum_{i \in j} h_i + \lambda} ∑i∈jhi+λ(∑i∈jgi)2
这个式子,正是XGBoost用于分枝时的指标“结构分数”(Structure Score)。
当分枝的时候,我们希望目标函数越小越好,因此在分枝过程中,父节点的目标函数是大于子节点的目标函数的,因此我们可以使用(父节点目标函数 - 子节点目标函数之和)来衡量分枝的质量,则有:
G
a
i
n
=
O
p
−
(
O
l
+
O
r
)
=
−
1
2
(
∑
i
∈
P
g
i
)
2
∑
i
∈
P
h
i
+
λ
+
γ
−
(
−
1
2
(
∑
i
∈
L
g
i
)
2
∑
i
∈
L
h
i
+
λ
+
γ
−
1
2
(
∑
i
∈
R
g
i
)
2
∑
i
∈
R
h
i
+
λ
+
γ
)
=
−
1
2
(
∑
i
∈
P
g
i
)
2
∑
i
∈
P
h
i
+
λ
+
γ
+
1
2
(
∑
i
∈
L
g
i
)
2
∑
i
∈
L
h
i
+
λ
−
γ
+
1
2
(
∑
i
∈
R
g
i
)
2
∑
i
∈
R
h
i
+
λ
−
γ
=
1
2
(
(
∑
i
∈
L
g
i
)
2
∑
i
∈
L
h
i
+
λ
+
(
∑
i
∈
R
g
i
)
2
∑
i
∈
R
h
i
+
λ
−
(
∑
i
∈
P
g
i
)
2
∑
i
∈
P
h
i
+
λ
)
−
γ
=
1
2
(
S
c
o
r
e
L
+
S
c
o
r
e
R
−
S
c
o
r
e
P
)
−
γ
其中, γ \gamma γ是可以设定的超参数, 1 2 \frac{1}{2} 21为常数,因此:
G a i n = S c o r e L + S c o r e R − S c o r e P Gain = Score_L + Score_R - Score_P Gain=ScoreL+ScoreR−ScoreP
这就是我们在分枝时所使用的结构分数增益了。
现在你发现了,XGBoost流程中所使用的全部新公式(包括独特的拟合值、独特的分枝指标、独特的输出值)都是通过令目标函数最小而求解出来的。因此,XGBoost整个流程就保证了目标函数一定是向着最小化方向进行迭代的,新生成的每片叶子上的输出值 w j w_j wj都是会令目标函数最小化的输出值。现在,你可以回答最开始的问题了