(九)XGBoost的原理、具体实例、代码实现

(九)XGBoost

本系列重点在浅显易懂,快速上手。不进行过多的理论讲解:也就是不去深究what,而是关注how。全文围绕以下三个问题展开:

1)长什么样?

2)解决什么问题?

3)怎么实现?

​ 3.1)从数学讲,原理

​ 3.2)具体实例

​ 3.3)从代码上讲,如何掉包实现

1 定义

XGBoost,全称eXtreme Gradient Boosting ,简称XGB,是GBDT算法的一种变种,是一种监督算法;它是boost算法的一种,也属于集成算法,是一种伸缩性强、便捷的可并行构建模型的Gradient Boosting算法。其高效地实现了GBDT算法并进行了算法和工程上的许多改进,被广泛应用在Kaggle竞赛及其他许多机器学习竞赛中。并取得了不错的成绩。它可用于分类,回归,排序问题。

XGBoost与GBDT比较大的不同就是目标函数的定义,基本思想是一致的,同样是利用加法模型与前向分步算法实现学习的优化过程。预测过程如下:
y ^ i = ∑ k = 1 K f k ( x i ) \hat{y}_{i}=\sum_{k=1}^{K}f_{k}({x}_{i}) y^i=k=1Kfk(xi)

其中, f k f_k fk表示回归X树,K为回归树的数量。

在这里插入图片描述

XGBoost是由GBDT发展而来,同样是利用加法模型与前向分步算法实现学习的优化过程,但与GBDT是有区别的。主要区别包括以下几点:

1、传统的GBDT以CART树作为基学习器,XGBoost还支持线性分类器(线性回归、逻辑回归),这个时候XGBoost相当于L1和L2正则化的逻辑斯蒂回归(分类)或者线性回归(回归);

2、传统的GBDT在优化的时候只用到一阶导数信息,XGBoost则对代价函数进行了二阶泰勒展开,得到一阶和二阶导数

3、XGBoost在代价函数中加入了正则项,用于控制模型的复杂度。从权衡方差偏差来看,它降低了模型的方差,使学习出来的模型更加简单,防止过拟合,这也是XGBoost优于传统GBDT的一个特性;

4、shrinkage(缩减或者学习速率),相当于学习速率(XGBoost中的eta)。XGBoost在进行完一次迭代时,会将叶子节点的权值乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。(GBDT也有学习速率);

5、列抽样。XGBoost借鉴了随机森林的做法,支持列抽样,不仅防止过拟合,还能减少计算;对缺失值的处理。对于特征的值有缺失的样本,XGBoost还可以自动学习出它的分裂方向;

6、XGBoost工具支持并行。Boosting不是一种串行的结构吗?怎么并行的?注意XGBoost的并行不是tree粒度的并行,XGBoost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。XGBoost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),XGBoost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。

性质

有监督,分类,回归,排序

与GBDT区别

  • 目标函数:XGBoost的损失函数添加了正则化项,使用正则用以控制模型的复杂度,正则项里包含了树的叶子节点个数、每个叶子节点权重(叶结点的socre值)的平方和。
  • 优化方法:GBDT在优化时只使用了一阶导数信息,XGBoost在优化时使用了一、二阶导数信息。
  • 缺失值处理:XBGoost对缺失值进行了处理,通过学习模型自动选择最优的缺失值默认切分方向,分别对左右侧计算损失值,那个小就划分到那一侧,并记录下来额,预测试也按这个标准来,否则默认左侧。
  • 防止过拟合: XGBoost除了增加了正则项来防止过拟合,还支持行列采样的方式来防止过拟合。
  • 结果:它可以在最短时间内用更少的计算资源得到更好的结果。

2实现过程

回归

在这里插入图片描述

二分类

在这里插入图片描述

  1. 给定初始常数项 f 0 ( x ) f_0(x) f0(x)

    y ^ i 0 = f 0 ( x i ) = 常数 \hat{y}_i^0=f_0(x_i)=\text{常数} y^i0=f0(xi)=常数

  2. 划分第一课树,采用近似精确贪心算法划分,Gain值最大的特征值为划分点(这个公式下面详细解释,这里先有个印象)。
    G a i n = 1 2 [ G L 2 H L 2 + λ + G R 2 H R 2 + λ − ( G L + G R ) 2 ( H L + H R ) 2 + λ ] − γ Gain=\frac{1}{2}\left[\frac{G_L^2}{H_L^2+\lambda}+\frac{G_R^2}{H_R^2+\lambda}-\frac{(G_L+G_R)^2}{(H_L+H_R)^2+\lambda}\right]-\gamma Gain=21[HL2+λGL2+HR2+λGR2(HL+HR)2+λ(GL+GR)2]γ

  3. 求解所有叶子节点的值(lr是学习率),求解预测值

f 1 ( x ) = l r ∗ w 1 = − l r ∗ G j H j + λ f_1(x)=lr*w_1=-lr*\frac{G_j}{H_j+\lambda} f1(x)=lrw1=lrHj+λGj

y ^ i 1 = y ^ i 0 + f 1 ( x i ) = f 0 ( x i ) + f 1 ( x i ) \hat{y}_i^1=\hat{y}_i^0+f_1(x_i)=f_0(x_i)+f_1(x_i) y^i1=y^i0+f1(xi)=f0(xi)+f1(xi)

  1. 根据预测值,划分第二棵树
    y ^ i 2 = y ^ i 1 + f 2 ( x i ) = f 0 ( x i ) + f 1 ( x i ) + f 2 ( x i ) \hat{y}_i^2=\hat{y}_i^1+f_2(x_i)=f_0(x_i)+f_1(x_i)+f_2(x_i) y^i2=y^i1+f2(xi)=f0(xi)+f1(xi)+f2(xi)

  2. 重复2~4,得到最终的学习器

    y ^ i = ∑ k = 1 K f k ( x i ) \hat{y}_i=\sum_{k=1}^Kf_k({x}_i) y^i=k=1Kfk(xi)

3单棵树的构建

XGB算法中单颗树的构建,不再是GBDT中采用CART回归树的方法构建。单同样是基于优化目标函数(通俗的讲就是如何让loss更小)的思想构建的,只不过在优化目标函数时考虑了二阶导数和正则项,而GBDT仅考虑了一阶导数。下面先看XGB的目标函数:

目标函数

O b j = ∑ i l ( y ^ i , y i ) + ∑ k Ω ( f k ) w h e r e       Ω ( f ) = γ T + 1 2 λ ∥ w ∥ 2 \begin{gathered} Obj=\sum_{i}l(\hat{y}_{i},y_{i})+\sum_{k}\Omega(f_{k}) \\ \mathrm{where}\:\ \ \ \Omega(f)=\gamma T+\frac{1}{2}\lambda\|w\|^{2} \end{gathered} Obj=il(y^i,yi)+kΩ(fk)where   Ω(f)=γT+21λw2

公式(2)第一部分是损失函数,第二部分是对模型复杂度的惩罚项(正则项);上面列出的是树形结构的惩罚项。如果是线性回归结构就是L1 L2正则项。 Υ 和 λ Υ和λ Υλ是超参数,T表示给定一棵树的叶子节点的数量。 ∣ ∣ w ∣ ∣ 2 ||w||^2 ∣∣w2表示每棵树叶子节点上的输出分数的平方(相当于L2正则),

对于第t棵树
O b j ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) Obj^{(t)}=\sum_{i=1}^nl(y_i,\hat{y}_i^{(t-1)}+f_t(\mathbf{x}_i))+\Omega(f_t) Obj(t)=i=1nl(yi,y^i(t1)+ft(xi))+Ω(ft)
采用泰勒公式
f ( x + Δ x ) ≃ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+\Delta x)\simeq f(x)+f'(x)\Delta x+\frac{1}{2}f''(x)\Delta x^{2} f(x+Δx)f(x)+f(x)Δx+21f′′(x)Δx2
展开:
f ( y ^ i ( t − 1 ) + f t ( X i ) ) = l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) f(\hat y_i^{(t-1)}+f_t(X_i))=l(y_i,\hat y_i^{(t-1)}+f_t(x_i))\\ f(y^i(t1)+ft(Xi))=l(yi,y^i(t1)+ft(xi))
映射倒泰勒公式中,这里:
x = y ^ i ( t − 1 ) ,   △ x = f t ( x i ) x=\hat y_i^{(t-1)}, \ \ △x=f_t(x_i)\\ x=y^i(t1),  x=ft(xi)
所以:
f ( x ) = f ( y ^ i ( t − 1 ) ) = l ( y i , y ^ i ( t − 1 ) ) f(x)=f(\hat y_i^{(t-1)})=l(y_i,\hat y_i^{(t-1)}) f(x)=f(y^i(t1))=l(yi,y^i(t1))

O b j ( t ) ≃ ∑ i = 1 n [ l ( y i , y ^ ( t − 1 ) ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) where     g i   =   ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) )     and      h i   =   ∂ y ^ ( t − 1 ) 2 l ( y i , y ^ ( t − 1 ) ) \begin{gathered} Obj^{(t)}\simeq\sum_{i=1}^n[l(y_i,\hat{y}^{(t-1)})+g_if_t({x}_i)+\frac{1}{2}h_if_t^2({x}_i)]+\Omega(f_t) \\ \text{where}\ \ \ \ g_i\:=\:\partial_{\hat{y}^{(t-1)}}l(y_i,\hat{y}^{(t-1)})\:\ \ \text{and}\:\ \ h_i\:=\:\partial_{\hat{y}^{(t-1)}}^2l(y_i,\hat{y}^{(t-1)}) \end{gathered} Obj(t)i=1n[l(yi,y^(t1))+gift(xi)+21hift2(xi)]+Ω(ft)where    gi=y^(t1)l(yi,y^(t1))  and  hi=y^(t1)2l(yi,y^(t1))

这里插播一点:

当损失函数是平方损失函数时:
g i = ∂ y ^ ( t − 1 ) ( y ^ ( t − 1 ) − y i ) 2 = 2 ( y ^ ( t − 1 ) − y i ) h i = ∂ y ^ ( t − 1 ) 2 ( y i − y ^ ( t − 1 ) ) 2 = 2 g_i=\partial_{\hat{y}^{(t-1)}}(\hat{y}^{(t-1)}-y_i)^2=2(\hat{y}^{(t-1)}-y_i)\quad h_i=\partial_{\hat{y}^{(t-1)}}^2(y_i-\hat{y}^{(t-1)})^2=2 gi=y^(t1)(y^(t1)yi)2=2(y^(t1)yi)hi=y^(t1)2(yiy^(t1))2=2
这时与直接展开得到的结果一致:
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 \begin{aligned} Obj^{(t)}& =\sum_{i=1}^{n}l(y_{i},\hat{y}_{i}^{(t)})+\sum_{i=1}^{t}\Omega(f_{i}) \\ &=\sum_{i=1}^{n}l\left(y_{i},\hat{y}_{i}^{(t-1)}+f_{t}(x_{i})\right)+\Omega(f_{t})+constant \end{aligned} Obj(t)=i=1nl(yi,y^i(t))+i=1tΩ(fi)=i=1nl(yi,y^i(t1)+ft(xi))+Ω(ft)+constant

O b j ( t ) = ∑ i = 1 n ( y i − ( y ^ i ( t − 1 ) + f t ( x i ) ) ) 2 + Ω ( f t ) + c o n s t a n t = ∑ i = 1 n [ 2 ( y ^ i ( t − 1 ) − y i ) f t ( x i ) + f t ( x i ) 2 ] + Ω ( f t ) + c o n s t a n t \begin{aligned} Obj^{(t)}& =\sum_{i=1}^n\left(y_i-(\hat{y}_i^{(t-1)}+f_t(x_i))\right)^2+\Omega(f_t)+constant \\ &=\sum_{i=1}^{n}\left[2(\hat{y}_{i}^{(t-1)}-y_{i})f_{t}(x_{i})+f_{t}(x_{i})^{2}\right]+\Omega(f_{t})+constant \end{aligned} Obj(t)=i=1n(yi(y^i(t1)+ft(xi)))2+Ω(ft)+constant=i=1n[2(y^i(t1)yi)ft(xi)+ft(xi)2]+Ω(ft)+constant

继续接插播之前的内容:

l ( y i , y ^ i ( t − 1 ) ) l(y_i,\hat y_i^{(t-1)}) l(yi,y^i(t1))为常数项,在求梯度时,常数项可以不考虑。所以我们损失函数化简为:
O b j ~ ( t ) = ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) \tilde{Obj}^{(t)}=\sum_{i=1}^n\Big[g_if_t({x}_i)+\frac{1}{2}h_if_t^2({x}_i)\Big]+\Omega(f_t) Obj~(t)=i=1n[gift(xi)+21hift2(xi)]+Ω(ft)
到目前只对第一项 损失函数做了改进,而没有考虑第二项 惩罚项 复杂度函数。

先将决策树表示为函数形式:
f t ( x ) = w q ( x ) , w ∈ R T , q   ; R d → { 1 , 2 , ⋯   , T } f_t(x)=w_{q(x)},\quad w\in\mathbf{R}^T,q\:;\mathbf{R}^d\to\{1,2,\cdots,T\} ft(x)=wq(x),wRT,q;Rd{1,2,,T}
T T T代表总共有几个叶子节点, w w w就是某个叶子节点的值(也叫权重), q ( x ) q(x) q(x)用于判断一条数据落入那个叶子节点。

所以惩罚项为:
Ω ( f t ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \Omega(f_t)=\gamma T+\frac{1}{2}\lambda\sum_{j=1}^{T}w_j^2 Ω(ft)=γT+21λj=1Twj2
加入惩罚项后重写目标函数:
O b j ~ ( t ) = ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + γ 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 \begin{gathered} \tilde{Obj}^{(t)} =\sum_{i=1}^{n}\Big[g_{i}f_{t}({x}_{i})+\frac12h_{i}f_{t}^{2}({x}_{i})\Big]+\gamma T+\frac12\lambda\sum_{j=1}^{T}w_{j}^{2} \\ =\sum_{j=1}^T\Bigg[(\sum_{i\in I_j}g_i)w_j+\frac{1}{2}(\sum_{i\in I_j}h_i+\lambda)w_j^2\Bigg]+\gamma T \end{gathered} Obj~(t)=i=1n[gift(xi)+21hift2(xi)]+γT+21λj=1Twj2=j=1T[(iIjgi)wj+21(iIjhi+λ)wj2]+γT
其中 I j = { i ∣ q ( x i ) = j } I_j=\{i|q(x_i)=j\} Ij={iq(xi)=j}表示一个叶子节点内全部的数据,公式从第一行转换到第二行只是数据打包到了每个叶子内,换了中求和的方式。

再做如下变换:
G j = ∑ i ∈ I j g i   H j = ∑ i ∈ I j h i G_j=\sum_{i\in I_j}g_i\:H_j=\sum_{i\in I_j}h_i Gj=iIjgiHj=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 \begin{gathered} Obj^{(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}+\lambda)w_{j}^{2}\right]+\gamma T \\ =\sum_{j=1}^{T}\left[G_{j}w_{j}+\frac{1}{2}(H_{j}+\lambda)w_{j}^{2}\right]+\gamma T \end{gathered} Obj(t)=j=1T (iIjgi)wj+21(iIjhi+λ)wj2 +γT=j=1T[Gjwj+21(Hj+λ)wj2]+γT

至此对目标函数的优化完成,求其极小值,令其一阶导数为0(或者用高中的知识点,一元二次方程最小值处 x = − b / 2 a , y = − b 2 / 4 a x=-b/2a,y=-b^2/4a x=b/2ay=b2/4a):
∂ O b j ( t ) ∂ w j = 0 \frac{\partial Obj^{(t)}}{\partial w_j} = 0 wjObj(t)=0
w j = − G j H j + λ w_j=-\frac{G_j}{H_j+λ} wj=Hj+λGj
w j w_j wj取上述值时,目标函数取得最小值,这就得到了最好的树。最小的目标函数值为:
O b j ( t ) = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T Obj^{(t)}=-\frac{1}{2}\sum_{j=1}^{T}\frac{G_j^2}{H_j+λ}+γT Obj(t)=21j=1THj+λGj2+γT

目标函数设计好了,那么怎么根据目标函数来生成最优的第k颗树呢?可以暴力枚举所有的划分方式,不论树的深度把全部能划分的树全列举出来,再比较目标函数Obj的值,看那个树的Obj最小,就选择那棵树。

注意:这里采用的是暴力枚举的方法,不再是按CART树采用均方差最小的方式来划分了,CART树只能得到一个树结构,而不能穷尽所有的结构

显然暴力枚举的方法是不可取的,所以类似于一般决策树的构建,XGBoost也是采用精确贪心算法。下面解释精确贪心算法的逻辑。

精确贪心算法

每一次尝试去对已有的叶子加入一个分割。通过增益来判断,这个分割是不是最好的。对于一个具体的分割方案,增益计算如下:
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+λGR2HL+HR+λ(GL+GR)2]γ
中括号中,第一项是“左子树的分数”,第二项是“右子树的分数”,第三项是“分裂之前的分数”, γ \gamma γ是加入新叶子节点引入的复杂度代价。

对于每次树的扩展划分,需要枚举所有特征的所有取值。Gain越大越好。

决策树中,ID3通过“信息增益”来控制树的分支,c4.5通过“信息增益率”来控制树的分支,CART回归树通过“平方误差和”来控制树的分支,CART分类树通过“基尼系数”来控制树的分支,GBDT的基学习器采用的就是CART回归树,而XGB中的每棵树(基学习器)都是采用上文中给出的Gain来控制树的分支。

近似算法

因为精确贪心算法需要遍历所有特征和取值,当数据量非常大的时候,无法将所有数据加载进内存时,精确贪心算法会非常耗时,XGBoost的作者引进了近似算法Approximate Algorithm。

根据每个特征 K K K的特征值的分布情况,选择 l l l个候选点 S k = { s k 1 , . . . , s k 1 } S_k=\{s_{k1},...,s_{k1}\} Sk={sk1,...,sk1}将特征K的特征值划分到这 l l l个桶(buckets)里面,对每个桶内每个样本的 g i , h i g_i,h_i gi,hi累加。

划分好候选切分点之后,按照精确贪心算法描述的算法步骤进行选择最优切分特征与最优切分点,不同的是切分点被上述候选切分点所代替,但原理和操作过程是一样的。

候选切分点的生成方式之一是:分位数。下面是个实例。

在这里插入图片描述

G a i n = max ⁡ { G a i n , G 1 2 H 1 + λ + G 23 2 H 23 + λ − G 123 2 H 123 + λ − γ , G 12 2 H 12 + λ + G 3 2 H 3 + λ − G 123 2 H 123 + λ − γ } \begin{gathered}Gain=\max\{Gain,\frac{G_1^2}{H_1+\lambda}+\frac{G_{23}^2}{H_{23}+\lambda}-\frac{G_{123}^2}{H_{123}+\lambda}-\gamma,\\\frac{G_{12}^2}{H_{12}+\lambda}+\frac{G_3^2}{H_3+\lambda}-\frac{G_{123}^2}{H_{123}+\lambda}-\gamma\}\end{gathered} Gain=max{Gain,H1+λG12+H23+λG232H123+λG1232γ,H12+λG122+H3+λG32H123+λG1232γ}

可以按照global的方式提出候选切分点,也可以按照local的方式提出候选切分点。

global表示在生成树之前进行候选切分点的提取,即开始之前为整树做一次提取即可,在每次的节点划分时都使用已经提取好的候选切分点。

而local则是在每次节点划分时才进行候选切分点的提取。

global方式需要更多的候选点,即对候选点提取数量比local更多,因为没有像local方式一样每次节点划分时,对当前节点的样本进行细化,local方式更适合树深度较大的情况。local需要更少的候选切分点,当global方式有足够多的候选点时正确率与local相当。

在这里插入图片描述

加权分位数略图

实际上,XGBoost不是简单地按照样本个数进行分位,而是以二阶导数值 h i h_i hi 作为样本的权重进行划分。为了处理带权重的候选切分点的选取,作者提出了Weighted Quantile Sketch算法。加权分位数略图算法提出了一种数据结构,这种数据结构支持merge和prune操作。详细内容可以看论文,此处略。

4具体案例

参考xgboost原理分析以及实践_SCUT_Sam-CSDN博客_xgboost流程图

以二分类情况为例。15条数据,两个特征。这里为了简单起见,树的深度设置为3(max_depth=3),树的颗数设置为(num_boost_round=2),学习率为0.1(eta=0.1)。另外再设置两个正则的参数, λ = 1 , γ = 0 λ=1,γ=0 λ=1,γ=0
损失函数选择logloss

IDx1x2y
11-50
2250
33-21
4121
5201
66-51
7751
86-20
9720
10601
118-51
12951
1310-20
14820
15901

logloss交叉熵损失函数的一阶导数以及二阶导数:
g i = y ^ i − y i h i = y ^ i ∗ ( 1 − y ^ i ) g_i=\hat y_i-y_i\\ h_i=\hat y_i*(1-\hat y_i) gi=y^iyihi=y^i(1y^i)

  1. 建立第一课树(K=1)

从根节点开始分裂,共有15个数据。取增益Gain最大的点划分。
G a i n = 1 2 [ G L 2 H L 2 + λ + G R 2 H R 2 + λ − ( G L + G R ) 2 ( H L + H R ) 2 + λ ] − γ Gain=\frac{1}{2}\left[\frac{G_L^2}{H_L^2+\lambda}+\frac{G_R^2}{H_R^2+\lambda}-\frac{(GL+G_R)^2}{(H_L+H_R)^2+\lambda}\right]-\gamma Gain=21[HL2+λGL2+HR2+λGR2(HL+HR)2+λ(GL+GR)2]γ
要计算 g i g_i gi h i h_i hi那么初始的 y ^ i \hat y_i y^i应该定位多少?这里和GBDT一样,应该说和所有的Boosting算法一样,都需要一个初始值。而在xgboost里面,对于分类任务只需要初始化为(0,1)中的任意一个数都可以。具体来说就是参数base_score。(其默认值是0.5)

  • 关于初始值

XGBoost - wwwpy - 博客园 (cnblogs.com)这样回答:

样本不均衡时可以使用 分类问题中它就是分类的先验概率 如有1000个样本,300个正样本和700个负样本,则base_score就是0.3 对于回归来说这个分数默认为0.5,但是理论上而言这个分数应该设置为标签的均值更好

xgboost : base_score 参数的含义 - IT屋-程序员软件开发技术分享社区 (it1352.com)这样回答:

对于高度不平衡的数据,您可以将其初始化为更有意义的基础分数,以改进学习过程.理论上,只要选择合适的学习率并给它足够的训练步骤,起始基础分数应该不会影响结果.看看这个issue中作者的回答.参考:https://github.com/dmlc/xgboost/issues/799

这里也设base_score=0.5。然后我们就可以计算每个样本的一阶导数值和二阶导数值了。具体如下表:

ID123456789101112131415
gi0.50.5-0.5-0.5-0.5-0.5-0.50.50.5-0.5-0.5-0.50.50.5-0.5
hi0.250.250.250.250.250.250.250.250.250.250.250.250.250.250.25
  1. 计算每个划分点的Gain,这里以x1<2为例划分,其他雷同:

左子结点包含的样本 I L = [ 1 , 4 ] I_L=[1,4] IL=[1,4],右子节点包含的样本 I R = [ 2 , 3 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] I_R=[2,3,5,6,7,8,9,10,11,12,13,14,15] IR=[2,3,5,6,7,8,9,10,11,12,13,14,15]

左子结点的一阶导数和: G L = ∑ ( i ∈ I L ) g i = ( 0.5 − 0.5 ) = 0 G_L=\sum_{(i \in I_L)}g_i=(0.5-0.5)=0 GL=(iIL)gi=(0.50.5)=0

左子结点的二阶导数和: H L = ∑ ( i ∈ I L ) h i = ( 0.25 + 0.25 ) = 0.5 H_L=\sum_{(i \in I_L)}h_i=(0.25+0.25)=0.5 HL=(iIL)hi=(0.25+0.25)=0.5

右子结点的一阶导数和: G R = ∑ ( i ∈ I R ) g i = − 1.5 G_R=\sum_{(i \in I_R)}g_i=-1.5 GR=(iIR)gi=1.5

右子结点的二阶导数和: H R = ∑ ( i ∈ I R ) h i = 3.25 H_R=\sum_{(i \in I_R)}h_i=3.25 HR=(iIR)hi=3.25

增 益:

G a i n = [ G L 2 H L 2 + λ + G L 2 H L 2 + λ − ( G L + G R ) 2 ( H L + H R ) 2 + λ ] = 0.0557275541796 Gain=\left[\frac{G_L^2}{H_L^2+\lambda}+\frac{G_L^2}{H_L^2+\lambda}-\frac{(GL+G_R)^2}{(H_L+H_R)^2+\lambda}\right]=0.0557275541796 Gain=[HL2+λGL2+HL2+λGL2(HL+HR)2+λ(GL+GR)2]=0.0557275541796

其他的X1的特征值类似,计算归总到下面表 ( ( x 1 < 1 ) 是 I L 为空,数据全在 I R ,跟没分一样,所以 G a i n 为 0 ) ((x1<1)是I_L为空,数据全在I_R,跟没分一样,所以Gain为0) ((x1<1)IL为空,数据全在IR,跟没分一样,所以Gain0)

切分点23678910
GL00-0.5-1-1-1-2
HL0.511.2522.533.5
GR-1.5-1.5-1-0.5-0.5-0.50.5
HR3.252.752.51.751.250.750.25
Gain0.055720.12631-0.07686-0.04944-0.07685-0.080830.61520

同样的方法遍历X2的全部特征值:

切分点-2025
GL-0.50-1.5-1
HL0.751.52.253
GR-1-1.50-0.5
HR32.251.50.75
Gain-0.080830.218620.21862-0.08083

可见最大增益为x<10处的0.61520,以x1<10来进行分裂。

左子节点的样本集合 I L = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 14 , 15 ] I_L=[1,2,3,4,5,6,7,8,9,10,11,12,14,15] IL=[1,2,3,4,5,6,7,8,9,10,11,12,14,15]右子节点的样本集合 I R = [ 13 ] I_R=[13] IR=[13]

  1. 设置的最大深度是3,此时只有1层,所以还需要继续往下分裂。

右子节点此时只剩一个样本,不需要分裂了,也就是已经是叶子结点。可以计算其对应的叶子结点值了,按照公式:
w 1 = − G R H R + λ = − g 13 h 13 + 1 = − 0.5 1 + 0.25 = − 0.4 w_1=-\frac{G_R}{H_R+\lambda}=-\frac{g_{13}}{h_{13}+1}=-\frac{0.5}{1+0.25}=-0.4 w1=HR+λGR=h13+1g13=1+0.250.5=0.4
下面就是对左子结点IL进行分裂。分裂的时候把此时的结点看成根节点,其实就是循环上面的过程,同样也是需要遍历所有特征(x1,x2)的所有取值作为分裂点,选取增益最大的点。

所有X1,X2的值都遍历完后可以得到下表:

切分点236789
GL00-0.5-1-1-1
HL0.511.2522.53
GR-2-2-1.5-1-1-1
HR32.52.251.510.5
Gain0.111110.25397-0.08547-0.15555-0.103170.02777
切分点-2025
GL-0.5-0.5-2.5-1.5
HL0.751.252.252.75
GR-1.5-1.50.5-0.5
HR2.752.251.250.75
Gain-0.14603-0.085470.44444-0.14603

x 2 < 2 x2<2 x2<2时分裂可以获得最大的增益0.44444,以x2<2为分裂点。分裂后左右叶子结点的集合如下:

I L = [ 1 , 3 , 5 , 6 , 8 , 10 , 11 , 15 ] , I R = [ 2 , 4 , 7 , 9 , 12 , 14 ] I_L=[1,3,5,6,8,10,11,15],I_R=[2,4,7,9,12,14] IL=[1,3,5,6,8,10,11,15],IR=[2,4,7,9,12,14]

  1. 然后同样的方法继续分裂,这里直接给出最终结果:
    在这里插入图片描述

w1计算结果为-0.4,图上却是-0.04. 不要忘记学习率:这里其实和在GBDT中的处理一样,我们会以一个学习率来乘这个值,当完全取-0.4时说明学习率取1,这个时候很容易过拟合。所以每次得到叶子结点的值后需要乘上学习率eta,在前面我们已经设置了学习率是0.1。这里也是GBDT和xgboost一个共同点,大家都是通过学习率来进行Shrinkage,以减少过拟合的风险。

  1. 建立第2颗树(k=2)

其实过程和第一颗树完全一样。只不过对于 y ^ i \hat y_i y^i需要进行更新,也就是拟合第二颗树是在第一颗树预测的结果基础上。这和GBDT一样,因为大家都是Boosting思想的算法。

在第一颗树里面由于前面没有树,所以初始 y ^ i = 0.5 \hat y_i=0.5 y^i=0.5(自己设置的)。假设此时,模型只有这一颗树(K=1),那么模型对样例xi进行预测时,预测的结果表达是什么呢?

由加法模型可知:
y i K = ∑ k = 0 K f k ( x i )         y i 1 = f 0 ( x i ) + f 1 ( x i ) y_i^K=\sum_{k=0}^Kf_k(x_i) \ \ \ \ \ \ \ y_i^1=f_0(x_i)+f_1(x_i) yiK=k=0Kfk(xi)       yi1=f0(xi)+f1(xi)
f 1 ( x i ) f_1(x_i) f1(xi)是xi落在第一棵树上对应的叶子节点的值, f 0 ( x i ) f_0(x_i) f0(xi)呢?它并不是我们的初始值base_score。base_score是 y ^ i \hat y_i y^i的值,是 f 0 ( x i ) f_0(x_i) f0(xi)取sigmoid函数之后的结果。所以
f 0 ( x i ) = l n y 1 − y = 0 f_0(x_i)=ln\frac{y}{1-y}=0 f0(xi)=ln1yy=0
所以第一颗树预测的结果就是
y i 1 = f 0 ( x i ) + f 1 ( x i ) = 0 + w q ( x i ) y_i^1=f_0(x_i)+f_1(x_i)=0+w_{q(x_i)} yi1=f0(xi)+f1(xi)=0+wq(xi)
对应的预测结果就是
y ^ i = 1 1 − e − y i 1 \hat y_i=\frac{1}{1-e^{-y_i^1}} y^i=1eyi11
比如对于ID=1的样本,其落在-0.04这个节点。那么经过sigmod映射后的值为 y ^ 1 = 1 1 − e − ( 0 − 0.04 ) ) = 0.49001 \hat y_1=\frac{1}{1-e^{-(0-0.04))}}=0.49001 y^1=1e(00.04))1=0.49001

(其实当训练次数K足够多的时候,初始化这个值几乎不起作用的,这个在官网文档上有说明)

所以,我们可以得到第一棵树预测的结果为下表(预测后将其映射成概率)

ID y ^ i \hat y_i y^i
10.490001
20.494445
30.522712
40.494445
50.522712
60.522712
70.494445
80.522712
90.494445
100.522712
110.522712
120.509999
130.490001
140.494445
150.522712

有了这个之后,就可以计算所有样本新的一阶导数和二阶导数的值了。具体如下表:

ID g i g_i gi h i h_i hi
10.4900013208390.249900026415
20.4944446682930.24996913829
3-0.4772883653640.249484181652
4-0.5055553317070.24996913829
5-0.4772883653640.249484181652
6-0.4772883653640.249484181652
7-0.5055553317070.24996913829
80.5227116346360.249484181652
90.4944446682930.24996913829
10-0.4772883653640.249484181652
11-0.4772883653640.249484181652
12-0.4900013208390.24990002641513
140.4944446682930.24996913829
15-0.4772883653640.249484181652

之后,我们和第一颗树建立的时候一样以公式(10)去建树。拟合完后第二颗树如下图:

在这里插入图片描述

  1. 最终的预测结果为

第一课树:

叶子12345
样本13 5 6 8 10 11 152 4 7 9 141213
f 1 ( x i ) f_1(x_i) f1(xi)-0.040.09090910.0222220.04-0.04

第二棵树:

叶子12345
样本13 5 6 8 10 11 1542 7 9 12 1413
f 2 ( x i ) f_2(x_i) f2(xi)-0.03920320.08523990.0404454-0.0216811-0.0392032

预测结果:

ID y i 2 = f 0 ( x i ) + f 1 ( x i ) + f 2 ( x i ) y_i^2=f_0(x_i)+f_1(x_i)+f_2(x_i) yi2=f0(xi)+f1(xi)+f2(xi) y ^ i = 1 1 + e − y i 2 \hat y_i=\frac{1}{1+e^{-y_i^2}} y^i=1+eyi21
1-0.0790.48
2-0.2440.439
30.1760.544
40.0630.516
50.1760.544
60.1760.544
70.0010.5
80.1760.544
90.0010.5
100.1760.544
110.1760.544
120.0180.504
13-0.0790.48
140.0010.5
150.1760.544

5代码实现

XGBoost的并没有包含在sklearn包中,而是一个单独的包,但是他提供了sklearn接口。在使用时可以用XGboost原生的一套流程,也可以采用sklearn的一套流程。

没有安装xgboost包的首先命令行运行一下命令,安装一下

pip install xgboost

以二分类问题为例:

import xgboost as xgb #导入xgboost包

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) #拆分训练集和验证集

'''生成xgb结构数据'''
import xgboost as xgb
train_xgb = xgb.DMatrix(X_train, label=y_train)
test_xgb = xgb.DMatrix(X_test, label=y_test)

'''定义xgb模型参数'''
params = {'booster': 'gbtree',# 基分类器的烈性
#          'objective': 'multi:softmax',
          'num_class': 9,#几分类
         'objective': 'multi:softprob',
        'verbosity':2, #打印消息的详细程度
#         'objective':'binary:logistic',# 目标函数
        'tree_method':'auto', # 这是xgb框架自带的训练方法,可选参数为[auto,exact,approx,hist,gpu_hist]
#         'eval_metric': 'auc', # 评价指标
#         'scale_pos_weight':[1, 3, 1], # 正样本的权重,在二分类模型中,如果两个分类的样本比例失衡,可以设置该参数,模型效果会更高。一般取值:负样本数/正样本数,一般模型里偏少的部分会认为是正样本。
          
        'max_depth': 6, #树的深度,默认为6,一般取值[3,10] 越大偏差越小,方差越大,需综合考虑时间及拟合性。
          
        'min_child_weight': 3, #分裂叶子节点中样本权重和的最小值,如果新分裂的节点的样本权重和小于min_child_weight则停止分裂,默认为1,取值范围[0,],当值越大时,越容易欠拟合,当值越小时,越容易过拟合。
          
        'gamma':0, #别名min_split_loss 制定节点分裂所需的最小损失很熟下降值,节点分裂时,只有损失函数的减小值大于等于gamma,节点才会分裂,gamma越大,算法越保守,取值范围为[0,] 【0,1,5】
          
        'subsample': 0.8, #训练每棵树时子采样比例,默认为1,一般在[0.5,1]之间,调节该参数可以防止过拟合。
          
        'colsample_bytree':0.7, #训练每棵树时,使用特征占全部特征的比例,默认为1,典型值为[0.5,1],调节该参数可以防止过拟合

        'alpha':1, #别名reg_alpha,L1正则化,在高维度的情况下,调节该参数可以加快算法的速度,增大该值将是模型更保守,一般我们做特征选择的时候会用L1正则项,
          
        'lambda': 2, #L2正则化,调节、、增大该参数可以减少过拟合,默认值为1
          
        'eta': 0.1, #别名learning_rate 学习率一般越小越好,只是耗时会更长
          
        'n_estimators':500, #基学习器的个数,越大越好,偏差越小,但耗时会增加
          
        'max_delat_step':2, #限制每棵树权重改变的最大步长,默认值为0,及没有约束,如果为正值,则这个算法更加保守,通常不需要设置该参数,但是当样本十分不平衡时,对逻辑回归有帮助。

        'nthread': -1,# 有多少处理器可以使用,默认为1,-1表示没有限制。
        'silent': 1, #默认为0,不输出中间过程,=1,输出中间过程
        'seed' : 2023, #随机种子
        'is_unbalance':True}

'''训练模型'''
model1 = xgb.train(params,train_xgb,evals=[(train_xgb,'train'),(test_xgb,'val')], num_boost_round=100,early_stopping_rounds=10,verbose_eval=5,xgb_model=None)


'''查看训练集(测试机替换为test_xgb,y_test)各项指标'''
y_score = model1.predict(train_xgb)
y_pred = np.argmax(y_score,axis=1)

from sklearn.metrics import roc_auc_score, accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report
# display(pd.DataFrame(confusion_matrix(y_train, y_pred),columns=['prediction negetive','prediction positive'], index=['condition negtive', 'condition positive']))
# print 'AUC:',roc_auc_score(y_train, y_pred)
print 'ACC:',accuracy_score(y_train, y_pred)
print 'Precision:',precision_score(y_train, y_pred, average='micro')
print 'Recall:',recall_score(y_train, y_pred, average='micro')
print 'F1-score: ', f1_score(y_train, y_pred, average='micro')
print 'report:\n',classification_report(y_train,y_pred)
  • 4
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值