GBDT
一、概念
G B D T ( G r a d i e n t B o o s t i n g D e c i s i o n T r e e ) GBDT(Gradient \; Boosting \; Decision \;Tree) GBDT(GradientBoostingDecisionTree) 梯度提升决策树,是 G r a d i e n t B o o s t i n g Gradient \; Boosting GradientBoosting 框架下使用的较多的一个模型,且在 G B D T GBDT GBDT 中,其基学习器是分类回归树,也就是 C A R T CART CART(详见 3.决策树原理与数学建模实战),且使用的是 C A R T CART CART 树中的回归树。
决策树分为两大类:回归树与分类树。前者用于预测实数值,后者用于分类标签值,其区别是,前者的结果加减是有意义的,如, 10 元 + 3 元 = 13 元 10元+3元=13元 10元+3元=13元,后者的加减是无意义的,如: 男 + 女 = ? 男+女=? 男+女=?
G B D T GBDT GBDT 的核心在于累加所有树的结果作为最终结果。而分类树的结果显然是没有办法累加的,所以 G B D T GBDT GBDT 中的树都是回归树,不是分类树(尽管 G B D T GBDT GBDT 也可以用作分类但不代表所使用的树是分类树)。
二、算法原理
2.1 原理
G r a d i e n t B o o s t i n g Gradient \; Boosting GradientBoosting 集成了 B o o s t i n g Boosting Boosting 的思想,利用加法模型与前向分布算法实现学习的优化过程。每个基学习器在上一轮学习器的基础上进行训练。对弱学习器 (弱分类器)的要求一般足够简单,并且是低方差和高偏差的(欠拟合)。因为训练的过程就是通过降低偏差不断提高最终学习器的精度。
它根据当前模型损失函数的负梯度信息来训练新加入的弱分类器,然后将训练好的弱分类器以累加的形式结合到现有的模型中。其算法伪码如下:
2.2 算法流程
A l g o r i t h m : Algorithm: Algorithm:
- F 0 ( x ) = arg min p ∑ i = 1 N L ( y i , ρ ) F_0(x) = \arg \min_p \sum\limits^N_{i=1}L(y_i, \rho) F0(x)=argminpi=1∑NL(yi,ρ)
- F o r m = 1 t o M d o : For \; m = 1 \; to \; M \; do: Form=1toMdo:
- y ~ i = [ ∂ L ( y i ) , F ( x i ) ∂ F ( x i ) ] F ( x ) = F m − 1 ( X ) i = 1 , … , M \quad \tilde y_i = [\frac{\partial L(y_i), F(x_i)}{\partial F(x_i)}]_{F(x) = F_{m-1(X)}} \quad i = 1, \dots, M y~i=[∂F(xi)∂L(yi),F(xi)]F(x)=Fm−1(X)i=1,…,M
- a m = arg min α , β ∑ i = 1 N [ y ~ i = β h ( x i : a ) ] 2 \quad a_m = \arg \min_{\alpha, \beta}\sum\limits^{N}_{i=1}[\tilde y_i = \beta h(x_i :a)]^2 am=argminα,βi=1∑N[y~i=βh(xi:a)]2
- ρ m = arg min ρ ∑ i = 1 N L ( y i , F m − 1 ( x i ) + ρ h ( x i : a m ) ) \quad \rho _m = \arg \min_\rho \sum\limits^{N}_{i=1}L(y_i, F_{m-1}(x_i)+ \rho h(x_i : a_m)) ρm=argminρi=1∑NL(yi,Fm−1(xi)+ρh(xi:am))
- F m ( x ) = F m − 1 ( x ) + ρ m h ( x : a m ) \quad F_m(x) = F_{m-1}(x) + \rho_m h(x:a_m) Fm(x)=Fm−1(x)+ρmh(x:am)
- e n d F o r end \; For endFor
- e n d A l g o r i t h m end \; Algorithm endAlgorithm
G B D T GBDT GBDT的核心就在于,每一棵树学习的是之前所有树结论和的残差,这个残差就是一个加预测值后能得真实值的累加量。
比如 A A A 的真实年龄是 18 18 18 岁,但第一棵树的预测年龄是12岁,差了6岁,即残差为6岁。那么在第二棵树里我们把 A A A 的年龄设为6岁去学习,如果第二棵树真的能把 A A A 分到6岁的叶子节点,那累加两棵树的结论就是 A A A 的真实年龄;如果第二棵树的结论是5岁,则 A A A 仍然存在 1 岁的残差,第三棵树里 A A A 的年龄就变成1岁。如果我们的迭代轮数还没有完,可以继续迭代下面每一轮迭代,拟合的岁数误差都会减小。
2.3 目标函数
模型一共训练
M
M
M 轮,每轮产生一个弱学习器
T
(
x
;
θ
m
)
T(x;θ_m)
T(x;θm)。弱学习器的目标函数可以表示为:
θ
m
^
=
∑
i
=
1
arg
min
N
L
(
y
i
,
F
m
−
1
(
x
i
)
+
T
(
x
;
θ
m
)
)
\hat{\theta_m} = \sum\limits^{\arg \min N}_{i=1}L(y_i, F_{m-1}(x_i) + T(x; \theta_m))
θm^=i=1∑argminNL(yi,Fm−1(xi)+T(x;θm))
F
m
−
1
(
x
i
)
F_{m−1}(x_i)
Fm−1(xi) 为当前的模型,
G
B
D
T
GBDT
GBDT 通过经验风险极小化来确定下一个弱学习器的参数。具体使用到的损失函数的选择也就是这里的
L
L
L,主要有平方损失函数,
0
−
1
0-1
0−1 损失函数,对数损失函数等等。如果我们选择平方损失函数,那么这个差值其实就是我们平常所说的残差。
目标:第一个是希望我们的损失函数能够不断减小;第二个是希望我们的损失函数能够尽可能快地减小。
让损失函数沿着负梯度方向的下降,是 G B D T GBDT GBDT 的 G B GB GB 另外一个核心。利用损失函数的负梯度在当前模型的值,作为回归提升树算法中的残差的近似值去拟合一个回归树。 G B D T GBDT GBDT 每轮迭代的时候,都去拟合损失函数在当前模型下的负梯度。
这样每轮训练的时候都能够让损失函数尽可能快的减小,尽快地收敛达到局部最优或者全局最优。
2.4 梯度提升于梯度下降
两者对比可用下表表示:
梯度提升 | 函数空间 F F F | F = F t − 1 − ρ t ∇ F L ∣ F = F t − 1 F = F_{t-1} - \rho_t\nabla_FL|_{F=F_{t-1}} F=Ft−1−ρt∇FL∣F=Ft−1 | L = ∑ L ( y i , F ( x i ) ) L=\sum\limits L(y_i, F(x_i)) L=∑L(yi,F(xi)) |
---|---|---|---|
梯度下降 | 参数空间 W W W | w t = w t − 1 − ρ t ∇ w L ∣ w = w t − 1 w_t = w_{t-1} - \rho_t \nabla_wL|_{w=w_{t-1}} wt=wt−1−ρt∇wL∣w=wt−1 | L = ∑ L ( y i , f w ( w i ) ) L = \sum\limits L(y_i,f_w(w_i)) L=∑L(yi,fw(wi)) |
可以发现,两者都是在每一轮迭代中,利用损失函数相对于模型的负梯度方向的信息来对当前模型进行更新,只不过在梯度下降中,模型是以参数化形式表示,从而模型的更新等价于参数的更新。而在梯度提升中,模型并不需要进行参数化表示,而是直接定义在函数空间中,从而大大扩展了可以使用的模型种类。
三、Python实现
G
B
D
T
GBDT
GBDT 封装在 sklearn.ensemble
中,其分类器 GradientBoostingClassifier
与回归器 GridientBoostingRegressor
接口皆在此包内。
from sklearn.datasets import load_boston
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
X, y = load_boston(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, random_state=42)
gbdt = GradientBoostingRegressor()
gbdt.fit(X_train, y_train)
plt.plot(y_test, 'r', label='True')
plt.plot(gbdt.predict(X_test), 'b', label='Predict')
plt.legend()
plt.savefig('E:/Computer/Desktop/数模/有监督学习/集成学习/gbdt.png', dpi=400)
拟合效果如下:
四、XGBoost
4.1 概念
X G B o o s t XGBoost XGBoost 本质上还是 G B D T GBDT GBDT,但是把速度和效率做到了极致;
不同于传统的 G B D T GBDT GBDT 方式,只利用了一阶的导数信息, X G B o o s t XGBoost XGBoost 对损失函数做了二阶的求导 (泰勒展开),并在目标函数之外加入了正则项整体求最优解,用以权衡目标函数的下降和模型的复杂程度,避免过拟合。
X G B o o s t XGBoost XGBoost 继承了 G B D T GBDT GBDT ,模型也是 C A R T CART CART , 但不仅限于回归树。因此还是沿用了加法模型,但是在目标函数上有所区别。
4.2 区别与联系
原始的
G
B
D
T
GBDT
GBDT 算法基于经验损失函数的负梯度来构造新的决策树,只是在决策树构建完成后再进行剪枝。而
X
G
B
o
o
s
t
XGBoost
XGBoost 在决策树构建阶段就加入了正则项,即:
L
t
=
∑
i
l
(
y
i
,
F
t
−
1
(
x
i
)
)
+
Ω
f
(
x
t
)
L_t = \sum\limits_il(y_i, F_{t-1}(x_i)) + \Omega f(x_t)
Lt=i∑l(yi,Ft−1(xi))+Ωf(xt)
其中
F
t
−
1
(
x
i
)
F_{t-1}(x_i)
Ft−1(xi) 表示现有的
t
−
1
t-1
t−1 棵树的最优解,关于树结构的正则化项定义为:
Ω
(
f
t
)
=
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
\Omega (f_t) = \gamma T + \frac 12 \lambda\sum\limits^T_{j=1}w^2_j
Ω(ft)=γT+21λj=1∑Twj2
其中
T
T
T 为叶子节点个数,
w
j
w_j
wj 表示第
j
j
j 个叶子节点的预测值。对该损失函数在
F
t
−
1
F_{t-1}
Ft−1 处进行二阶泰勒展开可以推导出:
L
t
≈
L
t
~
=
∑
j
=
1
T
[
G
j
w
j
+
1
2
(
H
j
+
λ
)
w
j
2
]
+
γ
T
L_t \approx \tilde{L_t} = \sum\limits^T_{j=1}[G_jw_j + \frac 12 (H_j + \lambda)w_j^2] + \gamma T
Lt≈Lt~=j=1∑T[Gjwj+21(Hj+λ)wj2]+γT
其中
T
T
T 为决策树
f
t
f_t
ft 中叶子节点的个数,
G
j
=
∑
i
∈
I
j
∇
F
t
−
1
I
(
y
i
,
F
t
−
1
(
x
i
)
)
G_j = \sum\limits_{i \in I_j}\nabla_{F_{t-1}}I(y_i, F_{t-1}(x_i))
Gj=i∈Ij∑∇Ft−1I(yi,Ft−1(xi)),
H
j
=
∑
j
∈
I
j
∇
F
t
−
1
2
l
(
y
i
,
F
t
−
1
(
x
i
)
)
H_j = \sum\limits_{j \in I_j}\nabla^2_{F_{t-1}}l(y_i, F_{t-1}(x_i))
Hj=j∈Ij∑∇Ft−12l(yi,Ft−1(xi)),
I
j
I_j
Ij 表示所有属于叶子节点
j
j
j 的样本的索引的结合。
假设决策树的结构已知,通过令损失函数相当于
w
j
w_j
wj 的导数为 0 可以求出在最小化损失函数的情况下各个叶子节点上的预测值
w
j
∗
=
−
G
j
H
j
+
λ
w_j^* = - \frac{G_j}{H_j + \lambda}
wj∗=−Hj+λGj
然而从所有的树结构中寻找最优的树结构是一个
N
P
−
h
a
r
d
NP-hard
NP−hard 问题,因此在实际中往往采用贪心法来构建出一个次优的树结构,基本思想是从根节点开始,每次对一个叶子节点进行分裂,针对每一种可能的分裂,根据特定的准则选取最优的分裂。不同的决策树算法采用不同的准则,如
I
D
3
ID3
ID3 采用信息增益,
C
4.5
C4.5
C4.5 为了克服信息增益中容易偏向取值较多的特征而采用信息增益比,
C
A
R
T
CART
CART 算法使用基尼指数和平方误差,
X
G
B
o
o
s
t
XGBoost
XGBoost 也有特定的准则来选取最优分裂。
通过将预测值代入到损失函数中可求得损失函数的最小值
L
t
∗
~
=
−
1
2
∑
j
=
1
T
G
j
2
H
j
+
λ
+
γ
T
\tilde {L^*_t} = - \frac 12 \sum\limits^T_{j=1}\frac{G^2_j}{H_j+\lambda} + \gamma T
Lt∗~=−21j=1∑THj+λGj2+γT
容易计算出分裂前后损失函数的差值为:
G
a
i
n
=
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
−
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
−
γ
Gain = \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} - \gamma
Gain=HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2−γ
X
G
B
o
o
s
t
XGBoost
XGBoost 采用最大化这个差值作为准则来进行决策树的构建,通过遍历所有特征的所有取值,寻找使得损失函数前后相差最大时对应的分裂方式,此外,由于损失函数前后存在差值一定为正的限制,此时
γ
\gamma
γ 起到了一定的预剪枝效果。
除了算法上与传统的 G B D T GBDT GBDT 有一些不同外, X G B o o s t XGBoost XGBoost 还在工程实现上做了大量优化,总的来说,二者联系可以分为以下几个部分:
- G B D T GBDT GBDT 是机器学习算法, X G b o o s t XGboost XGboost 是该算法的工程实现;
- 在使用 C A R T CART CART 作为基分类器时, X G B o o s t XGBoost XGBoost 显式的加入了正则项来控制模型的复杂度,有利于防止过拟合,从而提高模型的泛化能力;
- G B D T GBDT GBDT 在模型训练时只使用了代价函数的一阶导数信息, X G B o o s t XGBoost XGBoost 对代价函数进行二阶泰勒展开,可以同时使用一阶和二阶导数;
- 传统 G B D T GBDT GBDT 采用 C A R T CART CART 作为基分类器, X G B o o s t XGBoost XGBoost 支持多种类型的基分类器,如线性分类器;
- 传统的 G B D T GBDT GBDT 在每轮迭代时使用全部的数据, X G B o o s t XGBoost XGBoost 则采用了与随机森林相似的策略,支持对数据进行采样;
- 传统 G B D T GBDT GBDT 没有设计对缺失值进行处理, X G B o o s t XGBoost XGBoost 能够自动学习出缺失值的处理策略。
四、小结
G B D T GBDT GBDT:
优点:
- 可以灵活处理各种类型的数据,包括连续值和离散值;
- 在相对较少的调参时间情况下,预测的准确率也可以比较高,这个是相较于 S V M SVM SVM 的;
- 使用一些健壮的损失函数,对异常值的鲁棒性非常强,比如 H u b e r Huber Huber 损失函数和 Q u a n t i l e Quantile Quantile 损失函数;
- 很好的利用弱分类器进行级联;
- 充分考虑的每个分类器的权重;
缺点:
对异常样本敏感,异常样本在迭代中可能获得较高的权重,影响最终的强学习器的预测准确性。
X G B o o s t : XGBoost: XGBoost:
优点:
- 对缺失值不敏感,可以给缺失值自动划分方向;
- 可以引入阈值限制树分裂,控制树的规模,防止过拟合问题;
- 分裂依据分开后与未分前的差值增益,不用每个节点排序算增益,减少计算量,可以并行计算
缺点:
- 当预测器有很多类别时,容易过拟合;
- 参数过多,调参困难;
- 如果采取精确搜索算法对内存消耗过大,且不支持分布式。
值不敏感,可以给缺失值自动划分方向;
2. 可以引入阈值限制树分裂,控制树的规模,防止过拟合问题;
3. 分裂依据分开后与未分前的差值增益,不用每个节点排序算增益,减少计算量,可以并行计算
缺点:
- 当预测器有很多类别时,容易过拟合;
- 参数过多,调参困难;
- 如果采取精确搜索算法对内存消耗过大,且不支持分布式。