GBDT不仅可以应用于分类任务,它在回归任务中也有很好的表现。GBDT回归算法的原理与二分类算法很相似,最大的区别在于目标函数的选择上。下面就来看看GBDT回归算法的具体步骤。
该算法的具体步骤如下:
∑ i = 1 N ∂ L ( y i , c ) ∂ c = N c − ∑ i = 1 N y i = 0 ⇒ c = ∑ i = 1 N y i N \sum_{i=1}^N\frac{\partial L(y_i,c)}{\partial c}=Nc - \sum_{i=1}^Ny_i=0 \Rightarrow c=\frac{\sum_{i=1}^N y_i}{N} i=1∑N∂c∂L(yi,c)=Nc−i=1∑Nyi=0⇒c=N∑i=1Nyi
故:
F 0 ( x ) = c = ∑ i = 1 N y i N F_0(\boldsymbol x)=c=\frac{\sum_{i=1}^N y_i}{N} F0(x)=c=N∑i=1Nyi
也就是说,用所有训练样本标签的均值初始化 F 0 ( x ) F_0(\boldsymbol x ) F0(x)。
初始化完成后,下面要建立起 M M M棵分类回归树,设每一棵树的编号为 m m m ( m = 1 , 2 , . . . , M ) (m=1,2,...,M) (m=1,2,...,M),求出各棵树对各个样本 x i \boldsymbol x_i xi的残差。以第1棵树对第 i i i个样本的残差为例:
r 1 , i = − ∣ ∂ L ( y i , F 0 ( x i ) ) ∂ F 0 ( x i ) ∣ = − ∂ 1 2 ( y i − F 0 2 ( x i ) ) ∂ F 0 ( x i ) = y i − F 0 ( x i ) r_{1,i}=-|\frac{\partial L(y_i, F_{0}(\boldsymbol x_i))}{\partial F_{0}(\boldsymbol x_i)}|=-\frac{\partial\frac{1}{2}(y_i-F^2_0(\boldsymbol x_i))}{\partial F_0(\boldsymbol x_i)}=y_i-F_0(\boldsymbol x_i) r1,i=−∣∂F0(xi)∂L(yi,F0(xi))∣=−∂F0(xi)∂21(yi−F02(xi))=yi−F0(xi)
也就是说,第1棵提升树对第 i i i个样本 x i \boldsymbol x_i xi的残差为:样本 x i \boldsymbol x_i xi的标签值减去初始弱分类器 F 0 ( x i ) F_0(\boldsymbol x_i) F0(xi)对样本 x i x_i xi的预测值。
根据上面求得的残差,就可以计算出第1棵树对其第 j j j个叶子节点的最佳拟合值为:
c 1 , j = arg min k j ∑ x i ∈ R 1 , j J 1 L ( y i , F 0 ( x i ) + k j ) = arg min k j ∑ x i ∈ R 1 , j J 1 1 2 [ y i − ( F 0 ( x i ) + k j ) ] 2 c_{1,j}=\mathop{\arg\min}_{k_j}\sum_{\boldsymbol x_i\in R_{1,j}}^{J_1}L(y_i,F_0{\boldsymbol (x_i)+k_j})=\mathop{\arg\min}_{k_j}\sum_{x_i\in R_{1,j}}^{J_1}\frac{1}{2}[y_i-(F_0(\boldsymbol x_i)+k_j)]^2 c1,j=argminkjxi∈R1,j∑J1L(yi,F0(xi)+kj)=argminkjxi∈R1,j∑J121[yi−(F0(xi)+kj)]2
其中 R 1 , j R_{1,j} R1,j表示第1棵树的第 j j j个叶子节点区域, J 1 J_1 J1为第1棵树叶子节点的个数。
该函数也是个凸函数,使得导数为0的 k j k_j kj值就是我们要求的最佳拟合值 c 1 , j c_{1,j} c1,j,故可求得:
∂ ∑ x i ∈ R 1 , j J 1 1 2 [ y i − ( F 0 ( x i ) + k j ) 2 ] ∂ k j = 0 ⇓ k j = ∑ x i ∈ R 1 , j J 1 r 1 , i J 1 \frac{\partial\sum_{\boldsymbol x_i\in R_{1,j}}^{J_1}\frac{1}{2}[y_i-(F_0(\boldsymbol x_i)+k_j)^2]}{\partial k_j} =0 \\\Downarrow \\k_j=\frac{\sum_{\boldsymbol x_i\in R_{1,j}}^{J_1}r_{1,i}}{J_1} ∂kj∂∑xi∈R1,jJ121[yi−(F0(xi)+kj)2]=0⇓kj=J1∑xi∈R1,jJ1r1,i
即:第1棵树第 j j j个节点的最佳拟合值 c 1 , j c_{1,j} c1,j为第1棵树第 j j j个叶子节点里所有样本残差之和的平均值。对于其他第 m m m棵树也同理。
在上面求得的最佳拟合值的基础上,就可以用下面的公式求出初始分类器 F 0 ( x ) F_0(\boldsymbol x) F0(x)经过第1棵树提升后的新学习器 F 1 ( x ) F_1(\boldsymbol x) F1(x)的表达式:
F 1 ( x ) = F 0 ( x ) + ∑ j = 1 J 1 c 1 , j I ( x ∈ R 1 , j ) F_1(\boldsymbol x)=F_0(\boldsymbol x)+\sum_{j=1}^{J_1}c_{1,j}I(x\in R_{1,j}) F1(x)=F0(x)+j=1∑J1c1,jI(x∈R1,j)
类推到第 m m m个学习器:
F m ( x ) = F m − 1 ( x ) + ∑ j = 1 J m c m , j I ( x ∈ R m , j ) F_m(\boldsymbol x)=F_{m-1}(\boldsymbol x)+\sum_{j=1}^{J_m}c_{m,j}I(x\in R_{m,j}) Fm(x)=Fm−1(x)+j=1∑Jmcm,jI(x∈Rm,j)
用上面的方法经过 M M M次提升后,得到最终强学习器的表达式如下:
F M ( x ) = F 0 ( x ) + ∑ m = 1 M ∑ j = 1 J m c m , j I ( x ∈ R m , j ) F_{M}(\boldsymbol x)=F_{0}(\boldsymbol x)+\sum_{m=1}^M\sum_{j=1}^{J_m}c_{m,j}I(x\in R_{m,j}) FM(x)=F0(x)+m=1∑Mj=1∑Jmcm,jI(x∈Rm,j)
其中 ∑ m = 1 M \sum_{m=1}^M ∑m=1M表示对所有 M M M棵提升树求累加, ∑ j = 1 J m c m , j \sum_{j=1}^{J_m}c_{m,j} ∑j=1Jmcm,j表示对每棵提升树的所有叶子节点的最佳拟合值求累加。意思就是说,以初始回归器 F 0 ( x ) F_{0}(\boldsymbol x) F0(x)为起点,不断将所有 M M M棵树的所有叶子节点的最佳拟合值加起来,最终就得到了强回归器 F M ( x ) F_{M}(\boldsymbol x) FM(x)。为了控制每一棵提升树的对回归器的提升程度,这里同样可以引入学习率 η \eta η来控制每一棵树对回归器的提升程度:
F M ( x ) = F 0 ( x ) + ∑ m = 1 M ∑ j = 1 J m η c m , j I ( x ∈ R m , j ) F_{M}(\boldsymbol x)=F_{0}(\boldsymbol x)+\sum_{m=1}^M\sum_{j=1}^{J_m}\eta \,c_{m,j}I(x\in R_{m,j}) FM(x)=F0(x)+m=1∑Mj=1∑Jmηcm,jI(x∈Rm,j)
经过上面六个步骤,就完成了GBDT回归算法的流程。
sklearn中的GradientBoostingRegressor
类对GradientBoosting分类算法进行了实现。
class sklearn.ensemble.GradientBoostingRegressor(*, loss='ls', learning_rate=0.1, n_estimators=100, subsample=1.0, criterion='friedman_mse', min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_depth=3, min_impurity_decrease=0.0, min_impurity_split=None, init=None, random_state=None, max_features=None, alpha=0.9, verbose=0, max_leaf_nodes=None, warm_start=False, presort='deprecated', validation_fraction=0.1, n_iter_no_change=None, tol=0.0001, ccp_alpha=0.0)
由于GBDT分类算法和GBDT回归算法的流程很相似,最大的差别在与损失函数的选择上,所以GradientBoostingRegressor类的原型与GradientBoostingClassifier非常类似,两者的参数、属性和方法也几乎完全一致,因此为了节省篇幅,下面只列举出GradientBoostingRegressor类与
GradientBoostingClassifier类不同的地方。
GradientBoostingRegressor类中的常用参数与GradientBoostingClassifier类最大的不同在于loss
和alpha
:
loss
: 默认为’ls’
表示回归损失函数的类型,可用选项为{ ‘ls’, ‘lad’, ‘huber’, ‘quantile’ }。下面将一一说明各个选项所代表的损失函数,以及使用这些损失函数时回归模型的优化目标(下面公式用 t a r g e t target target表示优化目标,用 f ( x i ) f(\boldsymbol x_i) f(xi)表示模型对第 i i i个样本 x i \boldsymbol x_i xi的拟合值):
‘ls’ (least squares):表示最小二乘回归损失。它将样本的标签与拟合值之差的平方(即均方误差MSE)作为回归拟合效果的评价标准。此时模型的优化目标为:
t a r g e t = arg min f ( x i ) ∑ i = 1 N ( y i − f ( x i ) ) 2 target=\mathop{\arg\min}_{f(\boldsymbol x_i)}\sum_{i=1}^N (y_i-f(\boldsymbol x_i))^2 target=argminf(xi)i=1∑N(yi−f(xi))2
采用该损失函数时的拟合效果如下:
使用该损失函数时,噪声点所造成的损失容易被放大,使得模型容易对噪声进行拟合,增大过拟合的风险。
‘lad’ (least absolute deviations):表示最小一乘回归损失。它将样本的标签与拟合值之差的绝对值(即平均绝对误差MAE)作为回归拟合效果的评价标准。此时模型的优化目标为:
t a r g e t = arg min f ( x i ) ∑ i = 1 N ∣ y i − f ( x i ) ∣ target=\mathop{\arg\min}_{f(\boldsymbol x_i)}\sum_{i=1}^N|y_i-f(\boldsymbol x_i)| target=argminf(xi)i=1∑N∣yi−f(xi)∣
采用该损失函数时的拟合效果如下:
使用该损失函数时模型对噪声的敏感度较小,可以在一定程度上缓解最小二乘回归损失函数所带来的过拟合问题。
‘huber’ (Huber Loss):表示Huber损失。此时模型的优化目标为:
t a r g e t = { arg min f ( x i ) ∑ i = 1 N 1 2 ( y i − f ( x i ) ) 2 , i f ∣ y i − f ( x i ) ∣ ⩽ δ arg min f ( x i ) ∑ i = 1 N ( ∣ y i − f ( x i ) ∣ − 1 2 δ ) , o t h e r w i s e target =\left\{ \begin{aligned}\mathop{\arg\min}_{f(\boldsymbol x_i)}\sum_{i=1}^N\frac{1}{2}(y_i-f(\boldsymbol x_i))^2,\quad if\, |y_i-f(\boldsymbol x_i)|\leqslant\delta \\ \mathop{\arg\min}_{f(\boldsymbol x_i)}\sum_{i=1}^N(|y_i-f(\boldsymbol x_i)|-\frac{1}{2}\delta),\quad otherwise \end{aligned} \right. target=⎩ ⎨ ⎧argminf(xi)i=1∑N21(yi−f(xi))2,if∣yi−f(xi)∣⩽δargminf(xi)i=1∑N(∣yi−f(xi)∣−21δ),otherwise
这个函数看起来很复杂,但实际上意思就是:当预测偏差小于等于 δ \delta δ时,它等价于最小二乘回归;当预测偏差大于 δ \delta δ时,它等价于最小一乘回归,所以综合了两种回归方法的优点,对噪声的鲁棒性更强。这个 δ \delta δ 可以由用户指定。当指定loss='huber'
时,可以通过调整参数alpha
来调整 δ \delta δ的值,从而实现不同的拟合效果。直观图如下:
‘quantile’ (quantile regression loss):表示分位数回归损失。在有些时候,我们对样本 x i \boldsymbol x_i xi做回归预测不仅仅是为了得到一个单一的回归预测值 f ( x i ) f(\boldsymbol x_i) f(xi),而是更希望能探索出 f ( x i ) f(\boldsymbol x_i) f(xi)的完整分布状况,或者说某些情况下我们更希望了解 f ( x i ) f(\boldsymbol x_i) f(xi)的某个分位数,这时我们就需要用到分位数回归。通过指定loss='quntile'
,并调整参数alpha
对分位值 γ \gamma γ进行赋值,就可以对回归预测值给予不同的惩罚,获取不同的分位数回归。
同样和GradientBoostingClassifier类的几乎完全一样。
GradientBoostingRegressor类的方法比GradientBoostingClassifier的少了如下几个:
staged_predict(X)
staged_predict_probe(X)
predict_proba(X)
predict_log_proba(X)
decision_function(X)
staged_decision_function(X)
其他方法的名称和用法与GradientBoostingClassifier类一样,这里不再赘述。
在本实例中我们会对GBDT回归模型分别用到这4种回归方法进行比较,加深读者对它们对GBDT回归模型拟合效果所造成的影响的认识。在开始之前,需要先定义随机数种子,确保每次运行生成的数据集均相同,这样才能比较采用不同回归损失函数时拟合效果的差异。
np.random.seed(1)
代码如下:
# 定义回归预测的目标函数
def xsin(x):
return x * np.sin(x)
# 定义0到10的1000个随机数作为训练数据集
X = np.atleast_2d(np.random.uniform(0, 10, size=100)).T.astype(np.float32)
# 求出训练数据集中各个点的回归目标值
y = xsin(X).ravel()
# 为训练数据集的回归目标值添加随机噪声扰动,更便于比较不同回归方法的拟合效果
disturb_y = 1.5 + 2 * np.random.random(y.shape)
# 将上面得到的噪声扰动转化为高斯分布值,拉大不同噪声点之间的距离,优化回归拟合的可视化效果
y += np.random.normal(0, disturb_y).astype(np.float32)
# 创建1000个0到10之间的随机数,把它们送给拟合好的模型进行预测与可视化,就可以可视化整个模型的拟合效果
xx = np.atleast_2d(np.linspace(0, 10, 1000)).T.astype(np.float32)
最小二乘回归
前面我们已经简单介绍过各个损失函数的含义和原理,在这一部分,我们就来探究它们对模型回归拟合效果的影响。
首先使用最小二乘回归损失函数,并画出拟合后的效果。代码如下:
# 指定loss='ls'表示使用最小二乘回归损失函数
gdb1 = GradientBoostingRegressor(loss='ls', n_estimators=250, learning_rate=0.1)
gdb1.fit(X, y)
# 预测
y_pred = gdb1.predict(xx)
# 画图
fig = plt.figure(figsize=(10, 6))
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, xsin(xx), color='blue', linestyle='--', label='xsin(x)')
plt.plot(xx, y_pred, 'r-', label='Prediction')
plt.xlabel('x')
plt.ylabel('xsin(x)')
plt.title("Least Square Loss")
plt.ylim(-15, 15)
plt.legend(loc='upper left')
plt.show()
输出结果如下:
可以看到,预测曲线(红色)趋向于对所有的样本点都进行拟合,模型有过拟合的趋势。
最小一乘回归
代码如下:
# 指定loss='lad'表示使用最小一乘回归损失函数
gdb2 = GradientBoostingRegressor(loss='lad', n_estimators=250, learning_rate=0.1)
gdb2.fit(X, y)
# 预测
y_pred = gdb2.predict(xx)
# 画图
fig = plt.figure(figsize=(10, 6))
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, xsin(xx), color='blue', linestyle='--')
plt.plot(xx, y_pred, 'r-', label='Prediction')
plt.xlabel('$x$')
plt.ylabel('$xsin(x$')
plt.title("Least Absolute Loss")
plt.ylim(-15, 15)
plt.legend(loc='upper left')
plt.show()
输出结果如下:
对比图1.6.4,可以看到使用该损失函数时模型不会趋向于对噪声进行拟合,此时模型抗过拟合的能力较强。
alpha取值不同的huber回归
# 指定loss='huber'表示使用huber回归损失函数,此时指定不同的alpha参数值可实现不同的拟合效果
# alpha=0.1
gdb3_alpha01 = GradientBoostingRegressor(loss='huber', alpha=0.1,
n_estimators=250,
learning_rate=0.1)
gdb3_alpha01.fit(X, y)
# alpha=0.4
gdb3_alpha04 = GradientBoostingRegressor(loss='huber', alpha=0.4,
n_estimators=250,
learning_rate=0.1)
gdb3_alpha04.fit(X, y)
# alpha=0.6
gdb3_alpha06 = GradientBoostingRegressor(loss='huber', alpha=0.6,
n_estimators=250,
learning_rate=0.1)
gdb3_alpha06.fit(X, y)
# alpha=0.9
gdb3_alpha09 = GradientBoostingRegressor(loss='huber', alpha=0.9,
n_estimators=250,
learning_rate=0.1)
gdb3_alpha09.fit(X, y)
# 预测
y_pred01 = gdb3_alpha01.predict(xx)
y_pred04 = gdb3_alpha04.predict(xx)
y_pred06 = gdb3_alpha06.predict(xx)
y_pred09 = gdb3_alpha09.predict(xx)
# 画图
fig = plt.figure(figsize=(15, 10))
plt.subplot(221)
plt.plot(xx, xsin(xx), color='blue',linestyle='--')
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, y_pred01, 'r-', label='Prediction')
plt.xlabel('x')
plt.ylabel('xsin(x)')
plt.ylim(-15, 15)
plt.title("Huber Loss with alpha=0.1")
plt.legend(loc='upper left')
plt.subplot(222)
plt.plot(xx, xsin(xx), color='blue',linestyle='--')
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, y_pred04, 'r-', label='Prediction')
plt.xlabel('x')
plt.ylabel('xsin(x)')
plt.ylim(-15, 15)
plt.title("Huber Loss with alpha=0.4")
plt.legend(loc='upper left')
plt.subplot(223)
plt.plot(xx, xsin(xx), color='blue',linestyle='--')
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, y_pred06, 'r-', label='Prediction')
plt.xlabel('x')
plt.ylabel('xsin(x)')
plt.ylim(-15, 15)
plt.title("Huber Loss with alpha=0.6")
plt.legend(loc='upper left')
plt.subplot(224)
plt.plot(xx, xsin(xx), color='blue',linestyle='--')
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, y_pred09, 'r-', label='Prediction')
plt.xlabel('x')
plt.ylabel('xsin(x)')
plt.ylim(-15, 15)
plt.title("Huber Loss with alpha=0.9")
plt.legend(loc='upper left')
输出结果如下:
若指定huber loss为损失函数,则当alpha取值不同时,模型的回归拟合效果也会有很大的差异。从上图可以看到,alpha较大时,模型倾向于对噪声进行拟合,此时huber loss的效果与最小二乘回归损失函数比较接近;而当alpha较小时,模型对噪声的敏感程度较小,此时huber loss的效果与最小一乘回归损失函数比较接近。因此,使用huber loss可以非常方便地通过调整参数alpha来控制拟合的效果。
使用quantile回归,并取不同的分位数 alpha
最后再来看一下分位数回归损失函数。
# 指定loss='quantile'表示使用分位数回归损失函数,此时参数alpha表示分位值
# alpha=0.05
gdb4_alpha005 = GradientBoostingRegressor(loss='quantile', alpha=0.05,
n_estimators=250,
learning_rate=0.1)
gdb4_alpha005.fit(X, y)
# alpha=0.5
gdb4_alpha05 = GradientBoostingRegressor(loss='quantile', alpha=0.5,
n_estimators=250,
learning_rate=0.1)
gdb4_alpha05.fit(X, y)
# alpha=0.95
gdb4_alpha095 = GradientBoostingRegressor(loss='quantile', alpha=0.95,
n_estimators=250,
learning_rate=0.1)
gdb4_alpha095.fit(X, y)
# 分别得到0.95、0.5、0.05三种分位值下模型的预测结果
y_upper = gdb4_alpha095.predict(xx)
y_middle= gdb4_alpha05.predict(xx)
y_lower = gdb4_alpha005.predict(xx)
# 画图
fig = plt.figure(figsize=(10, 6))
plt.plot(xx, xsin(xx), color='blue',linestyle='--')
plt.plot(X, y, 'k.', markersize=10, label='Data Points')
plt.plot(xx, y_upper, color ='orange', linestyle = '-', label='Prediction with alpha=0.95 ')
plt.plot(xx, y_middle, 'r-', label='Prediction with alpha=0.5 ')
plt.plot(xx, y_lower, color = 'purple', linestyle = '-', label='Prediction with alpha=0.05 ')
plt.fill(np.concatenate([xx, xx[::-1]]),
np.concatenate([y_upper, y_lower[::-1]]),
alpha=0.2, fc='g', ec='None', label='90% Prediction Interval')
plt.xlabel('x')
plt.ylabel('xsin(x)')
plt.ylim(-15, 15)
plt.title("Quantile Loss with Different alpha")
plt.legend(loc='lower right')
plt.show()
输出结果如下:
在这里,我们结合两个重要的概念——置信度和置信区间——来分析这副图。
一般我们用[a,b]表示样本估计总体平均值误差范围的区间,a、b的具体数值取决于模型对于“该区间中包含总体均值”这一结果的可信程度,因此[a,b]被称为置信区间。而对于选定的置信区间[a,b],我们的目的是为了让“a和b之间包含总体平均值”的结果有一特定的概率,这个概率就是置信水平。
上图分别用橙色紫色表示了0.95和0.05两种分位值取值下的分位数回归预测曲线,并且画出了90%的置信区间(用浅绿色表示),那么这里的a就是不同样本对应的紫色曲线的值,b就是不同样本下对应的橙色曲线的值,橙色和紫色曲线就围成了整个数据集的置信区间,并且这个置信区间的置信水平为90%,表示该区间内有90%的概率会出现样本的总体均值。除此之外,图中还用红色曲线表示了0.5分位值的回归预测曲线,对比图1.6.5,可以发现,当分位值取值适中时,分位数回归曲线对数据的拟合程度与选择最小一乘回归损失函数的拟合程度非常接近。
经过对比可以发现,分位数回归相对于一般的回归模型来说,条件更为宽泛,它使得模型的回归预测结果可以描述样本的全局特征,而不只是局限于均值。另一方面,分位数回归使得使得模型的回归预测值受噪声的影响较小。因此,使用分位数回归损失函数可以使得GBDT模型具有较强的鲁棒性。