梯度提升树(GBDT,Gradient Boosting Decision Tree)回归是一种集成学习方法,它通过逐步构建多个决策树来优化预测结果,尤其适用于回归问题。GBDT 通过“加法模型”逐步提高模型的预测能力,每一步都在减少前一步模型的误差。
1. 预测过程
GBDT 的回归预测过程为:初始值 + 每棵决策树的输出结果 * 学习率,如下公式所示:

其中:
- \( \hat{y}(x) \) 表示模型对输入 \( x \) 的最终预测值
- \( \hat{y}_{0}(x) \) 是初始值,可以理解为初步的预测分数
- \( f_{m}(x) \) 是第 \( m \) 棵树的输出分数
- \( \eta \) 表示学习率,控制每棵树的贡献
- \( M \) 表示 GBDT 中回归决策树的数量
假设:我们基于训练数据得到的 GBDT 回归模型如下,我们需要对新数据 [0.64768854, -0.1382643, 0.49671415]
进行预测:



上面的三棵回归决策树构成了强学习器 GBDT。
首先计算初始值,对于平方误差(Squared Error),初始值为训练数据的均值,即:mean(y) = -8.09
然后将新数据分别送入到不同的决策树中得到预测值:
- 第一个决策树输出值:31.51
- 第二个决策树输出值:28.36
- 第三个决策树输出值:19.47
假设学习率为 0.1,则最终输出值:-8.09 + (31.51 + 28.36 + 19.47) * 0.1 = -0.155。下面为具体的代码计算过程:
from sklearn.ensemble import GradientBoostingRegressor from sklearn.datasets import make_regression import numpy as np import matplotlib.pyplot as plt from sklearn.tree import plot_tree # 生成随机数据 np.random.seed(0) x, y = make_regression(n_samples=10, n_features=3, random_state=42) # 训练分类模型 gbdt = GradientBoostingRegressor(n_estimators=3, max_depth=2, criterion='squared_error', loss='squared_error') gbdt.fit(x, y) # 可视化 GBDT for estimator in gbdt.estimators_: plt.figure(figsize=(13, 8)) plot_tree(estimator[0], filled=True, rounded=True, fontsize=14, precision=2) plt.show() # 新数据预测 new_x, new_y = make_regression(n_samples=1, n_features=3, random_state=42) print('输入数据:', new_x, new_y) # 直接计算类别概率 print('直接计算:', gbdt.predict(new_x)[0]) # 分布计算类别概率 def test(): # 1. 初始值 total_score = np.mean(y) # squared_error,使用均值 # total_score = np.median(y) # absolute_error,使用中位数 # total_score = np.median(y) # huber,使用中位数 # total_score = np.percentile(y, 90) # quantile,0.9 分位数,alpha 参数指定 # 2. 累加每个树的得分 learning_rate = 0.1 for estimator in gbdt.estimators_: # 每棵树计算得分 score = estimator[0].predict(new_x) # 累加每棵树得分 total_score += score[0] * learning_rate # 3. 将得分输出概率 print('手动计算:', total_score) if __name__ == '__main__': test()
程序输出结果:
输入数据: [[ 0.64768854 -0.1382643 0.49671415]] 9.35482833533384 直接计算: -0.15512286528967145 手动计算: -0.15512286528967145
2. 训练过程
我们以 criterion=’squared_error’ 和 loss=’squared_error’ 为例,训练数据如下:
特征1 | 特征2 | 特征3 | 目标值 |
-0.90802408 | 1.46564877 | -1.4123037 | -106.40638687 |
-0.54438272 | -1.15099358 | 0.11092259 | -46.41270092 |
0.24196227 | -1.72491783 | -1.91328024 | -88.70824211 |
0.37569802 | -0.29169375 | -0.60063869 | -4.29054154 |
1.52302986 | -0.23413696 | -0.23415337 | 91.04704754 |
0.54256004 | -0.46572975 | -0.46341769 | 11.04262105 |
0.49671415 | 0.64768854 | -0.1382643 | 35.80536172 |
-0.56228753 | 0.31424733 | -1.01283112 | -79.21856515 |
1.57921282 | -0.46947439 | 0.76743473 | 136.10440487 |
-0.2257763 | -1.42474819 | 0.0675282 | -29.86346474 |
接下来,演示训练过程。根据下面 GBDT 公式:

可知训练过程需要做两件事:
- 计算初值
- 训练多个回归决策树
对于初始值,使用不同的损失函数,初始值的计算方式也有所不同,例如:使用 squared_error 损失函数时,会使用训练数据的均值作为初值。使用 absolute_error 或者 huber 时,则使用训练数据的中位数作为初值。我们这里使用的是 squared_error,所以初始值 \( \hat{y}_{0}(x) = mean(y) = -8.09 \)。
接下来,我们开始训练 \( M=3 \) 个回归决策树,每个回归决策树训练步骤如下:
- 根据损失函数计算残差
- 以残差作为目标值训练回归决策树
- 修正叶子节点的输出值(默认是叶子节点的样本均值,这里根据不同的损失函数进行修正)
我们这里使用 squared_error,其残差(负梯度)计算公式如下:

其中,\( y_{i} \) 为样本真实目标值,\( F(x_{i}) \) 为到当前回归决策树的 GBDT 输出值。
特征1 | 特征2 | 特征3 | 目标值 | 残差 |
-0.90802408 | 1.46564877 | -1.4123037 | -106.40638687 | -98.31638687 |
-0.54438272 | -1.15099358 | 0.11092259 | -46.41270092 | -38.32270092 |
0.24196227 | -1.72491783 | -1.91328024 | -88.70824211 | -80.61824211 |
0.37569802 | -0.29169375 | -0.60063869 | -4.29054154 | 3.79945846 |
1.52302986 | -0.23413696 | -0.23415337 | 91.04704754 | 99.13704754 |
0.54256004 | -0.46572975 | -0.46341769 | 11.04262105 | 19.13262105 |
0.49671415 | 0.64768854 | -0.1382643 | 35.80536172 | 43.89536172 |
-0.56228753 | 0.31424733 | -1.01283112 | -79.21856515 | -71.12856515 |
1.57921282 | -0.46947439 | 0.76743473 | 136.10440487 | 144.19440487 |
-0.2257763 | -1.42474819 | 0.0675282 | -29.86346474 | -21.77346474 |
使用上面的数据进行训练,这里注意,训练拟合的目标值是残差,而不是真实值。假设 max_depth=2,使用 criterion 指定的方法计算所有候选切分点,并进行树的分裂,训练结束后,得到如下回归决策树:

最后一步进行叶子节点值的修正,对于 squared_error 损失函数,叶子节点不需要额外的修正。注意:对于其他的损失函数,叶子节点值并不是简单得样本均值,而是经过一定的方法进行修正,这里就不深入探讨这一部分了。
特征1 | 特征2 | 特征3 | 目标值 | 当前输出值 | 累计输出值 |
-0.90802408 | 1.46564877 | -1.4123037 | -106.40638687 | -83.35435143 | -16.42548176 |
-0.54438272 | -1.15099358 | 0.11092259 | -46.41270092 | -18.76552245 | -9.966598861 |
0.24196227 | -1.72491783 | -1.91328024 | -88.70824211 | -83.35435143 | -16.42548176 |
0.37569802 | -0.29169375 | -0.60063869 | -4.29054154 | -18.76552245 | -9.966598861 |
1.52302986 | -0.23413696 | -0.23415337 | 91.04704754 | 121.66577282 | 4.0765306665 |
0.54256004 | -0.46572975 | -0.46341769 | 11.04262105 | 31.514038 | -4.938642815 |
0.49671415 | 0.64768854 | -0.1382643 | 35.80536172 | 31.514038 | -4.938642815 |
-0.56228753 | 0.31424733 | -1.01283112 | -79.21856515 | -83.35435143 | -16.42548176 |
1.57921282 | -0.46947439 | 0.76743473 | 136.10440487 | 121.66577282 | 4.0765306665 |
-0.2257763 | -1.42474819 | 0.0675282 | -29.86346474 | -18.76552245 | -9.966598861 |
上表中,累计输出值,是到当前决策树所有决策树的输出 * 学习率 + 初始值。接下来,训练第二棵决策树,还是先计算残差:
特征1 | 特征2 | 特征3 | 目标值 | 残差 |
-0.90802408 | 1.46564877 | -1.4123037 | -106.40638687 | -89.9809051156 |
-0.54438272 | -1.15099358 | 0.11092259 | -46.41270092 | -36.4461020608 |
0.24196227 | -1.72491783 | -1.91328024 | -88.70824211 | -72.2827603483 |
0.37569802 | -0.29169375 | -0.60063869 | -4.29054154 | 5.67605732113 |
1.52302986 | -0.23413696 | -0.23415337 | 91.04704754 | 86.9705168713 |
0.54256004 | -0.46572975 | -0.46341769 | 11.04262105 | 15.98126386669 |
0.49671415 | 0.64768854 | -0.1382643 | 35.80536172 | 40.7440045344 |
-0.56228753 | 0.31424733 | -1.01283112 | -79.21856515 | -62.79308338979 |
1.57921282 | -0.46947439 | 0.76743473 | 136.10440487 | 132.0278742051 |
-0.2257763 | -1.42474819 | 0.0675282 | -29.86346474 | -19.8968658841 |
这里残差计算就是原始的目标值 – 累计输出值,有了残差,继续训练第二个回归决策树。至此,大家应该能发现了,GBDT 的训练过程就是不停的基于残差方向进行训练,目标是减少 GBDT 预测的误差,而且随着训练的回归决策树越来越多,预测的精度也会越来越高。
但是,需要注意的是,训练的决策树并不是越多越好,更多的决策树能够带来较小的训练误差,但是也可能会导致过度拟合训练数据,引起较大的泛化误差。
另外,我们在训练过程中,引入了学习率。学习率降低了训练时,每棵树对预测结果的贡献,从而使得需要训练更多的回归决策树。所以,学习率是用来平衡每棵树的贡献和决策树数量的一个参数。