赞
踩
本系列重点在浅显易懂,快速上手。不进行过多的理论讲解:也就是不去深究what,而是关注how。全文围绕以下三个问题展开:
1)长什么样?
2)解决什么问题?
3)怎么实现?
3.1)从数学讲,原理
3.2)具体实例
3.3)从代码上讲,如何掉包实现
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=1∑Kfk(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结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
有监督,分类,回归,排序
给定初始常数项 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)=常数
划分第一课树,采用近似精确贪心算法划分,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]−γ
求解所有叶子节点的值(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)=lr∗w1=−lr∗Hj+λ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)
根据预测值,划分第二棵树
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~4,得到最终的学习器
y ^ i = ∑ k = 1 K f k ( x i ) \hat{y}_i=\sum_{k=1}^Kf_k({x}_i) y^i=k=1∑Kfk(xi)
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
公式(2)第一部分是损失函数,第二部分是对模型复杂度的惩罚项(正则项);上面列出的是树形结构的惩罚项。如果是线性回归结构就是L1 L2正则项。 Υ 和 λ Υ和λ Υ和λ是超参数,T表示给定一棵树的叶子节点的数量。 ∣ ∣ w ∣ ∣ 2 ||w||^2 ∣∣w∣∣2表示每棵树叶子节点上的输出分数的平方(相当于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=1∑nl(yi,y^i(t−1)+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(t−1)+ft(Xi))=l(yi,y^i(t−1)+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(t−1), △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(t−1))=l(yi,y^i(t−1))
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
)
)
这里插播一点:
当损失函数是平方损失函数时:
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^(t−1)(y^(t−1)−yi)2=2(y^(t−1)−yi)hi=∂y^(t−1)2(yi−y^(t−1))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
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
继续接插播之前的内容:
l
(
y
i
,
y
^
i
(
t
−
1
)
)
l(y_i,\hat y_i^{(t-1)})
l(yi,y^i(t−1))为常数项,在求梯度时,常数项可以不考虑。所以我们损失函数化简为:
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=1∑n[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),w∈RT,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=1∑Twj2
加入惩罚项后重写目标函数:
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
其中
I
j
=
{
i
∣
q
(
x
i
)
=
j
}
I_j=\{i|q(x_i)=j\}
Ij={i∣q(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=i∈Ij∑giHj=i∈Ij∑hi
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
至此对目标函数的优化完成,求其极小值,令其一阶导数为0(或者用高中的知识点,一元二次方程最小值处
x
=
−
b
/
2
a
,
y
=
−
b
2
/
4
a
x=-b/2a,y=-b^2/4a
x=−b/2a,y=−b2/4a):
∂
O
b
j
(
t
)
∂
w
j
=
0
\frac{\partial Obj^{(t)}}{\partial w_j} = 0
∂wj∂Obj(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=1∑THj+λ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+λGR2−HL+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
+
λ
−
γ
}
可以按照global的方式提出候选切分点,也可以按照local的方式提出候选切分点。
global表示在生成树之前进行候选切分点的提取,即开始之前为整树做一次提取即可,在每次的节点划分时都使用已经提取好的候选切分点。
而local则是在每次节点划分时才进行候选切分点的提取。
global方式需要更多的候选点,即对候选点提取数量比local更多,因为没有像local方式一样每次节点划分时,对当前节点的样本进行细化,local方式更适合树深度较大的情况。local需要更少的候选切分点,当global方式有足够多的候选点时正确率与local相当。
实际上,XGBoost不是简单地按照样本个数进行分位,而是以二阶导数值 h i h_i hi 作为样本的权重进行划分。为了处理带权重的候选切分点的选取,作者提出了Weighted Quantile Sketch算法。加权分位数略图算法提出了一种数据结构,这种数据结构支持merge和prune操作。详细内容可以看论文,此处略。
参考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
。
ID | x1 | x2 | y |
---|---|---|---|
1 | 1 | -5 | 0 |
2 | 2 | 5 | 0 |
3 | 3 | -2 | 1 |
4 | 1 | 2 | 1 |
5 | 2 | 0 | 1 |
6 | 6 | -5 | 1 |
7 | 7 | 5 | 1 |
8 | 6 | -2 | 0 |
9 | 7 | 2 | 0 |
10 | 6 | 0 | 1 |
11 | 8 | -5 | 1 |
12 | 9 | 5 | 1 |
13 | 10 | -2 | 0 |
14 | 8 | 2 | 0 |
15 | 9 | 0 | 1 |
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^i−yihi=y^i∗(1−y^i)
从根节点开始分裂,共有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。然后我们就可以计算每个样本的一阶导数值和二阶导数值了。具体如下表:
ID | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
gi | 0.5 | 0.5 | -0.5 | -0.5 | -0.5 | -0.5 | -0.5 | 0.5 | 0.5 | -0.5 | -0.5 | -0.5 | 0.5 | 0.5 | -0.5 |
hi | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 | 0.25 |
左子结点包含的样本 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=∑(i∈IL)gi=(0.5−0.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=∑(i∈IL)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=∑(i∈IR)gi=−1.5
右子结点的二阶导数和: H R = ∑ ( i ∈ I R ) h i = 3.25 H_R=\sum_{(i \in I_R)}h_i=3.25 HR=∑(i∈IR)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,跟没分一样,所以Gain为0)
切分点 | 2 | 3 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|
GL | 0 | 0 | -0.5 | -1 | -1 | -1 | -2 |
HL | 0.5 | 1 | 1.25 | 2 | 2.5 | 3 | 3.5 |
GR | -1.5 | -1.5 | -1 | -0.5 | -0.5 | -0.5 | 0.5 |
HR | 3.25 | 2.75 | 2.5 | 1.75 | 1.25 | 0.75 | 0.25 |
Gain | 0.05572 | 0.12631 | -0.07686 | -0.04944 | -0.07685 | -0.08083 | 0.61520 |
同样的方法遍历X2的全部特征值:
切分点 | -2 | 0 | 2 | 5 |
---|---|---|---|---|
GL | -0.5 | 0 | -1.5 | -1 |
HL | 0.75 | 1.5 | 2.25 | 3 |
GR | -1 | -1.5 | 0 | -0.5 |
HR | 3 | 2.25 | 1.5 | 0.75 |
Gain | -0.08083 | 0.21862 | 0.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]
右子节点此时只剩一个样本,不需要分裂了,也就是已经是叶子结点。可以计算其对应的叶子结点值了,按照公式:
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的值都遍历完后可以得到下表:
切分点 | 2 | 3 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|
GL | 0 | 0 | -0.5 | -1 | -1 | -1 |
HL | 0.5 | 1 | 1.25 | 2 | 2.5 | 3 |
GR | -2 | -2 | -1.5 | -1 | -1 | -1 |
HR | 3 | 2.5 | 2.25 | 1.5 | 1 | 0.5 |
Gain | 0.11111 | 0.25397 | -0.08547 | -0.15555 | -0.10317 | 0.02777 |
切分点 | -2 | 0 | 2 | 5 |
---|---|---|---|---|
GL | -0.5 | -0.5 | -2.5 | -1.5 |
HL | 0.75 | 1.25 | 2.25 | 2.75 |
GR | -1.5 | -1.5 | 0.5 | -0.5 |
HR | 2.75 | 2.25 | 1.25 | 0.75 |
Gain | -0.14603 | -0.08547 | 0.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],
w1计算结果为-0.4,图上却是-0.04. 不要忘记学习率:这里其实和在GBDT中的处理一样,我们会以一个学习率来乘这个值,当完全取-0.4时说明学习率取1,这个时候很容易过拟合。所以每次得到叶子结点的值后需要乘上学习率eta,在前面我们已经设置了学习率是0.1。这里也是GBDT和xgboost一个共同点,大家都是通过学习率来进行Shrinkage,以减少过拟合的风险。
其实过程和第一颗树完全一样。只不过对于 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=0∑Kfk(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)=ln1−yy=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=1−e−yi11
比如对于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=1−e−(0−0.04))1=0.49001
(其实当训练次数K足够多的时候,初始化这个值几乎不起作用的,这个在官网文档上有说明)
所以,我们可以得到第一棵树预测的结果为下表(预测后将其映射成概率)
ID | y ^ i \hat y_i y^i |
---|---|
1 | 0.490001 |
2 | 0.494445 |
3 | 0.522712 |
4 | 0.494445 |
5 | 0.522712 |
6 | 0.522712 |
7 | 0.494445 |
8 | 0.522712 |
9 | 0.494445 |
10 | 0.522712 |
11 | 0.522712 |
12 | 0.509999 |
13 | 0.490001 |
14 | 0.494445 |
15 | 0.522712 |
有了这个之后,就可以计算所有样本新的一阶导数和二阶导数的值了。具体如下表:
ID | g i g_i gi | h i h_i hi |
---|---|---|
1 | 0.490001320839 | 0.249900026415 |
2 | 0.494444668293 | 0.24996913829 |
3 | -0.477288365364 | 0.249484181652 |
4 | -0.505555331707 | 0.24996913829 |
5 | -0.477288365364 | 0.249484181652 |
6 | -0.477288365364 | 0.249484181652 |
7 | -0.505555331707 | 0.24996913829 |
8 | 0.522711634636 | 0.249484181652 |
9 | 0.494444668293 | 0.24996913829 |
10 | -0.477288365364 | 0.249484181652 |
11 | -0.477288365364 | 0.249484181652 |
12 | -0.490001320839 | 0.24990002641513 |
14 | 0.494444668293 | 0.24996913829 |
15 | -0.477288365364 | 0.249484181652 |
之后,我们和第一颗树建立的时候一样以公式(10)去建树。拟合完后第二颗树如下图:
第一课树:
叶子 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
样本 | 1 | 3 5 6 8 10 11 15 | 2 4 7 9 14 | 12 | 13 |
f 1 ( x i ) f_1(x_i) f1(xi) | -0.04 | 0.0909091 | 0.022222 | 0.04 | -0.04 |
第二棵树:
叶子 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
样本 | 1 | 3 5 6 8 10 11 15 | 4 | 2 7 9 12 14 | 13 |
f 2 ( x i ) f_2(x_i) f2(xi) | -0.0392032 | 0.0852399 | 0.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+e−yi21 |
---|---|---|
1 | -0.079 | 0.48 |
2 | -0.244 | 0.439 |
3 | 0.176 | 0.544 |
4 | 0.063 | 0.516 |
5 | 0.176 | 0.544 |
6 | 0.176 | 0.544 |
7 | 0.001 | 0.5 |
8 | 0.176 | 0.544 |
9 | 0.001 | 0.5 |
10 | 0.176 | 0.544 |
11 | 0.176 | 0.544 |
12 | 0.018 | 0.504 |
13 | -0.079 | 0.48 |
14 | 0.001 | 0.5 |
15 | 0.176 | 0.544 |
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)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。