这篇应该是你见过的讲xgboost的文章里最细的。
首先需要介绍GBDT,它是一种基于boosting增强策略的加法模型,训练的时候采用前向分布算法进行贪婪的学习,每次迭代都学习一棵CART树来拟合之前 t-1 棵树的预测结果与训练样本真实值的残差。
在核心思想不变的情况下,XGBoost对GBDT进行了一系列优化,主要是损失函数进行了二阶泰勒展开,另外还有目标函数加入正则项、支持并行和默认缺失值处理等,在可扩展性和训练速度上有了巨大的提升。
训练数据集 D = { ( x i , y i ) } i = 1 n D=\{\left(x_i,y_i\right)\}_{i=1}^{n} D={(xi,yi)}i=1n,其中 x i ∈ R m , y i ∈ R x_i\in\mathbb{R}^m,y_i\in\mathbb{R} xi∈Rm,yi∈R。又有实例 x x x。
假设某棵决策树有 T T T个叶子节点,则单棵决策树模型可记为:
f ( x ) = w q ( x ) f(x)=w_{q(x)} f(x)=wq(x)
其中 q : R m → { 1 , … , T } q:\mathbb{R}^m\to \{1,\dots,T\} q:Rm→{1,…,T}是由输入 x x x向叶子节点编号的映射,其本质是树的分支结构;而 w ∈ R T w\in\mathbb{R}^T w∈RT是叶子权重向量。
例如实例 x x x 落在了决策树的第 j j j 个节点,则其输出值即为 w j w_j wj 的值。而叶子节点向量即为
w = ( w 1 , ⋯ , w j , ⋯ , w T ) w=(w_1,\cdots,w_j,\cdots,w_T) w=(w1,⋯,wj,⋯,wT)
使用 Boosting \text{Boosting} Boosting思想,对于实例 x x x,总模型的预测输出为
y ^ = ϕ ( x ) = ∑ k = 1 K f k ( x ) \hat{y}=\phi\left(x\right)=\sum_{k=1}^K f_k\left(x\right) y^=ϕ(x)=k=1∑Kfk(x)
其中, f k ( x ) f_k\left(x\right) fk(x) 为第 k k k 棵决策树。
X G B o o s t XGBoost XGBoost的正则化目标函数由损失函数和正则化项两部分组成,定义如下:
L = ∑ i = 1 n l ( y i , y ^ i ) + ∑ k = 1 K Ω ( f k ) L=\sum_{i=1}^nl(y_i,\hat y_i)+\sum_{k=1}^K \Omega(f_k) L=i=1∑nl(yi,y^i)+k=1∑KΩ(fk)
以下逐一解释各个参数:
l l l表示损失函数。由于模型将使用二阶泰勒展开,因此要求损失函数一阶和二阶可导。常见的损失函数有:
y ^ i \hat y_i y^i表示第 i i i个样本 x i x_i xi的预测值。作为加法模型,预测得分是每棵树打分的累加之和:
y ^ i = ∑ k = 1 K f k ( x i ) , f k ∈ F \hat y_i=\sum_{k=1}^K f_k(x_i),f_k \in \mathcal F y^i=k=1∑Kfk(xi),fk∈F
如上式,此模型中共有 k k k 棵树, f k f_k fk 为第 k k k 棵树的函数(模型)。
Ω ( f ) \Omega(f) Ω(f)为单棵数的复杂度。模型将全部 k k k 棵树的复杂度进行求和,添加到目标函数中作为正则化项,以防止模型过拟合:
∑ k = 1 K Ω ( f k ) \sum_{k=1}^K \Omega(f_k) k=1∑KΩ(fk)
假设在第 t t t 轮,也是生成第 t t t 棵树时,要训练的树模型为 f t f_t ft ,则在第 t t t 轮迭代后,实例 x x x 的预测结果 y ^ ( t ) \hat y^{(t)} y^(t) 为:
y ^ ( t ) = ∑ k = 1 t f k ( x ) = y ^ ( t − 1 ) + f t ( x ) \hat y^{(t)}=\sum_{k=1}^t f_k(x)=\hat y^{(t-1)}+f_t(x) y^(t)=k=1∑tfk(x)=y^(t−1)+ft(x)
公式中 y ^ ( t − 1 ) \hat y^{(t-1)} y^(t−1) 为前 t − 1 t-1 t−1 棵树的预测结果。
注意,在第 t t t 轮迭代时,前 t − 1 t-1 t−1 棵树已知,因此 y ^ ( t − 1 ) \hat y^{(t-1)} y^(t−1) 也是一个已知常量。
将上式代入模型的基本目标函数,得到第 t t t 轮的目标函数为
L ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t ) ) + ∑ k = 1 t Ω ( f k ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) + constant \begin{aligned} L^{(t)} &=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t)}\right)+\sum_{k=1}^{t} \Omega\left(f_{k}\right) \\ & = \sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)+\Omega\left(f_{t}\right)+\text { constant } \end{aligned} L(t)=i=1∑nl(yi,y^i(t))+k=1∑tΩ(fk)=i=1∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)+ constant
后半部分同理。因为在第 t t t 轮迭代时,前 t − 1 t-1 t−1 棵树已知,因此前 t − 1 t-1 t−1 棵树的复杂度都是已知常量,可以记为 constant \text{constant} constant:
∑ k = 1 t Ω ( f k ) = Ω ( f t ) + ∑ k = 1 t − 1 Ω ( f k ) = Ω ( f t ) + constant \sum_{k=1}^{t} \Omega\left(f_{k}\right)=\Omega\left(f_{t}\right)+\sum_{k=1}^{t-1} \Omega\left(f_{k}\right)=\Omega\left(f_{t}\right)+\text { constant } k=1∑tΩ(fk)=Ω(ft)+k=1∑t−1Ω(fk)=Ω(ft)+ constant
而 y i y_i yi为已知标签,因此在第 t t t 轮的目标函数中,只有唯一的变量,即第 t t t 棵树 f t f_t ft 。
因为常量不影响目标函数的优化,我们去除常量,得到第 t t t 轮的目标函数为
L ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) L^{(t)}=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)+\Omega\left(f_{t}\right) L(t)=i=1∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)
泰勒公式是 XGBoost \text {XGBoost} XGBoost模型的核心要点之一,也是核心亮点。相比较于 GBDT \text{GBDT} GBDT模型,二阶泰勒展开逼近更加有效。
泰勒公式是将一个在 x = x 0 x=x_0 x=x0处具有 n n n阶导数的函数 f ( x ) f(x) f(x)利用 ( x − x 0 ) (x-x_0) (x−x0)的 n n n次多项式来逼近函数的方法。
泰勒公式形式众多,从本文的实用角度出发,采用以下形式的泰勒公式的二阶展开形式:
f ( x + Δ x ) ≃ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+\Delta x) \simeq f(x)+f^{\prime}(x) \Delta x+\frac{1}{2} f^{\prime \prime}(x) \Delta x^{2} f(x+Δx)≃f(x)+f′(x)Δx+21f′′(x)Δx2
第 t t t 轮目标函数 L ( t ) {L}^{\left(t\right)} L(t) 在 y i ^ ( t − 1 ) \hat{y_i}^{\left(t-1\right)} yi^(t−1) 处的二阶泰勒展开为
L ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) ≃ ∑ i = 1 n [ l ( y i , y i ^ ( t − 1 ) ) + ∂ y i ^ ( t − 1 ) l ( y i , y i ^ ( t − 1 ) ) f t ( x i ) + 1 2 ∂ y i ^ ( t − 1 ) 2 l ( y i , y i ^ ( t − 1 ) ) f t 2 ( x i ) ] + Ω ( 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 ) ] + Ω ( f t ) \begin{aligned} {L}^{(t)} &=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)+\Omega\left(f_{t}\right)\\& \simeq \sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y_i}^{(t-1)}\right)+\partial_{\hat{y_i}^{(t-1)}} l\left(y_{i}, \hat{y_i}^{(t-1)}\right) f_{t}\left({x}_{i}\right)+\frac{1}{2} \partial_{\hat{y_i}^{(t-1)}}^{2} l\left(y_{i}, \hat{y_i}^{(t-1)}\right) f_{t}^{2}\left({x}_{i}\right)\right]+\Omega\left(f_{t}\right) \\ &=\sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y_i}^{(t-1)}\right)+g_{i} f_{t}\left({x}_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left({x}_{i}\right)\right]+\Omega\left(f_{t}\right) \end{aligned} L(t)=i=1∑nl(yi,y^i(t−1)+ft(xi))+Ω(ft)≃i=1∑n[l(yi,yi^(t−1))+∂yi^(t−1)l(yi,yi^(t−1))ft(xi)+21∂yi^(t−1)2l(yi,yi^(t−1))ft2(xi)]+Ω(ft)=i=1∑n[l(yi,yi^(t−1))+gift(xi)+21hift2(xi)]+Ω(ft)
其中 g i = ∂ y i ^ ( t − 1 ) l ( y i , y i ^ ( t − 1 ) ) g_{i}=\partial_{\hat{y_i}^{(t-1)}} l\left(y_{i}, \hat{y_i}^{(t-1)}\right) gi=∂yi^(t−1)l(yi,yi^(t−1)), h i = ∂ y i ^ ( t − 1 ) 2 l ( y i , y i ^ ( t − 1 ) ) h_{i}=\partial_{\hat{y_i}^{(t-1)}}^{2} l\left(y_{i}, \hat{y_i}^{(t-1)}\right) hi=∂yi^(t−1)2l(yi,yi^(t−1))
以下逐步解释:
形式。比较我们选择的泰勒公式的形式与损失函数的形式
l ( y , y ^ ( t − 1 ) + f t ( x ) ) l\left(y, \hat{y}^{(t-1)}+f_{t}\left(x\right)\right) l(y,y^(t−1)+ft(x))
上节已经说过, y i y_i yi 和 y ^ ( t − 1 ) \hat y^{(t-1)} y^(t−1) 为都为常数,相当于 x x x;而第 t t t 棵树 f t f_t ft 不确定,相当于 Δ x \Delta x Δx。
偏导。求和中的每一项都对本项的 y ^ i ( t − 1 ) \hat y_{i}^{(t-1)} y^i(t−1) 求偏导,如果使用以下记号来标记损失函数 l l l 关于 y ^ i ( t − 1 ) \hat y_{i}^{(t-1)} y^i(t−1) 的一阶偏导和二阶偏导
g i = ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) , h i = ∂ y ^ ( t − 1 ) 2 l ( y i , y ^ ( t − 1 ) ) g_{i}=\partial_{\hat{y}^{(t-1)}} l\left(y_{i}, \hat{y}^{(t-1)}\right), \quad h_{i}=\partial_{\hat{y}^{(t-1)}}^{2} l\left(y_{i}, \hat{y}^{(t-1)}\right) gi=∂y^(t−1)l(yi,y^(t−1)),hi=∂y^(t−1)2l(yi,y^(t−1))
那么,将每一项的损失函数在 y ^ i ( t − 1 ) \hat y_{i}^{(t-1)} y^i(t−1) 处泰勒二阶展开,则得下式:
l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) ≃ l ( y i , y ^ i ( t − 1 ) ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) l\left(y_{i}, \hat{y}_{i}^{(t-1)}+f_{t}\left(x_{i}\right)\right)\simeq l(y_i,\hat y_i^{(t-1)})+g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right) l(yi,y^i(t−1)+ft(xi))≃l(yi,y^i(t−1))+gift(xi)+21hift2(xi)
常数。 l ( y i , y ^ i ( t − 1 ) ) l\left(y_{i}, \hat{y}_{i}^{(t-1)}\right) l(yi,y^i(t−1))是前 t − 1 t-1 t−1 棵树对此项带来的损失,因此是常数; 在这一轮中,因为 g i g_{i} gi 和 h i h_{i} hi 已经是上一轮的产物,因此也是常数。
如果再次去掉所有常数项,将得到
L ( t ) ≃ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) L^{(t)} \simeq \sum_{i=1}^{n}\left[g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right) L(t)≃i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)
其中 g i = ∂ y i ^ ( t − 1 ) l ( y i , y i ^ ( t − 1 ) ) g_{i}=\partial_{\hat{y_i}^{(t-1)}} l\left(y_{i}, \hat{y_i}^{(t-1)}\right) gi=∂yi^(t−1)l(yi,yi^(t−1)), h i = ∂ y i ^ ( t − 1 ) 2 l ( y i , y i ^ ( t − 1 ) ) h_{i}=\partial_{\hat{y_i}^{(t-1)}}^{2} l\left(y_{i}, \hat{y_i}^{(t-1)}\right) hi=∂yi^(t−1)2l(yi,yi^(t−1)) 。
我们使用两个部分来定义一棵树 f t f_t ft 的复杂度:
即:
Ω ( f t ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \Omega\left(f_{t}\right)=\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2} Ω(ft)=γT+21λj=1∑Twj2其中 γ \gamma γ 和 λ \lambda λ 都是超参数。
设属于第 j j j个叶子节点的所有样本下标的集合为
I j = { i ∣ q ( x i ) = j } I_{j}=\left\{i | q\left(x_{i}\right)=j\right\} Ij={i∣q(xi)=j}
使用此种表示方法,参考模型前提设定,可将目标函数改换为按照叶子节点累加的形式
L ( 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 i ) 2 ] + λ 1 2 ∑ j = 1 T w j 2 + γ 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 \begin{aligned}L^{(t)} & \simeq \sum_{i=1}^{n}\left[g_{i} f_{t}\left(x_{i}\right)+\frac{1}{2} h_{i} f_{t}^{2}\left(x_{i}\right)\right]+\Omega\left(f_{t}\right) \\ &=\sum_{i=1}^{n}\left[g_{i} w_{q\left(x_{i}\right)}+\frac{1}{2} h_{i} w_{q\left(x_{i}\right)}^{2}\right]+\lambda \frac{1}{2} \sum_{j=1}^{T} w_{j}^{2}+\gamma T \\ &=\sum_{j=1}^{T}\left[\left(\sum_{i \in I_{j}} g_{i}\right) w_{j}+\frac{1}{2}\left(\sum_{i \in I_{j}} h_{i}+\lambda\right) w_{j}^{2}\right]+\gamma T\\ &=\sum_{j=1}^{T}\left[G_{j} w_{j}+\frac{1}{2}\left(H_{j}+\lambda\right) w_{j}^{2}\right]+\gamma T\end{aligned} L(t)≃i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)=i=1∑n[giwq(xi)+21hiwq(xi)2]+λ21j=1∑Twj2+γT=j=1∑T⎣⎡⎝⎛i∈Ij∑gi⎠⎞wj+21⎝⎛i∈Ij∑hi+λ⎠⎞wj2⎦⎤+γT=j=1∑T[Gjwj+21(Hj+λ)wj2]+γT
其中
G j = ∑ i ∈ I j g i H j = ∑ i ∈ I j h i G_{j}=\sum_{i \in I_{j}} g_{i} \quad H_{j}=\sum_{i \in I_{j}} h_{i} Gj=i∈Ij∑giHj=i∈Ij∑hi
到此,对目标函数的化简结束。
以下逐行解释:
将目标函数作为各个 w j w_j wj 的二次凸函数来求解 w j ∗ w_j^* wj∗
w j ∗ = arg min w j L ( t ) w_{j}^{*}=\underset{w_{j}}{\arg \min }{{L}}^{(t)} wj∗=wjargminL(t)
令 L ( t ) L^{(t)} L(t) 对 w j w_{j} wj 求偏导,并令其等于零
∂ L ( t ) ∂ w j = G j + ( H j + λ ) w j = 0 \frac{\partial L^{(t)}}{\partial w_j}=G_j+(H_j+\lambda)w_j=0 ∂wj∂L(t)=Gj+(Hj+λ)wj=0
解得每个叶子节点的权重值为
w j ∗ = − G j H j + λ w_{j}^{*}=-\frac{G_{j}}{H_{j}+\lambda} wj∗=−Hj+λGj
代入即得最优化的目标函数值
L ( t ) ∗ = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T L^{(t)*}=-\frac{1}{2} \sum_{j=1}^{T} \frac{G_{j}^{2}}{H_{j}+\lambda}+\gamma T L(t)∗=−21j=1∑THj+λGj2+γT
也可使用二次函数求顶点坐标的方法求得。
在实际训练的第 t t t 轮,模型都使用贪心法进行树节点的分裂:
对树中的每个叶子结点尝试进行分裂;
每次分裂后,原来的一个叶子结点继续分裂为左右两个子叶子结点,原叶子结点中的样本集将根据该结点的判断规则分散到左右两个叶子结点中;
新分裂一个结点后,我们需要检测这次分裂是否会给损失函数带来增益,增益的定义如下:
L split = L L + R − ( L L + L R ) = [ − 1 2 ( G L + G R ) 2 H L + H R + λ + γ ] − [ − 1 2 ( G L 2 H L + λ + G R 2 H R + λ ) + 2 γ ] = 1 2 [ G L 2 H L + λ + G R 2 H R + λ − ( G L + G R ) 2 H L + H R + λ ] − γ \begin{aligned} L_{\text {split}} &=L_{L+R}-\left(L_{L}+L_{R}\right) \\ &=\left[-\frac{1}{2} \frac{\left(G_{L}+G_{R}\right)^{2}}{H_{L}+H_{R}+\lambda}+\gamma\right]-\left[-\frac{1}{2}\left(\frac{G_{L}^{2}}{H_{L}+\lambda}+\frac{G_{R}^{2}}{H_{R}+\lambda}\right)+2 \gamma\right] \\ &=\frac{1}{2}\left[\frac{G_{L}^{2}}{H_{L}+\lambda}+\frac{G_{R}^{2}}{H_{R}+\lambda}-\frac{\left(G_{L}+G_{R}\right)^{2}}{H_{L}+H_{R}+\lambda}\right]-\gamma \end{aligned} Lsplit=LL+R−(LL+LR)=[−21HL+HR+λ(GL+GR)2+γ]−[−21(HL+λGL2+HR+λGR2)+2γ]=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
如果 L split > 0 L_{\text {split}}>0 Lsplit>0,或者大于设定的阈值,即分裂后目标函数值下降了,那么可以考虑此次分裂的结果。
但是一般都是有多个候选分割点,寻找最佳分割点的理论最佳步骤如下:
以上为全局扫描法,是一种贪心的方法,每次进行分裂尝试都要遍历一遍全部候选分割点,但不适用数据量过大的条件,如内存无法一次载入或者分布式的情况。
基于此,XGBoost提出了一系列加快寻找最佳分裂点的方案:
一棵树不会一直生长下去,除了决策树基础的下面是一些常见的限制条件。
XGBoost的并行,并不是说每棵树可以并行训练,XGB本质上仍然采用boosting思想,每棵树训练前需要等前面的树训练完成才能开始训练。XGBoost的并行,指的是特征维度的并行:在训练之前,每个特征按特征值对样本进行预排序,并存储为Block结构,在后面查找特征分割点时可以重复使用,而且特征已经被存储为一个个block结构,那么在寻找每个特征的最佳分割点时,可以利用多线程对每个block并行计算。
如果在意AUC,采用AUC来评估模型的性能,可以通过设置scale_pos_weight来增大少数样本的权重以平衡正样本和负样本的分布。例如当正负样本比例为1:10时,scale_pos_weight可以取10;
如果在意概率(预测得分的合理性),则不能重新平衡数据集,这会破坏数据的真实分布,应该设置max_delta_step为一个有限数字来帮助收敛(在基模型为LR时有效)。
参考:
https://mp.weixin.qq.com/s/wLE9yb7MtE208IVLFlZNkw