(八)GBDT
本系列重点在浅显易懂,快速上手。不进行过多的理论讲解:也就是不去深究what,而是关注how。全文围绕以下三个问题展开:
1)长什么样?
2)解决什么问题?
3)怎么实现?
3.1)从数学讲,原理
3.2)从代码上讲,如何掉包实现
1 长什么样
GBDT=Gradient Boosting+CART树
gradient boosting decision tree 梯度提升决策树,有多个若学习器组成,弱学习器的通常是层数较少的CART回归树,单个弱学习器,因层数叫浅,所以偏差较大,方差小,最终的GBDT的输出是每个若学习器的输出的加和。如下图所示,为什么是加和?看下文的解释。
备注:所有GBDT算法中,底层都是回归树。
GBDT也是Boosting算法的一种,但是和AdaBoost算法不同;区别如下:
AdaBoost算法是利用前一轮的弱学习器的误差来更新样本权重值,然后一轮一轮的迭代;GBDT也是迭代,但是GBDT要求弱学习器必须是回归CART模型,而且GBDT在模型训练的时候,是要求模型预测的样本损失尽可能的小。
要迭代所少次,(就是需要多少棵树),需要调参,分类看准确率召回率系列的指标。回归看MSE系列的指标。
给定一个步长step,在构建下一棵树的时候使用step*残差值作为输入值,这种方式可以减少过拟合的发生。
2 解决什么问题
可以解决分类或回归问题。
3 怎么实现
3.1 数学原理(回归算法)
(1)初始化第一个若学习器,得到常熟函数 f 0 ( x ) f_0(x) f0(x)
f 0 ( x ) = a r g m i n c ∑ i = 1 N L ( y i , c ) f_0(x) = arg min_c\sum^{N}_{i=1}L(y_i,c) f0(x)=argminci=1∑NL(yi,c)
损失函数L是误差平方和损失函数。凸函数,要求极值,可以求导,求解导函数为0的点。过程如下。
∑ i = 1 N ∂ L ( y i , c ) ) ∂ c = ∑ i = 1 N ∂ ( 1 2 ( y i − c ) 2 ) ∂ c = ∑ i = 1 N c − y i = 0 \begin{align*}\sum_{i=1}^N\frac{\partial L(y_i,c))}{\partial c} &=\sum_{i=1}^N\frac{\partial(\frac12(y_i-c)^2)}{\partial c}\\ & =\sum_{i=1}^Nc-y_i=0\\ \end{align*} i=1∑N∂c∂L(yi,c))=i=1∑N∂c∂(21(yi−c)2)=i=1∑Nc−yi=0
不难得到:
c = ( ∑ i = 1 N y i ) / N c=(\sum^{N}_{i=1}y_i)/N c=(i=1∑Nyi)/N
(2)构建新的样本集,GBDT算法是通过改变y的值,来构建新的样本集,具体的是将标签y,替换为残差 γ γ γ。
r i , m = − [ ∂ L ( y i , f ( x i ) ) ) ∂ f ( x i ) ] f ( x ) = f m − 1 ( x ) r_{i,m}=-\left[\frac{\partial L(y_i,f(x_i)))}{\partial f(x_i)}\right]_{f(x)=f_{m-1}(x)} ri,m=−[∂f(xi)∂L(yi,f(xi)))]f(x)=fm−1(x)
当回归问题是,采用误差平方和损失函数(MSE),此时
r i , m = − ∂ ( 1 / 2 ( y i − f m − 1 ( x ) ) 2 ) ∂ f m − 1 ( x ) = y i − f m − 1 ( x ) r_{i,m}=-\frac{\partial(1/2(y_i-f_{m-1}(x))^2)}{\partial f_{m-1}(x)}\\ =y_i-f_{m-1}(x) ri,m=−∂fm−1(x)∂(1/2(yi−fm−1(x))2)=yi−fm−1(x)
(3)用上一步,得到的新数据集构建第一个弱学习器,CART回归树,(CART回归树的实现原理,见本系列第5篇博客,(五)决策树)。最终得到的叶子节点如下:
Υ m , j = a r g m i n Υ ∑ x i ∈ R m , j L ( y i , f m − 1 ( x i ) + Υ ) = a r g m i n Υ ∑ x i ∈ R m , j L ( Υ i , m − 1 , Υ ) 凸函数,求极值,通过求导,让导函数为 0 的方式实现: ∑ i = 1 N ∂ L ( Υ i , m − 1 , Υ ) ∂ Υ = ∑ i = 1 N ∂ ( 1 2 ( Υ i , m − 1 − Υ ) 2 ) ∂ Υ = ∑ i = 1 N Υ − Υ i , m − 1 = 0 Υ = ( ∑ i = 1 N Υ i , m − 1 ) / N 所以,第一个若学习的输出为 Υ 1 , j I ( x i ∈ R 1 , j ) \begin{align*} Υ_{m,j}&=arg \ min_Υ\sum_{x_i∈R_{m,j}}L(y_i,f_{m-1}(x_i)+Υ)\\ &=arg \ min_Υ\sum_{x_i∈R_{m,j}}L(Υ_{i,m-1},Υ)\\\\ 凸函数,求极值,通过求导,让导函数为0的方式实现:\\ \\ \sum_{i=1}^N\frac{\partial L(Υ_{i,m-1},Υ)}{\partial Υ} &=\sum_{i=1}^N\frac{\partial(\frac12(Υ_{i,m-1}-Υ)^2)}{\partial Υ}\\ & =\sum_{i=1}^N Υ-Υ_{i,m-1}=0\\ Υ&=(\sum^{N}_{i=1}Υ_{i,m-1})/N\\ 所以,第一个若学习的输出为Υ_{1,j}I(x_i∈R_{1,j}) \end{align*}\\ Υm,j凸函数,求极值,通过求导,让导函数为0的方式实现:i=1∑N∂Υ∂L(Υi,m−1,Υ)Υ所以,第一个若学习的输出为Υ1,jI(xi∈R1,j)=arg minΥxi∈Rm,j∑L(yi,fm−1(xi)+Υ)=arg minΥxi∈Rm,j∑L(Υi,m−1,Υ)=i=1∑N∂Υ∂(21(Υi,m−1−Υ)2)=i=1∑NΥ−Υi,m−1=0=(i=1∑NΥi,m−1)/N
(4) 更新强学习器,
f 1 ( x ) = f 0 ( x ) + l r ∗ ∑ Υ 1 , j I ( x i ∈ R 1 , j ) f_1(x)=f_0(x)+lr*\sum Υ_{1,j}I(x_i∈R_{1,j}) f1(x)=f0(x)+lr∗∑Υ1,jI(xi∈R1,j)
(5)再次构建新的数据集,重复(2)(3)(4)的过程,得到最总的强学习器。
f ( x ) = f M ( x ) = f 0 ( x ) + l r ∗ ∑ m = 1 M ∑ j = 1 J Υ m , j I ( x i ∈ R m , j ) ,其中 l r 是学习率 f(x)=f_M(x)=f_0(x)+lr*\sum_{m=1}^M\sum _{j=1}^JΥ_{m,j}I(x_i∈R_{m,j}),其中lr是学习率 f(x)=fM(x)=f0(x)+lr∗m=1∑Mj=1∑JΥm,jI(xi∈Rm,j),其中lr是学习率
3.2数学原理(分类算法)
回归问题中,构建新数据集用的是负梯度值,就是用真实值减去预测值,但是在分类问题中,真实值和预测值都是类别,类别之间的相减是没有意义的。一种解决方案是,采用逻辑回归算法中的对数损失函数,用结果的预测概率值和真实概率值的差值作为残差。
逻辑回归的对数损失函数为:
ℓ ( θ ) = log L ( θ ) = ∑ i = 1 m ( y ( i ) log h θ ( x ( i ) ) + ( 1 − y ( i ) ) log ( 1 − h θ ( x ( i ) ) ) ) \ell(\theta)=\log L(\theta)=\sum_{i=1}^{m}\left(y^{(i)}\log h_{\theta}\left(x^{(i)}\right)+\left(1-y^{(i)}\right)\log\left(1-h_{\theta}\left(x^{(i)}\right)\right)\right) ℓ(θ)=logL(θ)=i=1∑m(y(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i))))
(1)初始化第一个弱学习器
对上式求导,领导数为0
∂
L
(
y
i
,
F
(
x
i
)
)
∂
F
(
x
i
)
=
y
i
−
1
1
+
e
−
F
(
x
i
)
∑
(
y
i
−
1
1
+
e
−
F
0
(
x
)
)
=
0
F
0
(
x
)
=
l
o
g
p
(
y
=
1
)
1
−
p
(
y
=
1
)
\begin{align*} &\frac{\partial L(y_i,F(x_i))}{\partial F(x_i)}=y_i-\frac1{1+e^{-F(x_i)}}\\\\ &\sum(y_i-\frac1{1+e^{-F_0(x)}})=0\\\\ &F_0(x)= log\frac{p(y=1)}{1-p(y=1)}\\ \end{align*}
∂F(xi)∂L(yi,F(xi))=yi−1+e−F(xi)1∑(yi−1+e−F0(x)1)=0F0(x)=log1−p(y=1)p(y=1)
(2)计算负梯度,伪残差
r
m
,
i
=
−
∣
∂
L
(
y
i
,
F
(
x
i
)
)
∂
F
(
x
i
)
∣
F
(
x
)
=
F
m
−
1
(
x
)
=
y
i
−
1
1
+
e
−
F
(
x
i
)
\begin{align*} r_{m,i}&=-\bigg|\frac{\partial L(y_{i},F(x_{i}))}{\partial F(x_{i})}\bigg|_{F(x)=F_{m-1}(x)}\\ &=y_{i}-\frac{1}{1+e^{-F(x_{i})}}\\ \end{align*}
rm,i=−
∂F(xi)∂L(yi,F(xi))
F(x)=Fm−1(x)=yi−1+e−F(xi)1
(3)利用上面求解得到的残差值,形成新的训练集
(
x
i
,
r
m
,
i
)
(x_i,r_{m,i})
(xi,rm,i),训练CART回归树。
(4)训练的cart回归树对应的叶子节点区域为 R m , j R_{m,j} Rm,j,各个叶子节点的拟合值为:
c m , j = arg min ∑ x i ∈ R m , j L ( y i , F m − 1 ( x i ) + c ) 上式没有闭式解,一般使用近似值代替 c m , j = ∑ x i ∈ R m , j r m , j ∑ x i ∈ R m , j r m , j ( y i − r m , j ) ( 1 − y i + r m , j ) \begin{align*} c_{m,j}&=\arg\min\sum_{x_{i}\in R_{m,j}}L(y_{i},F_{m-1}(x_{i})+c)\\ &上式没有闭式解,一般使用近似值代替\\ c_{m,j}&=\frac{\sum_{x_i∈R_{m,j}}r_{m,j}}{\sum_{x_i∈R_{m,j}}r_{m,j}(y_i-r_{m,j})(1-y_i+r_{m,j})}\\ \end{align*} cm,jcm,j=argminxi∈Rm,j∑L(yi,Fm−1(xi)+c)上式没有闭式解,一般使用近似值代替=∑xi∈Rm,jrm,j(yi−rm,j)(1−yi+rm,j)∑xi∈Rm,jrm,j
(5)更新强学习器
F
m
(
x
)
=
F
m
−
1
(
x
)
+
l
r
∗
∑
c
m
,
j
I
(
x
i
∈
R
m
,
j
)
F_m(x)=F_{m-1}(x)+lr*\sum c_{m,j}I(x_i∈R_{m,j})
Fm(x)=Fm−1(x)+lr∗∑cm,jI(xi∈Rm,j)
(6)重复3~5共M次,得到最终的强学习器
F
M
(
x
)
F_M(x)
FM(x)
F ( x ) = F M ( x ) = F 0 ( x ) + l r ∗ ∑ m = 1 M ∑ j = 1 J c m , j I ( x i ∈ R m , j ) 最终的预测结果为: y ^ i = 1 1 + e − F M ( x ) \begin{align*} F(x)&=F_M(x)=F_0(x)+lr*\sum_{m=1}^M\sum _{j=1}^Jc_{m,j}I(x_i∈R_{m,j})\\\\ &最终的预测结果为:\\\\ \hat y_i&=\frac{1}{1+e^{-F_M(x)}}\\ \end{align*} F(x)y^i=FM(x)=F0(x)+lr∗m=1∑Mj=1∑Jcm,jI(xi∈Rm,j)最终的预测结果为:=1+e−FM(x)1
3.3 掉包sklearn实现
# 调取sklearn包
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor #sklearn中,线性回归模型在linear_model模块中
from sklearn import tree
# 调取sklearn中自带的数据集
from sklearn.datasets import load_iris #调用鸢尾花分类数据集
from sklearn.datasets import load_boston #调用波士顿房价数据集
X1, y1 = load_iris(return_X_y=True) #获取X,y数据
X2, y2 = load_boston(return_X_y=True) #获取X,y数据
rfc = GradientBoostingClassifier()#初始化一个随机森林分类模型
rfr = GradientBoostingRegressor()#初始化一个随机森林回归模型
rfc.fit(X1,y1) #fit函数用于训练
rfr.fit(X2,y2)
详细情况参见:sklearn.ensemble.GradientBoostingClassifier — scikit-learn 1.3.0 documentation
entBoostingClassifier — scikit-learn 1.3.0 documentation](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html#sklearn.ensemble.GradientBoostingClassifier)