6. XGBoost算法
XGBoost是陈天奇等人开发的一个开源机器学习项目,高效地实现了GBDT算法并进行了算法和工程上的许多改进,被广泛应用在Kaggle竞赛及其他许多机器学习竞赛中并取得了不错的成绩。XGBoost本质上还是一个GBDT,但是力争把速度和效率发挥到极致,所以叫X (Extreme) GBoosted,包括前面说过,两者都是boosting方法。XGBoost是一个优化的分布式梯度增强库,旨在实现高效,灵活和便携。 它在Gradient Boosting框架下实现机器学习算法。 XGBoost提供了并行树提升(也称为GBDT,GBM),可以快速准确地解决许多数据科学问题。 相同的代码在主要的分布式环境(Hadoop,SGE,MPI)上运行,并且可以解决超过数十亿个样例的问题。XGBoost利用了核外计算并且能够使数据科学家在一个主机上处理数亿的样本数据。最终,将这些技术进行结合来做一个端到端的系统以最少的集群系统来扩展到更大的数据集上。Xgboost以CART决策树为子模型,通过Gradient Tree Boosting实现多棵CART树的集成学习,得到最终模型。下面我们来看看XGBoost的最终模型构建:
引用陈天奇的论文,我们的数据为:
D
=
{
(
x
i
,
y
i
)
}
(
∣
D
∣
=
n
,
x
i
∈
R
m
,
y
i
∈
R
)
\mathcal{D}=\left\{\left(\mathbf{x}_{i}, y_{i}\right)\right\}\left(|\mathcal{D}|=n, \mathbf{x}_{i} \in \mathbb{R}^{m}, y_{i} \in \mathbb{R}\right)
D={(xi,yi)}(∣D∣=n,xi∈Rm,yi∈R)
(1) 构造目标函数:
假设有K棵树,则第i个样本的输出为
y
^
i
=
ϕ
(
x
i
)
=
∑
k
=
1
K
f
k
(
x
i
)
,
f
k
∈
F
\hat{y}_{i}=\phi\left(\mathrm{x}_{i}\right)=\sum_{k=1}^{K} f_{k}\left(\mathrm{x}_{i}\right), \quad f_{k} \in \mathcal{F}
y^i=ϕ(xi)=∑k=1Kfk(xi),fk∈F,其中,
F
=
{
f
(
x
)
=
w
q
(
x
)
}
(
q
:
R
m
→
T
,
w
∈
R
T
)
\mathcal{F}=\left\{f(\mathbf{x})=w_{q(\mathbf{x})}\right\}\left(q: \mathbb{R}^{m} \rightarrow T, w \in \mathbb{R}^{T}\right)
F={f(x)=wq(x)}(q:Rm→T,w∈RT)
因此,目标函数的构建为:
L
(
ϕ
)
=
∑
i
l
(
y
^
i
,
y
i
)
+
∑
k
Ω
(
f
k
)
\mathcal{L}(\phi)=\sum_{i} l\left(\hat{y}_{i}, y_{i}\right)+\sum_{k} \Omega\left(f_{k}\right)
L(ϕ)=i∑l(y^i,yi)+k∑Ω(fk)
其中,
∑
i
l
(
y
^
i
,
y
i
)
\sum_{i} l\left(\hat{y}_{i}, y_{i}\right)
∑il(y^i,yi)为loss function,
∑
k
Ω
(
f
k
)
\sum_{k} \Omega\left(f_{k}\right)
∑kΩ(fk)为正则化项。
(2) 叠加式的训练(Additive Training):
给定样本
x
i
x_i
xi,
y
^
i
(
0
)
=
0
\hat{y}_i^{(0)} = 0
y^i(0)=0(初始预测),
y
^
i
(
1
)
=
y
^
i
(
0
)
+
f
1
(
x
i
)
\hat{y}_i^{(1)} = \hat{y}_i^{(0)} + f_1(x_i)
y^i(1)=y^i(0)+f1(xi),
y
^
i
(
2
)
=
y
^
i
(
0
)
+
f
1
(
x
i
)
+
f
2
(
x
i
)
=
y
^
i
(
1
)
+
f
2
(
x
i
)
\hat{y}_i^{(2)} = \hat{y}_i^{(0)} + f_1(x_i) + f_2(x_i) = \hat{y}_i^{(1)} + f_2(x_i)
y^i(2)=y^i(0)+f1(xi)+f2(xi)=y^i(1)+f2(xi)…以此类推,可以得到:$ \hat{y}_i^{(K)} = \hat{y}_i^{(K-1)} + f_K(x_i)$ ,其中,$ \hat{y}_i^{(K-1)} $ 为前K-1棵树的预测结果,$ f_K(x_i)$ 为第K棵树的预测结果。
因此,目标函数可以分解为:
L
(
K
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
(
K
−
1
)
+
f
K
(
x
i
)
)
+
∑
k
Ω
(
f
k
)
\mathcal{L}^{(K)}=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(K-1)}+f_{K}\left(\mathrm{x}_{i}\right)\right)+\sum_{k} \Omega\left(f_{k}\right)
L(K)=i=1∑nl(yi,y^i(K−1)+fK(xi))+k∑Ω(fk)
由于正则化项也可以分解为前K-1棵树的复杂度加第K棵树的复杂度,因此:
L
(
K
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
(
K
−
1
)
+
f
K
(
x
i
)
)
+
∑
k
=
1
K
−
1
Ω
(
f
k
)
+
Ω
(
f
K
)
\mathcal{L}^{(K)}=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(K-1)}+f_{K}\left(\mathrm{x}_{i}\right)\right)+\sum_{k=1} ^{K-1}\Omega\left(f_{k}\right)+\Omega\left(f_{K}\right)
L(K)=∑i=1nl(yi,y^i(K−1)+fK(xi))+∑k=1K−1Ω(fk)+Ω(fK),由于
∑
k
=
1
K
−
1
Ω
(
f
k
)
\sum_{k=1} ^{K-1}\Omega\left(f_{k}\right)
∑k=1K−1Ω(fk)在模型构建到第K棵树的时候已经固定,无法改变,因此是一个已知的常数,可以在最优化的时候省去【简化正则化项】,故:
L
(
K
)
=
∑
i
=
1
n
l
(
y
i
,
y
^
i
(
K
−
1
)
+
f
K
(
x
i
)
)
+
Ω
(
f
K
)
\mathcal{L}^{(K)}=\sum_{i=1}^{n} l\left(y_{i}, \hat{y}_{i}^{(K-1)}+f_{K}\left(\mathrm{x}_{i}\right)\right)+\Omega\left(f_{K}\right)
L(K)=i=1∑nl(yi,y^i(K−1)+fK(xi))+Ω(fK)
(3) 使用泰勒级数近似目标函数:
L
(
K
)
≃
∑
i
=
1
n
[
l
(
y
i
,
y
^
(
K
−
1
)
)
+
g
i
f
K
(
x
i
)
+
1
2
h
i
f
K
2
(
x
i
)
]
+
Ω
(
f
K
)
\mathcal{L}^{(K)} \simeq \sum_{i=1}^{n}\left[l\left(y_{i}, \hat{y}^{(K-1)}\right)+g_{i} f_{K}\left(\mathrm{x}_{i}\right)+\frac{1}{2} h_{i} f_{K}^{2}\left(\mathrm{x}_{i}\right)\right]+\Omega\left(f_{K}\right)
L(K)≃i=1∑n[l(yi,y^(K−1))+gifK(xi)+21hifK2(xi)]+Ω(fK)
其中,
g
i
=
∂
y
^
(
t
−
1
)
l
(
y
i
,
y
^
(
t
−
1
)
)
g_{i}=\partial_{\hat{y}(t-1)} l\left(y_{i}, \hat{y}^{(t-1)}\right)
gi=∂y^(t−1)l(yi,y^(t−1))和
h
i
=
∂
y
^
(
t
−
1
)
2
l
(
y
i
,
y
^
(
t
−
1
)
)
h_{i}=\partial_{\hat{y}^{(t-1)}}^{2} l\left(y_{i}, \hat{y}^{(t-1)}\right)
hi=∂y^(t−1)2l(yi,y^(t−1))
在这里,我们补充下泰勒级数的相关知识:
在数学中,泰勒级数(英语:Taylor series)用无限项连加式——级数来表示一个函数,这些相加的项由函数在某一点的导数求得。具体的形式如下:
f
(
x
)
=
f
(
x
0
)
0
!
+
f
′
(
x
0
)
1
!
(
x
−
x
0
)
+
f
′
′
(
x
0
)
2
!
(
x
−
x
0
)
2
+
…
+
f
(
n
)
(
x
0
)
n
!
(
x
−
x
0
)
n
+
.
.
.
.
.
.
f(x)=\frac{f\left(x_{0}\right)}{0 !}+\frac{f^{\prime}\left(x_{0}\right)}{1 !}\left(x-x_{0}\right)+\frac{f^{\prime \prime}\left(x_{0}\right)}{2 !}\left(x-x_{0}\right)^{2}+\ldots+\frac{f^{(n)}\left(x_{0}\right)}{n !}\left(x-x_{0}\right)^{n}+......
f(x)=0!f(x0)+1!f′(x0)(x−x0)+2!f′′(x0)(x−x0)2+…+n!f(n)(x0)(x−x0)n+......
由于
∑
i
=
1
n
l
(
y
i
,
y
^
(
K
−
1
)
)
\sum_{i=1}^{n}l\left(y_{i}, \hat{y}^{(K-1)}\right)
∑i=1nl(yi,y^(K−1))在模型构建到第K棵树的时候已经固定,无法改变,因此是一个已知的常数,可以在最优化的时候省去【简化损失项】,故:
L
~
(
K
)
=
∑
i
=
1
n
[
g
i
f
K
(
x
i
)
+
1
2
h
i
f
K
2
(
x
i
)
]
+
Ω
(
f
K
)
\tilde{\mathcal{L}}^{(K)}=\sum_{i=1}^{n}\left[g_{i} f_{K}\left(\mathbf{x}_{i}\right)+\frac{1}{2} h_{i} f_{K}^{2}\left(\mathbf{x}_{i}\right)\right]+\Omega\left(f_{K}\right)
L~(K)=i=1∑n[gifK(xi)+21hifK2(xi)]+Ω(fK)
(4) 如何定义一棵树:
为了说明如何定义一棵树的问题,我们需要定义几个概念:
第一个概念是样本所在的节点位置 q ( x ) q(x) q(x),
第二个概念是有哪些样本落在节点j上 I j = { i ∣ q ( x i ) = j } I_{j}=\left\{i \mid q\left(\mathbf{x}_{i}\right)=j\right\} Ij={i∣q(xi)=j},
第三个概念是每个结点的预测值 w q ( x ) w_{q(x)} wq(x),
第四个概念是模型复杂度 Ω ( f K ) \Omega\left(f_{K}\right) Ω(fK),它可以由叶子节点的个数以及节点函数值来构建,
则: Ω ( f K ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \Omega\left(f_{K}\right) = \gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2} Ω(fK)=γT+21λ∑j=1Twj2。如下图的例子:
q
(
x
1
)
=
1
,
q
(
x
2
)
=
3
,
q
(
x
3
)
=
1
,
q
(
x
4
)
=
2
,
q
(
x
5
)
=
3
q(x_1) = 1,q(x_2) = 3,q(x_3) = 1,q(x_4) = 2,q(x_5) = 3
q(x1)=1,q(x2)=3,q(x3)=1,q(x4)=2,q(x5)=3,
I
1
=
{
1
,
3
}
,
I
2
=
{
4
}
,
I
3
=
{
2
,
5
}
I_1 = \{1,3\},I_2 = \{4\},I_3 = \{2,5\}
I1={1,3},I2={4},I3={2,5},
w
=
(
15
,
12
,
20
)
w = (15,12,20)
w=(15,12,20)
因此,目标函数用以上符号替代后:
L
~
(
K
)
=
∑
i
=
1
n
[
g
i
f
K
(
x
i
)
+
1
2
h
i
f
K
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{aligned} \tilde{\mathcal{L}}^{(K)} &=\sum_{i=1}^{n}\left[g_{i} f_{K}\left(\mathrm{x}_{i}\right)+\frac{1}{2} h_{i} f_{K}^{2}\left(\mathrm{x}_{i}\right)\right]+\gamma T+\frac{1}{2} \lambda \sum_{j=1}^{T} w_{j}^{2} \\ &=\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 \end{aligned}
L~(K)=i=1∑n[gifK(xi)+21hifK2(xi)]+γT+21λj=1∑Twj2=j=1∑T⎣⎡⎝⎛i∈Ij∑gi⎠⎞wj+21⎝⎛i∈Ij∑hi+λ⎠⎞wj2⎦⎤+γT
由于我们的目标就是最小化目标函数,现在的目标函数化简为一个关于w的二次函数:
L
~
(
K
)
=
∑
j
=
1
T
[
(
∑
i
∈
I
j
g
i
)
w
j
+
1
2
(
∑
i
∈
I
j
h
i
+
λ
)
w
j
2
]
+
γ
T
\tilde{\mathcal{L}}^{(K)}=\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
L~(K)=∑j=1T[(∑i∈Ijgi)wj+21(∑i∈Ijhi+λ)wj2]+γT,根据二次函数求极值的公式:
y
=
a
x
2
b
x
c
y=ax^2 bx c
y=ax2bxc求极值,对称轴在
x
=
−
b
2
a
x=-\frac{b}{2 a}
x=−2ab,极值为
y
=
4
a
c
−
b
2
4
a
y=\frac{4 a c-b^{2}}{4 a}
y=4a4ac−b2,因此:
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
以及
L
~
(
K
)
(
q
)
=
−
1
2
∑
j
=
1
T
(
∑
i
∈
I
j
g
i
)
2
∑
i
∈
I
j
h
i
+
λ
+
γ
T
\tilde{\mathcal{L}}^{(K)}(q)=-\frac{1}{2} \sum_{j=1}^{T} \frac{\left(\sum_{i \in I_{j}} g_{i}\right)^{2}}{\sum_{i \in I_{j}} h_{i}+\lambda}+\gamma T
L~(K)(q)=−21j=1∑T∑i∈Ijhi+λ(∑i∈Ijgi)2+γT
(5) 如何寻找树的形状:
不难发现,刚刚的讨论都是基于树的形状已经确定了计算
w
w
w和
L
L
L,但是实际上我们需要像学习决策树一样找到树的形状。因此,我们借助决策树学习的方式,使用目标函数的变化来作为分裂节点的标准。我们使用一个例子来说明:
例子中有8个样本,分裂方式如下,因此:
L
~
(
o
l
d
)
=
−
1
2
[
(
g
7
+
g
8
)
2
H
7
+
H
8
+
λ
+
(
g
1
+
.
.
.
+
g
6
)
2
H
1
+
.
.
.
+
H
6
+
λ
]
+
2
γ
L
~
(
n
e
w
)
=
−
1
2
[
(
g
7
+
g
8
)
2
H
7
+
H
8
+
λ
+
(
g
1
+
.
.
.
+
g
3
)
2
H
1
+
.
.
.
+
H
3
+
λ
+
(
g
4
+
.
.
.
+
g
6
)
2
H
4
+
.
.
.
+
H
6
+
λ
]
+
3
γ
L
~
(
o
l
d
)
−
L
~
(
n
e
w
)
=
1
2
[
(
g
1
+
.
.
.
+
g
3
)
2
H
1
+
.
.
.
+
H
3
+
λ
+
(
g
4
+
.
.
.
+
g
6
)
2
H
4
+
.
.
.
+
H
6
+
λ
−
(
g
1
+
.
.
.
+
g
6
)
2
h
1
+
.
.
.
+
h
6
+
λ
]
−
γ
\tilde{\mathcal{L}}^{(old)} = -\frac{1}{2}[\frac{(g_7 + g_8)^2}{H_7+H_8 + \lambda} + \frac{(g_1 +...+ g_6)^2}{H_1+...+H_6 + \lambda}] + 2\gamma \\ \tilde{\mathcal{L}}^{(new)} = -\frac{1}{2}[\frac{(g_7 + g_8)^2}{H_7+H_8 + \lambda} + \frac{(g_1 +...+ g_3)^2}{H_1+...+H_3 + \lambda} + \frac{(g_4 +...+ g_6)^2}{H_4+...+H_6 + \lambda}] + 3\gamma\\ \tilde{\mathcal{L}}^{(old)} - \tilde{\mathcal{L}}^{(new)} = \frac{1}{2}[ \frac{(g_1 +...+ g_3)^2}{H_1+...+H_3 + \lambda} + \frac{(g_4 +...+ g_6)^2}{H_4+...+H_6 + \lambda} - \frac{(g_1+...+g_6)^2}{h_1+...+h_6+\lambda}] - \gamma
L~(old)=−21[H7+H8+λ(g7+g8)2+H1+...+H6+λ(g1+...+g6)2]+2γL~(new)=−21[H7+H8+λ(g7+g8)2+H1+...+H3+λ(g1+...+g3)2+H4+...+H6+λ(g4+...+g6)2]+3γL~(old)−L~(new)=21[H1+...+H3+λ(g1+...+g3)2+H4+...+H6+λ(g4+...+g6)2−h1+...+h6+λ(g1+...+g6)2]−γ
因此,从上面的例子看出:分割节点的标准为
m
a
x
{
L
~
(
o
l
d
)
−
L
~
(
n
e
w
)
}
max\{\tilde{\mathcal{L}}^{(old)} - \tilde{\mathcal{L}}^{(new)} \}
max{L~(old)−L~(new)},即:
L
split
=
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
+
λ
]
−
γ
\mathcal{L}_{\text {split }}=\frac{1}{2}\left[\frac{\left(\sum_{i \in I_{L}} g_{i}\right)^{2}}{\sum_{i \in I_{L}} h_{i}+\lambda}+\frac{\left(\sum_{i \in I_{R}} g_{i}\right)^{2}}{\sum_{i \in I_{R}} h_{i}+\lambda}-\frac{\left(\sum_{i \in I} g_{i}\right)^{2}}{\sum_{i \in I} h_{i}+\lambda}\right]-\gamma
Lsplit =21[∑i∈ILhi+λ(∑i∈ILgi)2+∑i∈IRhi+λ(∑i∈IRgi)2−∑i∈Ihi+λ(∑i∈Igi)2]−γ
(6.1) 精确贪心分裂算法:
XGBoost在生成新树的过程中,最基本的操作是节点分裂。节点分裂中最重 要的环节是找到最优特征及最优切分点, 然后将叶子节点按照最优特征和最优切 分点进行分裂。选取最优特征和最优切分点的一种思路如下:首先找到所有的候 选特征及所有的候选切分点, 一一求得其
L
split
\mathcal{L}_{\text {split }}
Lsplit , 然后选择
L
s
p
l
i
t
\mathcal{L}_{\mathrm{split}}
Lsplit 最大的特征及 对应切分点作为最优特征和最优切分点。我们称此种方法为精确贪心算法。该算法是一种启发式算法, 因为在节点分裂时只选择当前最优的分裂策略, 而非全局最优的分裂策略。精确贪心算法的计算过程如下所示:
(6.2) 基于直方图的近似算法:
精确贪心算法在选择最优特征和最优切分点时是一种十分有效的方法。它计算了所有特征、所有切分点的收益, 并从中选择了最优的, 从而保证模型能比较好地拟合了训练数据。但是当数据不能完全加载到内存时,精确贪心算法会变得 非常低效,算法在计算过程中需要不断在内存与磁盘之间进行数据交换,这是个非常耗时的过程, 并且在分布式环境中面临同样的问题。为了能够更高效地选 择最优特征及切分点, XGBoost提出一种近似算法来解决该问题。 基于直方图的近似算法的主要思想是:对某一特征寻找最优切分点时,首先对该特征的所有切分点按分位数 (如百分位) 分桶, 得到一个候选切分点集。特征的每一个切分点都可以分到对应的分桶; 然后,对每个桶计算特征统计G和H得到直方图, G为该桶内所有样本一阶特征统计g之和, H为该桶内所有样本二阶特征统计h之和; 最后,选择所有候选特征及候选切分点中对应桶的特征统计收益最大的作为最优特征及最优切分点。基于直方图的近似算法的计算过程如下所示:
-
对于每个特征 k = 1 , 2 , ⋯ , m , k=1,2, \cdots, m, k=1,2,⋯,m, 按分位数对特征 k k k 分桶 Θ , \Theta, Θ, 可得候选切分点, S k = { S k 1 , S k 2 , ⋯ , S k l } 1 S_{k}=\left\{S_{k 1}, S_{k 2}, \cdots, S_{k l}\right\}^{1} Sk={Sk1,Sk2,⋯,Skl}1
-
对于每个特征 k = 1 , 2 , ⋯ , m , k=1,2, \cdots, m, k=1,2,⋯,m, 有:
G k v ← = ∑ j ∈ { j ∣ s k , v ≥ x j k > s k , v − 1 } g j H k v ← = ∑ j ∈ { j ∣ s k , v ≥ x j k > s k , v − 1 } h j \begin{array}{l} G_{k v} \leftarrow=\sum_{j \in\left\{j \mid s_{k, v} \geq \mathbf{x}_{j k}>s_{k, v-1\;}\right\}} g_{j} \\ H_{k v} \leftarrow=\sum_{j \in\left\{j \mid s_{k, v} \geq \mathbf{x}_{j k}>s_{k, v-1\;}\right\}} h_{j} \end{array} Gkv←=∑j∈{j∣sk,v≥xjk>sk,v−1}gjHkv←=∑j∈{j∣sk,v≥xjk>sk,v−1}hj -
类似精确贪心算法,依据梯度统计找到最大增益的候选切分点。
下面用一个例子说明基于直方图的近似算法:
假设有一个年龄特征,其特征的取值为18、19、21、31、36、37、55、57,我们需要使用近似算法找到年龄这个特征的最佳分裂点:
近似算法实现了两种候选切分点的构建策略:全局策略和本地策略。全局策略是在树构建的初始阶段对每一个特征确定一个候选切分点的集合, 并在该树每一层的节点分裂中均采用此集合计算收益, 整个过程候选切分点集合不改变。本地策略则是在每一次节点分裂时均重新确定候选切分点。全局策略需要更细的分桶才能达到本地策略的精确度, 但全局策略在选取候选切分点集合时比本地策略更简单。在XGBoost系统中, 用户可以根据需求自由选择使用精确贪心算法、近似算法全局策略、近似算法本地策略, 算法均可通过参数进行配置。
系统讲解 xgboost
XGBoost的参数设置(括号内的名称为sklearn接口对应的参数名字):
推荐博客:https://link.zhihu.com/?target=https%3A//blog.csdn.net/luanpeng825485697/article/details/79907149
推荐官方文档:https://link.zhihu.com/?target=https%3A//xgboost.readthedocs.io/en/latest/parameter.html
XGBoost的参数分为三种:
-
通用参数:(两种类型的booster,因为tree的性能比线性回归好得多,因此我们很少用线性回归。)
- booster:使用哪个弱学习器训练,默认gbtree,可选gbtree,gblinear 或dart
- nthread:用于运行XGBoost的并行线程数,默认为最大可用线程数
- verbosity:打印消息的详细程度。有效值为0(静默),1(警告),2(信息),3(调试)。
- Tree Booster的参数:
- eta(learning_rate):learning_rate,在更新中使用步长收缩以防止过度拟合,默认= 0.3,范围:[0,1];典型值一般设置为:0.01-0.2
- gamma(min_split_loss):默认= 0,分裂节点时,损失函数减小值只有大于等于gamma节点才分裂,gamma值越大,算法越保守,越不容易过拟合,但性能就不一定能保证,需要平衡。范围:[0,∞]
- max_depth:默认= 6,一棵树的最大深度。增加此值将使模型更复杂,并且更可能过度拟合。范围:[0,∞]
- min_child_weight:默认值= 1,如果新分裂的节点的样本权重和小于min_child_weight则停止分裂 。这个可以用来减少过拟合,但是也不能太高,会导致欠拟合。范围:[0,∞]
- max_delta_step:默认= 0,允许每个叶子输出的最大增量步长。如果将该值设置为0,则表示没有约束。如果将其设置为正值,则可以帮助使更新步骤更加保守。通常不需要此参数,但是当类极度不平衡时,它可能有助于逻辑回归。将其设置为1-10的值可能有助于控制更新。范围:[0,∞]
- subsample:默认值= 1,构建每棵树对样本的采样率,如果设置成0.5,XGBoost会随机选择一半的样本作为训练集。范围:(0,1]
- sampling_method:默认= uniform,用于对训练实例进行采样的方法。
- uniform:每个训练实例的选择概率均等。通常将subsample> = 0.5 设置 为良好的效果。
- gradient_based:每个训练实例的选择概率与规则化的梯度绝对值成正比,具体来说就是 g 2 + λ h 2 \sqrt{g^2+\lambda h^2} g2+λh2,subsample可以设置为低至0.1,而不会损失模型精度。
- colsample_bytree:默认= 1,列采样率,也就是特征采样率。范围为(0,1]
- lambda(reg_lambda):默认=1,L2正则化权重项。增加此值将使模型更加保守。
- alpha(reg_alpha):默认= 0,权重的L1正则化项。增加此值将使模型更加保守。
- tree_method:默认=auto,XGBoost中使用的树构建算法。
- auto:使用启发式选择最快的方法。
- 对于小型数据集,exact将使用精确贪婪()。
- 对于较大的数据集,approx将选择近似算法()。它建议尝试hist,gpu_hist,用大量的数据可能更高的性能。(gpu_hist)支持。external memory外部存储器。
- exact:精确的贪婪算法。枚举所有拆分的候选点。
- approx:使用分位数和梯度直方图的近似贪婪算法。
- hist:更快的直方图优化的近似贪婪算法。(LightGBM也是使用直方图算法)
- gpu_hist:GPU hist算法的实现。
- auto:使用启发式选择最快的方法。
- scale_pos_weight:控制正负权重的平衡,这对于不平衡的类别很有用。Kaggle竞赛一般设置sum(negative instances) / sum(positive instances),在类别高度不平衡的情况下,将参数设置大于0,可以加快收敛。
- num_parallel_tree:默认=1,每次迭代期间构造的并行树的数量。此选项用于支持增强型随机森林。
- monotone_constraints:可变单调性的约束,在某些情况下,如果有非常强烈的先验信念认为真实的关系具有一定的质量,则可以使用约束条件来提高模型的预测性能。(例如params_constrained[‘monotone_constraints’] = “(1,-1)”,(1,-1)我们告诉XGBoost对第一个预测变量施加增加的约束,对第二个预测变量施加减小的约束。)
- Linear Booster的参数:
- lambda(reg_lambda):默认= 0,L2正则化权重项。增加此值将使模型更加保守。归一化为训练示例数。
- alpha(reg_alpha):默认= 0,权重的L1正则化项。增加此值将使模型更加保守。归一化为训练示例数。
- updater:默认= shotgun。
- shotgun:基于shotgun算法的平行坐标下降算法。使用“ hogwild”并行性,因此每次运行都产生不确定的解决方案。
- coord_descent:普通坐标下降算法。同样是多线程的,但仍会产生确定性的解决方案。
- feature_selector:默认= cyclic。特征选择和排序方法
- cyclic:通过每次循环一个特征来实现的。
- shuffle:类似于cyclic,但是在每次更新之前都有随机的特征变换。
- random:一个随机(有放回)特征选择器。
- greedy:选择梯度最大的特征。(贪婪选择)
- thrifty:近似贪婪特征选择(近似于greedy)
- top_k:要选择的最重要特征数(在greedy和thrifty内)
-
任务参数(这个参数用来控制理想的优化目标和每一步结果的度量方法。)
- objective:默认=reg:squarederror,表示最小平方误差。
- reg:squarederror,最小平方误差。
- reg:squaredlogerror,对数平方损失。 1 2 [ l o g ( p r e d + 1 ) − l o g ( l a b e l + 1 ) ] 2 \frac{1}{2}[log(pred+1)-log(label+1)]^2 21[log(pred+1)−log(label+1)]2
- reg:logistic,逻辑回归
- reg:pseudohubererror,使用伪Huber损失进行回归,这是绝对损失的两倍可微选择。
- binary:logistic,二元分类的逻辑回归,输出概率。
- binary:logitraw:用于二进制分类的逻辑回归,逻辑转换之前的输出得分。
- binary:hinge:二进制分类的铰链损失。这使预测为0或1,而不是产生概率。(SVM就是铰链损失函数)
- count:poisson –计数数据的泊松回归,泊松分布的输出平均值。
- survival:cox:针对正确的生存时间数据进行Cox回归(负值被视为正确的生存时间)。
- survival:aft:用于检查生存时间数据的加速故障时间模型。
- aft_loss_distribution:survival:aft和aft-nloglik度量标准使用的概率密度函数。
- multi:softmax:设置XGBoost以使用softmax目标进行多类分类,还需要设置num_class(类数)
- multi:softprob:与softmax相同,但输出向量,可以进一步重整为矩阵。结果包含属于每个类别的每个数据点的预测概率。
- rank:pairwise:使用LambdaMART进行成对排名,从而使成对损失最小化。
- rank:ndcg:使用LambdaMART进行列表式排名,使标准化折让累积收益(NDCG)最大化。
- rank:map:使用LambdaMART进行列表平均排名,使平均平均精度(MAP)最大化。
- reg:gamma:使用对数链接进行伽马回归。输出是伽马分布的平均值。
- reg:tweedie:使用对数链接进行Tweedie回归。
- 自定义损失函数和评价指标:https://xgboost.readthedocs.io/en/latest/tutorials/custom_metric_obj.html
- eval_metric:验证数据的评估指标,将根据目标分配默认指标(回归均方根,分类误差,排名的平均平均精度),用户可以添加多个评估指标
- rmse,均方根误差; rmsle:均方根对数误差; mae:平均绝对误差;mphe:平均伪Huber错误;logloss:负对数似然; error:二进制分类错误率;
- merror:多类分类错误率; mlogloss:多类logloss; auc:曲线下面积; aucpr:PR曲线下的面积;ndcg:归一化累计折扣;map:平均精度;
- seed :随机数种子,[默认= 0]。
- objective:默认=reg:squarederror,表示最小平方误差。
-
命令行参数(这里不说了,因为很少用命令行控制台版本)
# from IPython.display import IFrame
# IFrame('https://xgboost.readthedocs.io/en/latest/parameter.html', width=1400, height=800)
# 在jupyter内部导入xgboost文档
XGBoost的调参说明:
参数调优的一般步骤
-
- 确定学习速率和提升参数调优的初始值
-
- max_depth 和 min_child_weight 参数调优
-
- gamma参数调优
-
- subsample 和 colsample_bytree 参数优
-
- 正则化参数alpha调优
-
- 降低学习速率和使用更多的决策树
XGBoost详细攻略:
具体的api请查看:https://xgboost.readthedocs.io/en/latest/python/python_api.html
推荐github:https://github.com/dmlc/xgboost/tree/master/demo/guide-python
安装XGBoost:
方式1:pip3 install xgboost
方式2:pip install xgboost
数据接口(XGBoost可处理的数据格式DMatrix)
from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score # 准确率
# 加载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234565) # 数据集分割
# 算法参数
params = {
'booster': 'gbtree',
'objective': 'multi:softmax',
'num_class': 3,
'gamma': 0.1,
'max_depth': 6,
'lambda': 2,
'subsample': 0.7,
'colsample_bytree': 0.75,
'min_child_weight': 3,
'silent': 0,
'eta': 0.1,
'seed': 1,
'nthread': 4,
}
plst = list(params.items())
dtrain = xgb.DMatrix(X_train, y_train) # 生成数据集格式
num_rounds = 500
model = xgb.train(plst, dtrain, num_rounds) # xgboost模型训练
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)
# 计算准确率
accuracy = accuracy_score(y_test,y_pred)
print("accuarcy: %.2f%%" % (accuracy*100.0))
# 显示重要特征
plot_importance(model)
plt.show()
[00:52:54] WARNING: ../src/learner.cc:573:
Parameters: { "silent" } might not be used.
This may not be accurate due to some parameters are only used in language bindings but
passed down to XGBoost core. Or some parameters are not used but slip through this
verification. Please open an issue if you find above cases.
[00:52:54] WARNING: …/src/learner.cc:1095: Starting in XGBoost 1.3.0, the default evaluation metric used with the objective ‘multi:softmax’ was changed from ‘merror’ to ‘mlogloss’. Explicitly set eval_metric if you’d like to restore the old behavior.
accuarcy: 96.67%
回归案例
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error
# 加载数据集
boston = load_boston()
X,y = boston.data,boston.target
# XGBoost训练过程
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
params = {
'booster': 'gbtree',
'objective': 'reg:squarederror',
'gamma': 0.1,
'max_depth': 5,
'lambda': 3,
'subsample': 0.7,
'colsample_bytree': 0.7,
'min_child_weight': 3,
'silent': 1,
'eta': 0.1,
'seed': 1000,
'nthread': 4,
}
dtrain = xgb.DMatrix(X_train, y_train)
num_rounds = 300
plst = list(params.items())
model = xgb.train(plst, dtrain, num_rounds)
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
ans = model.predict(dtest)
# 显示重要特征
plot_importance(model)
plt.show()
[00:53:14] WARNING: ../src/learner.cc:573:
Parameters: { "silent" } might not be used.
This may not be accurate due to some parameters are only used in language bindings but
passed down to XGBoost core. Or some parameters are not used but slip through this
verification. Please open an issue if you find above cases.
XGBoost调参(结合sklearn网格搜索)
代码参考:https://www.jianshu.com/p/1100e333fcab
8. LightGBM算法
LightGBM也是像XGBoost一样,是一类集成算法,他跟XGBoost总体来说是一样的,算法本质上与Xgboost没有出入,只是在XGBoost的基础上进行了优化,因此就不对原理进行重复介绍,在这里我们来看看几种算法的差别:
- 优化速度和内存使用
- 降低了计算每个分割增益的成本。
- 使用直方图减法进一步提高速度。
- 减少内存使用。
- 减少并行学习的计算成本。
- 稀疏优化
- 用离散的bin替换连续的值。如果#bins较小,则可以使用较小的数据类型(例如uint8_t)来存储训练数据 。
- 无需存储其他信息即可对特征数值进行预排序 。
- 精度优化
- 使用叶子数为导向的决策树建立算法而不是树的深度导向。
- 分类特征的编码方式的优化
- 通信网络的优化
- 并行学习的优化
- GPU支持
LightGBM的优点:
1)更快的训练效率
2)低内存使用
3)更高的准确率
4)支持并行化学习
5)可以处理大规模数据
1.速度对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQ4nKyWt-1619456870318)(./速度对比.png)]
2.准确率对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKatQ9yL-1619456870319)(./准确率对比.png)]
3.内存使用量对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1pQ1nwIA-1619456870320)(./内存使用量.png)]
LightGBM参数说明:
推荐文档1:https://lightgbm.apachecn.org/#/docs/6
推荐文档2:https://lightgbm.readthedocs.io/en/latest/Parameters.html
1.核心参数:(括号内名称是别名)
- objective(objective,app ,application):默认regression,用于设置损失函数
- 回归问题:
- L2损失:regression(regression_l2,l2,mean_squared_error,mse,l2_root,root_mean_squared_error,rmse)
- L1损失:regression_l1(l1, mean_absolute_error, mae)
- 其他损失:huber,fair,poisson,quantile,mape,gamma,tweedie
- 二分类问题:二进制对数损失分类(或逻辑回归):binary
- 多类别分类:
- softmax目标函数: multiclass(softmax)
- One-vs-All 目标函数:multiclassova(multiclass_ova,ova,ovr)
- 交叉熵:
- 用于交叉熵的目标函数(具有可选的线性权重):cross_entropy(xentropy)
- 交叉熵的替代参数化:cross_entropy_lambda(xentlambda)
- 回归问题:
- boosting :默认gbdt,设置提升类型,选项有gbdt,rf,dart,goss,别名:boosting_type,boost
- gbdt(gbrt):传统的梯度提升决策树
- rf(random_forest):随机森林
- dart:多个加性回归树的DROPOUT方法 Dropouts meet Multiple Additive Regression Trees,参见:https://arxiv.org/abs/1505.01866
- goss:基于梯度的单边采样 Gradient-based One-Side Sampling
- data(train,train_data,train_data_file,data_filename):用于训练的数据或数据file
- valid (test,valid_data,valid_data_file,test_data,test_data_file,valid_filenames):验证/测试数据的路径,LightGBM将输出这些数据的指标
- num_iterations:默认=100,类型= INT
- n_estimators:提升迭代次数,LightGBM构造用于多类分类问题的树num_class * num_iterations
- learning_rate(shrinkage_rate,eta) :收缩率,默认=0.1
- num_leaves(num_leaf,max_leaves,max_leaf) :默认=31,一棵树上的最大叶子数
- tree_learner (tree,tree_type,tree_learner_type):默认=serial,可选:serial,feature,data,voting
- serial:单台机器的 tree learner
- feature:特征并行的 tree learner
- data:数据并行的 tree learner
- voting:投票并行的 tree learner
- num_threads(num_thread, nthread):LightGBM 的线程数,为了更快的速度, 将此设置为真正的 CPU 内核数, 而不是线程的数量 (大多数 CPU 使用超线程来使每个 CPU 内核生成 2 个线程),当你的数据集小的时候不要将它设置的过大 (比如, 当数据集有 10,000 行时不要使用 64 线程),对于并行学习, 不应该使用全部的 CPU 内核, 因为这会导致网络性能不佳。
- device(device_type):默认cpu,为树学习选择设备, 你可以使用 GPU 来获得更快的学习速度,可选cpu, gpu。
- seed (random_seed,random_state):与其他种子相比,该种子具有较低的优先级,这意味着如果您明确设置其他种子,它将被覆盖。
2.用于控制模型学习过程的参数:
- max_depth:限制树模型的最大深度. 这可以在 #data 小的情况下防止过拟合. 树仍然可以通过 leaf-wise 生长。
- min_data_in_leaf: 默认=20,一个叶子上数据的最小数量. 可以用来处理过拟合。
- min_sum_hessian_in_leaf(min_sum_hessian_per_leaf, min_sum_hessian, min_hessian):默认=1e-3,一个叶子上的最小 hessian 和. 类似于 min_data_in_leaf, 可以用来处理过拟合.
- feature_fraction:default=1.0,如果 feature_fraction 小于 1.0, LightGBM 将会在每次迭代中随机选择部分特征. 例如, 如果设置为 0.8, 将会在每棵树训练之前选择 80% 的特征,可以用来加速训练,可以用来处理过拟合。
- feature_fraction_seed:默认=2,feature_fraction 的随机数种子。
- bagging_fraction(sub_row, subsample):默认=1,不进行重采样的情况下随机选择部分数据
- bagging_freq(subsample_freq):bagging 的频率, 0 意味着禁用 bagging. k 意味着每 k 次迭代执行bagging
- bagging_seed(bagging_fraction_seed) :默认=3,bagging 随机数种子。
- early_stopping_round(early_stopping_rounds, early_stopping):默认=0,如果一个验证集的度量在 early_stopping_round 循环中没有提升, 将停止训练
- lambda_l1(reg_alpha):L1正则化系数
- lambda_l2(reg_lambda):L2正则化系数
- min_split_gain(min_gain_to_split):执行切分的最小增益,默认=0.
- cat_smooth:默认=10,用于分类特征,可以降低噪声在分类特征中的影响, 尤其是对数据很少的类别
3.度量参数:
- metric:default={l2 for regression}, {binary_logloss for binary classification}, {ndcg for lambdarank}, type=multi-enum, options=l1, l2, ndcg, auc, binary_logloss, binary_error …
- l1, absolute loss, alias=mean_absolute_error, mae
- l2, square loss, alias=mean_squared_error, mse
- l2_root, root square loss, alias=root_mean_squared_error, rmse
- quantile, Quantile regression
- huber, Huber loss
- fair, Fair loss
- poisson, Poisson regression
- ndcg, NDCG
- map, MAP
- auc, AUC
- binary_logloss, log loss
- binary_error, 样本: 0 的正确分类, 1 错误分类
- multi_logloss, mulit-class 损失日志分类
- multi_error, error rate for mulit-class 出错率分类
- xentropy, cross-entropy (与可选的线性权重), alias=cross_entropy
- xentlambda, “intensity-weighted” 交叉熵, alias=cross_entropy_lambda
- kldiv, Kullback-Leibler divergence, alias=kullback_leibler
- 支持多指标, 使用 , 分隔
- train_metric(training_metric, is_training_metric):默认=False,如果你需要输出训练的度量结果则设置 true
4.GPU 参数:
- gpu_device_id:default为-1, 这个default意味着选定平台上的设备。
LightGBM与网格搜索结合调参:
参考代码:https://blog.csdn.net/u012735708/article/details/83749703
import lightgbm as lgb
from sklearn import metrics
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
canceData=load_breast_cancer()
X=canceData.data
y=canceData.target
X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=0,test_size=0.2)
### 数据转换
print('数据转换')
lgb_train = lgb.Dataset(X_train, y_train, free_raw_data=False)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train,free_raw_data=False)
### 设置初始参数--不含交叉验证参数
print('设置参数')
params = {
'boosting_type': 'gbdt',
'objective': 'binary',
'metric': 'auc',
'nthread':4,
'learning_rate':0.1
}
### 交叉验证(调参)
print('交叉验证')
max_auc = float('0')
best_params = {}
# 准确率
print("调参1:提高准确率")
for num_leaves in range(5,100,5):
for max_depth in range(3,8,1):
params['num_leaves'] = num_leaves
params['max_depth'] = max_depth
cv_results = lgb.cv(
params,
lgb_train,
seed=1,
nfold=5,
metrics=['auc'],
early_stopping_rounds=10,
verbose_eval=True
)
mean_auc = pd.Series(cv_results['auc-mean']).max()
boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
if mean_auc >= max_auc:
max_auc = mean_auc
best_params['num_leaves'] = num_leaves
best_params['max_depth'] = max_depth
if 'num_leaves' and 'max_depth' in best_params.keys():
params['num_leaves'] = best_params['num_leaves']
params['max_depth'] = best_params['max_depth']
# 过拟合
print("调参2:降低过拟合")
for max_bin in range(5,256,10):
for min_data_in_leaf in range(1,102,10):
params['max_bin'] = max_bin
params['min_data_in_leaf'] = min_data_in_leaf
cv_results = lgb.cv(
params,
lgb_train,
seed=1,
nfold=5,
metrics=['auc'],
early_stopping_rounds=10,
verbose_eval=True
)
mean_auc = pd.Series(cv_results['auc-mean']).max()
boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
if mean_auc >= max_auc:
max_auc = mean_auc
best_params['max_bin']= max_bin
best_params['min_data_in_leaf'] = min_data_in_leaf
if 'max_bin' and 'min_data_in_leaf' in best_params.keys():
params['min_data_in_leaf'] = best_params['min_data_in_leaf']
params['max_bin'] = best_params['max_bin']
print("调参3:降低过拟合")
for feature_fraction in [0.6,0.7,0.8,0.9,1.0]:
for bagging_fraction in [0.6,0.7,0.8,0.9,1.0]:
for bagging_freq in range(0,50,5):
params['feature_fraction'] = feature_fraction
params['bagging_fraction'] = bagging_fraction
params['bagging_freq'] = bagging_freq
cv_results = lgb.cv(
params,
lgb_train,
seed=1,
nfold=5,
metrics=['auc'],
early_stopping_rounds=10,
verbose_eval=True
)
mean_auc = pd.Series(cv_results['auc-mean']).max()
boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
if mean_auc >= max_auc:
max_auc=mean_auc
best_params['feature_fraction'] = feature_fraction
best_params['bagging_fraction'] = bagging_fraction
best_params['bagging_freq'] = bagging_freq
if 'feature_fraction' and 'bagging_fraction' and 'bagging_freq' in best_params.keys():
params['feature_fraction'] = best_params['feature_fraction']
params['bagging_fraction'] = best_params['bagging_fraction']
params['bagging_freq'] = best_params['bagging_freq']
print("调参4:降低过拟合")
for lambda_l1 in [1e-5,1e-3,1e-1,0.0,0.1,0.3,0.5,0.7,0.9,1.0]:
for lambda_l2 in [1e-5,1e-3,1e-1,0.0,0.1,0.4,0.6,0.7,0.9,1.0]:
params['lambda_l1'] = lambda_l1
params['lambda_l2'] = lambda_l2
cv_results = lgb.cv(
params,
lgb_train,
seed=1,
nfold=5,
metrics=['auc'],
early_stopping_rounds=10,
verbose_eval=True
)
mean_auc = pd.Series(cv_results['auc-mean']).max()
boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
if mean_auc >= max_auc:
max_auc=mean_auc
best_params['lambda_l1'] = lambda_l1
best_params['lambda_l2'] = lambda_l2
if 'lambda_l1' and 'lambda_l2' in best_params.keys():
params['lambda_l1'] = best_params['lambda_l1']
params['lambda_l2'] = best_params['lambda_l2']
print("调参5:降低过拟合2")
for min_split_gain in [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]:
params['min_split_gain'] = min_split_gain
cv_results = lgb.cv(
params,
lgb_train,
seed=1,
nfold=5,
metrics=['auc'],
early_stopping_rounds=10,
verbose_eval=True
)
mean_auc = pd.Series(cv_results['auc-mean']).max()
boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
if mean_auc >= max_auc:
max_auc=mean_auc
best_params['min_split_gain'] = min_split_gain
if 'min_split_gain' in best_params.keys():
params['min_split_gain'] = best_params['min_split_gain']
print(best_params)
{‘num_leaves’: 5, ‘max_depth’: 7, ‘max_bin’: 105, ‘min_data_in_leaf’: 51, ‘feature_fraction’: 1.0, ‘bagging_fraction’: 1.0, ‘bagging_freq’: 45, ‘lambda_l1’: 0.001, ‘lambda_l2’: 0.0, ‘min_split_gain’: 0.0}
本章作业:
本章在最后介绍LightGBM的时候并没有详细介绍它的原理以及它与XGBoost的不一样的地方,希望读者好好看看别的文章分享,总结LigntGBM与XGBoost的不同,然后使用一个具体的案例体现两者的不同。
(1)xgboost采用的是level-wise的分裂策略,而lightGBM采用了leaf-wise的策略,区别是xgboost对每一层所有节点做无差别分裂,可能有些节点的增益非常小,对结果影响不大,但是xgboost也进行了分裂,带来了务必要的开销。 leaft-wise的做法是在当前所有叶子节点中选择分裂收益最大的节点进行分裂,如此递归进行,很明显leaf-wise这种做法容易过拟合,因为容易陷入比较高的深度中,因此需要对最大深度做限制,从而避免过拟合。
(2)lightgbm使用了基于histogram的决策树算法,这一点不同与xgboost中的 exact 算法,histogram算法在内存和计算代价上都有不小优势。1)内存上优势:很明显,直方图算法的内存消耗为(#data* #features * 1Bytes)(因为对特征分桶后只需保存特征离散化之后的值),而xgboost的exact算法内存消耗为:(2 * #data * #features* 4Bytes),因为xgboost既要保存原始feature的值,也要保存这个值的顺序索引,这些值需要32位的浮点数来保存。2)计算上的优势,预排序算法在选择好分裂特征计算分裂收益时需要遍历所有样本的特征值,时间为(#data),而直方图算法只需要遍历桶就行了,时间为(#bin)
(3)直方图做差加速,一个子节点的直方图可以通过父节点的直方图减去兄弟节点的直方图得到,从而加速计算。
(4)lightgbm支持直接输入categorical 的feature,在对离散特征分裂时,每个取值都当作一个桶,分裂时的增益算的是”是否属于某个category“的gain。类似于one-hot编码。
(5)xgboost在每一层都动态构建直方图,因为xgboost的直方图算法不是针对某个特定的feature,而是所有feature共享一个直方图(每个样本的权重是二阶导),所以每一层都要重新构建直方图,而lightgbm中对每个特征都有一个直方图,所以构建一次直方图就够了。
其适用场景根据实际项目和两种算法的优点进行选择。