文章目录
1. 前向分步算法
AdaBoost算法还有另一个解释,即可以认为AdaBoost算法是模型为加法模型、损失函数为指数函数、学习算法为前向分步算法时的二类分类学习方法。
1.1 加法模型
f
(
x
)
=
∑
m
=
1
M
β
m
b
(
x
;
γ
m
)
(1.1)
f(x) = \sum^M_{m=1}\beta_mb(x;\gamma_m)\tag{1.1}
f(x)=m=1∑Mβmb(x;γm)(1.1)
其中,
b
(
x
;
γ
m
)
b(x;\gamma_m)
b(x;γm)为基函数,
γ
m
\gamma_m
γm为基函数的参数,
β
m
\beta_m
βm为基函数的系数。显然,AdaBoost的基分类器的线性组合
f
(
x
)
=
∑
m
=
1
M
α
m
G
m
(
x
)
f(x) = \sum^M_{m=1}\alpha_mG_m(x)
f(x)=∑m=1MαmGm(x)是一个加法模型。
在给定训练数据以及损失函数
L
(
y
,
f
(
x
)
)
L(y,f(x))
L(y,f(x))的条件下,学习加法模型
f
(
x
)
f(x)
f(x)成为经验风险极小化,即损失函数极小化问题:
m
i
n
β
m
,
γ
m
∑
i
=
1
N
L
(
y
i
,
∑
m
=
1
M
β
m
b
(
x
i
;
γ
m
)
)
(1.2)
min_{\beta_m,\gamma_m}\sum_{i=1}^NL(y_i,\sum_{m=1}^M\beta_mb(x_i;\gamma_m))\tag{1.2}
minβm,γmi=1∑NL(yi,m=1∑Mβmb(xi;γm))(1.2)
通常这是一个复杂的优化问题。前向分步算法(forward stagwise algorithm)求解这一优化问题的思想是:因为学习的是加法模型,如果能够从前向后,每一步只学习一个基函数及其系数,逐步逼近优化目标函数式(1.2),那么就可以简化优化的复杂度。具体地,每步只需优化如下损失函数:
m
i
n
β
m
,
γ
m
∑
i
=
1
N
L
(
y
i
,
β
b
(
x
i
;
γ
m
)
)
(1.3)
min_{\beta_m,\gamma_m}\sum_{i=1}^NL(y_i,\beta b(x_i;\gamma_m))\tag{1.3}
minβm,γmi=1∑NL(yi,βb(xi;γm))(1.3)
可能这个式子还不是那么好理解,如果将其换成AdaBoost之后,就变为:
m
i
n
β
m
,
γ
m
∑
i
=
1
N
L
(
y
i
,
α
m
G
m
(
x
)
)
min_{\beta_m,\gamma_m}\sum_{i=1}^NL(y_i,\alpha_mG_m(x))
minβm,γmi=1∑NL(yi,αmGm(x))
1.2 前向分步算法
给定训练集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y n ) , } , x i ∈ X ⊆ R n T=\{ (x_1,y_1),(x_2,y_2), \dots,(x_N,y_n),\},x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n} T={(x1,y1),(x2,y2),…,(xN,yn),},xi∈X⊆Rn, y i ∈ Y = { + 1 , − 1 } y_{i} \in \mathcal{Y}=\{+1,-1\} yi∈Y={+1,−1}。损失函数 L ( y , f ( x ) ) L(y, f(x)) L(y,f(x)),基函数集合 { b ( x ; γ ) } \{b(x ; \gamma)\} {b(x;γ)},我们需要输出加法模型 f ( x ) f(x) f(x)。
(1)初始化:
f
0
(
x
)
=
0
f_{0}(x)=0
f0(x)=0
(2)对m = 1,2,…,M:
(a) 极小化损失函数:
(
β
m
,
γ
m
)
=
arg
min
β
,
γ
∑
i
=
1
N
L
(
y
i
,
f
m
−
1
(
x
i
)
+
β
b
(
x
i
;
γ
)
)
(1.4)
\left(\beta_{m}, \gamma_{m}\right)=\arg \min _{\beta, \gamma} \sum_{i=1}^{N} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+\beta b\left(x_{i} ; \gamma\right)\right)\tag{1.4}
(βm,γm)=argβ,γmini=1∑NL(yi,fm−1(xi)+βb(xi;γ))(1.4)
得到参数
β
m
\beta_{m}
βm与
γ
m
\gamma_{m}
γm
(b) 更新:
f
m
(
x
)
=
f
m
−
1
(
x
)
+
β
m
b
(
x
;
γ
m
)
(1.5)
f_{m}(x)=f_{m-1}(x)+\beta_{m} b\left(x ; \gamma_{m}\right)\tag{1.5}
fm(x)=fm−1(x)+βmb(x;γm)(1.5)
(3)得到加法模型:
f
(
x
)
=
f
M
(
x
)
=
∑
m
=
1
M
β
m
b
(
x
;
γ
m
)
(1.6)
f(x)=f_{M}(x)=\sum_{m=1}^{M} \beta_{m} b\left(x ; \gamma_{m}\right)\tag{1.6}
f(x)=fM(x)=m=1∑Mβmb(x;γm)(1.6)
这样,前向分步算法将同时求解从m=1到M的所有参数 β m \beta_{m} βm, γ m \gamma_{m} γm的优化问题简化为逐次求解各个 β m \beta_{m} βm, γ m \gamma_{m} γm的问题。
1.3 前向分步算法与Adaboost的关系:
由前向分步算法可以推导出AdaBoost,用定理叙述这一关系:
定理 AdaBoost算法是前向分步加法算法的特里。这时,模型是由基本分类器组成的加法模型,损失函数是指数函数。(《统计学习方法》 李航著 清华大学出版社)
2. 梯度提升决策树(GBDT)
2.1 提升树模型
提升方法实际采用加法模型(即基函数的线性组合)与前向分步算法。以决策树为基函数的提升方法称为提升树(boosting tree)。对分类问题决策树是二叉分类树,对回归问题决策树是二叉回归树。在之前的adaboost中,基本分类器
x
<
v
x<v
x<v或
x
>
v
x>v
x>v,可以看作是由一个根结点直接连接两个叶结点的两个简单决策树,即所谓的决策树桩(decision stump)。提升树模型可以表示为决策树的加法模型:
f
M
(
x
)
=
∑
m
=
1
M
T
(
x
;
Θ
m
)
(2.1)
f_M(x) = \sum_{m=1}^MT(x;\Theta_m)\tag{2.1}
fM(x)=m=1∑MT(x;Θm)(2.1)
其中,
T
(
x
;
Θ
m
)
T(x;\Theta_m)
T(x;Θm)表示决策树,
Θ
m
\Theta_m
Θm为决策树的参数,M为树的个数。
2.2 提升树算法
输入数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
⋯
,
(
x
N
,
y
N
)
}
,
x
i
∈
X
⊆
R
n
,
y
i
∈
Y
⊆
R
T=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\}, x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n}, y_{i} \in \mathcal{Y} \subseteq \mathbf{R}
T={(x1,y1),(x2,y2),⋯,(xN,yN)},xi∈X⊆Rn,yi∈Y⊆R,输出最终的提升树
f
M
(
x
)
f_{M}(x)
fM(x)
(1)初始化
f
0
(
x
)
=
0
f_0(x) = 0
f0(x)=0
(2)对m = 1,2,…,M:
(a)计算每个样本的残差:
r
m
i
=
y
i
−
f
m
−
1
(
x
i
)
,
i
=
1
,
2
,
⋯
,
N
r_{m i}=y_{i}-f_{m-1}\left(x_{i}\right), \quad i=1,2, \cdots, N
rmi=yi−fm−1(xi),i=1,2,⋯,N
(b)拟合残差
r
m
i
r_{mi}
rmi学习一棵回归树,得到
T
(
x
;
Θ
m
)
T\left(x ; \Theta_{m}\right)
T(x;Θm) 。
(c)更新
f
m
(
x
)
=
f
m
−
1
(
x
)
+
T
(
x
;
Θ
m
)
f_{m}(x)=f_{m-1}(x)+T\left(x ; \Theta_{m}\right)
fm(x)=fm−1(x)+T(x;Θm)。
(3)得到最终的回归问题的提升树:
f
M
(
x
)
=
∑
m
=
1
M
T
(
x
;
Θ
m
)
f_{M}(x)=\sum_{m=1}^{M} T\left(x ; \Theta_{m}\right)
fM(x)=m=1∑MT(x;Θm)
2.3 梯度提升
提升树利用加法模型与前向分步算法实现学习的优化过程。当损失函数是平方损失和指数损失函数时,每一步的优化是很简单的。但对于一般损失函数而言,往往每一步的优化并不容易。
针对这一问题,Freidman提出了梯度提升(gradient boosting)算法。这是利用最速下降法的近似方法,其关键是利用损失函数的负梯度在当前模型的值:
−
[
∂
L
(
y
,
f
(
x
i
)
)
∂
f
(
x
i
)
]
f
(
x
)
=
f
m
−
1
(
x
)
-\left[ \frac{\partial L(y,f(x_i))}{\partial f(x_i)} \right]_{f(x)=f_{m-1}(x)}
−[∂f(xi)∂L(y,f(xi))]f(x)=fm−1(x)
作为回归问题提升算法中残差的近似值,拟合一个回归树。
梯度提升算法
输入:训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y n ) , } , x i ∈ X ⊆ R n T=\{ (x_1,y_1),(x_2,y_2), \dots,(x_N,y_n),\},x_{i} \in \mathcal{X} \subseteq \mathbf{R}^{n} T={(x1,y1),(x2,y2),…,(xN,yn),},xi∈X⊆Rn, y i ∈ Y ⊆ R y_{i} \in \mathcal{Y} \subseteq \mathbf{R} yi∈Y⊆R。损失函数 L ( y , f ( x ) ) L(y, f(x)) L(y,f(x));
输出:回归树 f ^ ( x ) \hat f(x) f^(x)。
(1)初始化
f
0
(
x
)
=
arg
min
c
∑
i
=
1
N
L
(
y
i
,
c
)
f_{0}(x)=\arg \min _{c} \sum_{i=1}^{N} L\left(y_{i}, c\right)
f0(x)=argcmini=1∑NL(yi,c)
(2)对于m=1,2,…,M:
(a)对i = 1,2,…,N计算:
r
m
i
=
−
[
∂
L
(
y
i
,
f
(
x
i
)
)
∂
f
(
x
i
)
]
f
(
x
)
=
f
m
−
1
(
x
)
r_{m i}=-\left[\frac{\partial L\left(y_{i}, f\left(x_{i}\right)\right)}{\partial f\left(x_{i}\right)}\right]_{f(x)=f_{m-1}(x)}
rmi=−[∂f(xi)∂L(yi,f(xi))]f(x)=fm−1(x)
(b)对
r
m
i
r_{mi}
rmi拟合一个回归树,得到第m棵树的叶结点区域
R
m
j
,
j
=
1
,
2
,
⋯
,
J
R_{m j}, j=1,2, \cdots, J
Rmj,j=1,2,⋯,J。
(c)对j=1,2,…J,计算:
c
m
j
=
arg
min
c
∑
x
i
∈
R
m
j
L
(
y
i
,
f
m
−
1
(
x
i
)
+
c
)
c_{m j}=\arg \min _{c} \sum_{x_{i} \in R_{m j}} L\left(y_{i}, f_{m-1}\left(x_{i}\right)+c\right)
cmj=argcminxi∈Rmj∑L(yi,fm−1(xi)+c)
(d)更新
f
m
(
x
)
=
f
m
−
1
(
x
)
+
∑
j
=
1
J
c
m
j
I
(
x
∈
R
m
j
)
f_{m}(x)=f_{m-1}(x)+\sum_{j=1}^{J} c_{m j} I\left(x \in R_{m j}\right)
fm(x)=fm−1(x)+∑j=1JcmjI(x∈Rmj)
(3)得到回归树:
f
^
(
x
)
=
f
M
(
x
)
=
∑
m
=
1
M
∑
j
=
1
J
c
m
j
I
(
x
∈
R
m
j
)
\hat{f}(x)=f_{M}(x)=\sum_{m=1}^{M} \sum_{j=1}^{J} c_{m j} I\left(x \in R_{m j}\right)
f^(x)=fM(x)=m=1∑Mj=1∑JcmjI(x∈Rmj)
算法第一步初始化,估计使损失函数极小化的常数值,它是只有一个根节点的树,第2(a)步计算损失函数的负梯度在当前模型的值,将它作为残差的估计。对于平方损失函数,它就是通常所说的残差;对于一般损失函数,它就是残差的近似值。第2(b)步回归树叶节点区域,以拟合残差的近似值。第2(c)步利用线性搜索估计叶结点区域的值,使损失函数极小化。第2(d)步更新回归树。第3步得到输出的最终模型 f ^ ( x ) \hat{f}(x) f^(x)。
2.4 代码实现
sklearn手册:
- https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingRegressor.html#sklearn.ensemble.GradientBoostingRegressor
- https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html?highlight=gra#sklearn.ensemble.GradientBoostingClassifier
from sklearn.metrics import mean_squared_error
from sklearn.datasets import make_friedman1
from sklearn.ensemble import GradientBoostingRegressor
'''
GradientBoostingRegressor参数解释:
loss:{‘ls’, ‘lad’, ‘huber’, ‘quantile’}, default=’ls’:‘ls’ 指最小二乘回归. ‘lad’ (最小绝对偏差) 是仅基于输入变量的顺序信息的高度鲁棒的损失函数。. ‘huber’ 是两者的结合. ‘quantile’允许分位数回归(用于alpha指定分位数)
learning_rate:学习率缩小了每棵树的贡献learning_rate。在learning_rate和n_estimators之间需要权衡。
n_estimators:要执行的提升次数。
subsample:用于拟合各个基础学习者的样本比例。如果小于1.0,则将导致随机梯度增强。subsample与参数n_estimators。选择会导致方差减少和偏差增加。subsample < 1.0
criterion:{'friedman_mse','mse','mae'},默认='friedman_mse':“ mse”是均方误差,“ mae”是平均绝对误差。默认值“ friedman_mse”通常是最好的,因为在某些情况下它可以提供更好的近似值。
min_samples_split:拆分内部节点所需的最少样本数
min_samples_leaf:在叶节点处需要的最小样本数。
min_weight_fraction_leaf:在所有叶节点处(所有输入样本)的权重总和中的最小加权分数。如果未提供sample_weight,则样本的权重相等。
max_depth:各个回归模型的最大深度。最大深度限制了树中节点的数量。调整此参数以获得最佳性能;最佳值取决于输入变量的相互作用。
min_impurity_decrease:如果节点分裂会导致杂质的减少大于或等于该值,则该节点将被分裂。
min_impurity_split:提前停止树木生长的阈值。如果节点的杂质高于阈值,则该节点将分裂
max_features{‘auto’, ‘sqrt’, ‘log2’},int或float:寻找最佳分割时要考虑的功能数量:
如果为int,则max_features在每个分割处考虑特征。
如果为float,max_features则为小数,并 在每次拆分时考虑要素。int(max_features * n_features)
如果“auto”,则max_features=n_features。
如果是“ sqrt”,则max_features=sqrt(n_features)。
如果为“ log2”,则为max_features=log2(n_features)。
如果没有,则max_features=n_features。
'''
X, y = make_friedman1(n_samples=1200, random_state=0, noise=1.0)
X_train, X_test = X[:200], X[200:]
y_train, y_test = y[:200], y[200:]
est = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1,
max_depth=1, random_state=0, loss='ls').fit(X_train, y_train)
mean_squared_error(y_test, est.predict(X_test))
from sklearn.datasets import make_regression
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
X, y = make_regression(random_state=0)
X_train, X_test, y_train, y_test = train_test_split(
X, y, random_state=0)
reg = GradientBoostingRegressor(random_state=0)
reg.fit(X_train, y_train)
reg.score(X_test, y_test)