GBDT:从残差到梯度提升树的完整理解
GBDT(Gradient Boosting Decision Tree,梯度提升决策树)是机器学习中非常常用的一类模型,尤其适合表格数据任务。
它的名字看起来很长,但拆开以后并不神秘:
- Gradient:沿着损失函数下降的方向修正模型。
- Boosting:一轮一轮训练模型,后面的模型修正前面的错误。
- Decision Tree:每一轮加入的基模型通常是一棵回归树。
一句话概括:
GBDT 不是一次训练一棵很大的树,而是一棵树接一棵树地补前面模型的错误。
1. 为什么需要 GBDT
单棵决策树有两个明显问题:
- 树太浅,模型表达能力不够,容易欠拟合。
- 树太深,容易把训练数据记得太死,导致过拟合。
GBDT 的思路不是训练一棵特别复杂的树,而是训练很多棵相对简单的树。
每一棵树只做一件事:
修正当前模型还没预测好的部分
这样模型可以逐步变强,同时通过学习率、树深度、树数量等参数控制复杂度。
2. 从一个房价例子理解 GBDT
假设我们要预测 3 套房子的价格:
| 房子 | 面积 | 房龄 | 真实价格 |
|---|---|---|---|
| A | 70 平 | 15 年 | 100 万 |
| B | 100 平 | 8 年 | 160 万 |
| C | 130 平 | 3 年 | 200 万 |
GBDT 一开始不会直接训练复杂模型,而是先给一个很粗的初始预测。
对于平方误差损失,常见初始预测就是所有标签的平均值:
$$
F_0(x)=\bar{y}=\frac{100+160+200}{3}=153.3
$$
也就是说,一开始模型对所有房子都预测:
153.3 万
显然这个预测很粗糙。于是我们计算残差:
$$
r_i=y_i-F_0(x_i)
$$
| 房子 | 真实价格 $y$ | 当前预测 $F_0$ | 残差 $r=y-F_0$ |
|---|---|---|---|
| A | 100 | 153.3 | -53.3 |
| B | 160 | 153.3 | 6.7 |
| C | 200 | 153.3 | 46.7 |
残差的含义很直观:
- A 的残差是 -53.3,说明当前预测太高了,要往下修正。
- B 的残差是 6.7,说明当前预测已经差不多。
- C 的残差是 46.7,说明当前预测太低了,要往上修正。
第一棵树学习的目标不是房价本身,而是这些残差。
3. 第一棵树如何修正预测
假设第一棵树 $h_1(x)$ 学到了一个简单规则:
如果 面积 <= 90 平:
输出 -40
否则:
输出 35
这表示:
- 小面积房子当前预测偏高,需要减一点。
- 大面积房子当前预测偏低,需要加一点。
如果学习率 $\eta=0.5$,GBDT 的更新公式是:
$$
F_1(x)=F_0(x)+\eta h_1(x)
$$
代入每套房子:
| 房子 | $F_0(x)$ | $h_1(x)$ | $\eta h_1(x)$ | 新预测 $F_1(x)$ |
|---|---|---|---|---|
| A | 153.3 | -40 | -20 | 133.3 |
| B | 153.3 | 35 | 17.5 | 170.8 |
| C | 153.3 | 35 | 17.5 | 170.8 |
可以看到:
- A 从 153.3 降到 133.3,更接近真实值 100。
- C 从 153.3 升到 170.8,更接近真实值 200。
- B 被修正得有点过头,这也没关系,下一轮还会继续修正。
接着计算新的残差:
$$
r_i^{(2)}=y_i-F_1(x_i)
$$
| 房子 | 真实价格 | 当前预测 $F_1$ | 新残差 |
|---|---|---|---|
| A | 100 | 133.3 | -33.3 |
| B | 160 | 170.8 | -10.8 |
| C | 200 | 170.8 | 29.2 |
第二棵树继续学习这些新残差。
所以 GBDT 的训练过程可以理解为:
先给一个粗略预测
-> 计算当前还差多少
-> 训练一棵树去拟合这个差距
-> 用学习率控制修正幅度
-> 更新预测
-> 再计算新的差距
-> 继续训练下一棵树
4. GBDT 的加法模型形式
GBDT 最终得到的不是一棵树,而是很多棵树的累加:
$$
F_T(x)=F_0(x)+\eta\sum_{t=1}^{T}h_t(x)
$$
其中:
- $F_0(x)$ 是初始预测。
- $h_t(x)$ 是第 $t$ 棵树。
- $T$ 是树的数量。
- $\eta$ 是学习率。
如果每棵树都输出一个修正值,那么最终预测就是:
初始预测 + 第 1 棵树的修正 + 第 2 棵树的修正 + ... + 第 T 棵树的修正
例如某套房子的预测过程是:
初始预测:153.3
第 1 棵树修正:-20.0
第 2 棵树修正:-12.5
第 3 棵树修正:-6.0
最终预测为:
$$
\hat{y}=153.3-20.0-12.5-6.0=114.8
$$
这就是 GBDT 的加法模型思想。
5. 为什么叫“梯度”提升
前面用平方误差时,我们一直说“拟合残差”。但 GBDT 不只适用于平方误差。
更一般地说,GBDT 每一轮拟合的是损失函数的负梯度。
假设训练目标是最小化:
$$
\sum_{i=1}^{n}L(y_i,F(x_i))
$$
第 $t$ 轮时,当前模型是 $F_{t-1}(x)$。对每个样本,计算负梯度:
$$
r_i^{(t)}=
-\left[
\frac{\partial L(y_i,F(x_i))}{\partial F(x_i)}
\right]{F=F{t-1}}
$$
然后训练一棵新的回归树 $h_t(x)$,让它拟合这些 $r_i^{(t)}$。
这里的 $r_i^{(t)}$ 可以理解为:
当前模型在第 i 个样本上,最应该往哪个方向修正
所以 GBDT 的“梯度”不是说树本身能求导,而是说模型每一轮沿着损失函数下降的方向加一棵树。
6. 平方误差下:负梯度就是残差
回归任务中,常用平方误差:
$$
L(y,F(x))=\frac{1}{2}(y-F(x))^2
$$
对 $F(x)$ 求导:
$$
\frac{\partial L}{\partial F(x)}=F(x)-y
$$
负梯度为:
$$
-\frac{\partial L}{\partial F(x)}=y-F(x)
$$
而 $y-F(x)$ 正好就是残差。
所以在平方误差下:
拟合负梯度 = 拟合残差
这也是为什么很多入门材料会先用“拟合残差”解释 GBDT。这个说法没错,但它只是平方误差下的特殊情况。
7. 一棵 GBDT 树的叶子输出什么
在 GBDT 中,每一轮训练的是一棵回归树。它会把样本分到不同叶子节点,然后每个叶子输出一个修正值。
假设某一轮中有 5 个样本的残差:
| 样本 | 残差 |
|---|---|
| A | -50 |
| B | -40 |
| C | -10 |
| D | 30 |
| E | 45 |
一棵回归树可能把它们分成两个叶子:
叶子 1:A、B、C
叶子 2:D、E
对于平方误差,叶子输出通常取该叶子中残差的平均值。
叶子 1:
$$
\gamma_1=\frac{-50-40-10}{3}=-33.3
$$
叶子 2:
$$
\gamma_2=\frac{30+45}{2}=37.5
$$
如果学习率 $\eta=0.1$,那么落到叶子 1 的样本会被修正:
$$
0.1\times(-33.3)=-3.33
$$
落到叶子 2 的样本会被修正:
$$
0.1\times37.5=3.75
$$
这说明 GBDT 的每棵树不是给出最终答案,而是给出一小步修正。
8. 分类任务中的 GBDT
GBDT 不只能做回归,也能做分类。
二分类中,模型通常不直接输出类别,而是输出一个分数 $F(x)$,再通过 Sigmoid 函数转成概率:
$$
p(x)=\frac{1}{1+e^{-F(x)}}
$$
如果 $p(x)\ge 0.5$,预测为正类;否则预测为负类。
常用损失函数是对数损失:
$$
L(y,F(x))=-[y\log p(x)+(1-y)\log(1-p(x))]
$$
其中 $y\in{0,1}$。
对这个损失求负梯度,可以得到:
$$
r_i = y_i - p_i
$$
这也很直观:
- 如果真实 $y=1$,但模型预测概率 $p=0.2$,则 $r=0.8$,说明要大幅提高正类分数。
- 如果真实 $y=0$,但模型预测概率 $p=0.9$,则 $r=-0.9$,说明要大幅降低正类分数。
举个例子:
| 学生 | 是否通过 $y$ | 当前预测概率 $p$ | 负梯度 $r=y-p$ |
|---|---|---|---|
| A | 1 | 0.80 | 0.20 |
| B | 1 | 0.30 | 0.70 |
| C | 0 | 0.60 | -0.60 |
| D | 0 | 0.10 | -0.10 |
下一棵树会重点修正 B 和 C,因为它们的负梯度绝对值更大,说明当前模型错得更明显。
9. 学习率和树数量
GBDT 的更新公式通常写成:
$$
F_t(x)=F_{t-1}(x)+\eta h_t(x)
$$
其中 $\eta$ 是学习率。
学习率越小,每棵树的修正幅度越小。这样训练更稳,但通常需要更多树。
例如同样一棵树输出修正值 $h_t(x)=40$:
- 如果 $\eta=1$,本轮直接加 40。
- 如果 $\eta=0.1$,本轮只加 4。
- 如果 $\eta=0.01$,本轮只加 0.4。
所以常见经验是:
小学习率 + 多棵树
例如:
learning_rate = 0.05
n_estimators = 300
通常比:
learning_rate = 0.5
n_estimators = 30
更稳,但训练成本也更高。
10. GBDT 的重要参数
使用 GBDT 时,常见参数包括:
| 参数 | 含义 | 影响 |
|---|---|---|
n_estimators |
树的数量 | 太少容易欠拟合,太多可能过拟合 |
learning_rate |
学习率 | 控制每棵树的修正幅度 |
max_depth |
单棵树最大深度 | 控制每棵树复杂度 |
min_samples_leaf |
叶子节点最少样本数 | 防止叶子过小 |
subsample |
每轮采样比例 | 小于 1 时类似随机梯度提升,可降低过拟合 |
loss |
损失函数 | 决定负梯度如何计算 |
调参时可以先抓住三个核心:
learning_raten_estimatorsmax_depth
树越深,单棵树越强,也越容易过拟合。GBDT 中的树通常不需要太深,很多任务中深度 3 到 6 已经能工作得不错。
11. sklearn 中训练 GBDT 回归模型
下面用一个简单的回归例子训练 GBDT。
from sklearn.datasets import make_regression
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
# 构造一个回归数据集
X, y = make_regression(
n_samples=300,
n_features=6,
noise=15,
random_state=42,
)
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
)
model = GradientBoostingRegressor(
n_estimators=200,
learning_rate=0.05,
max_depth=3,
random_state=42,
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("MSE:", mean_squared_error(y_test, y_pred))
这段代码对应前面的公式:
先初始化预测
-> 每一轮训练一棵回归树
-> 新树拟合当前负梯度
-> 更新整体模型
实际项目中,GBDT 很常用于房价预测、销量预测、风险评分、用户价值预测等表格数据回归任务。
12. sklearn 中训练 GBDT 分类模型
下面用鸢尾花数据集训练一个分类模型。
from sklearn.datasets import load_iris
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
iris = load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y,
)
model = GradientBoostingClassifier(
n_estimators=100,
learning_rate=0.05,
max_depth=3,
random_state=42,
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred, target_names=iris.target_names))
分类任务中,GBDT 常用于:
- 用户是否会流失。
- 是否为欺诈交易。
- 是否点击广告。
- 是否通过考试。
- 商品是否会被购买。
如果类别不平衡,不建议只看准确率,可以结合精确率、召回率、F1、AUC 等指标。
13. 为什么会出现 GBDT 的变体
原始 GBDT 思想很清楚,但真实工程中会遇到一些问题:
- 数据量大时,训练速度慢。
- 特征很多时,寻找最佳分裂很耗时。
- 容易过拟合,需要更好的正则化。
- 类别特征处理麻烦。
- 缺失值、稀疏特征、大规模数据需要更好的工程实现。
于是出现了很多 GBDT 变体。
14. XGBoost:加入二阶梯度和正则化
XGBoost 可以看成是 GBDT 的一个强工程增强版。
它的目标函数通常写成:
$$
Obj=\sum_{i=1}^{n}L(y_i,\hat{y}i)+\sum{t=1}^{T}\Omega(f_t)
$$
前半部分是训练损失,后半部分是树的复杂度惩罚。
常见正则项为:
$$
\Omega(f)=\gamma K+\frac{1}{2}\lambda\sum_{j=1}^{K}w_j^2
$$
其中:
- $K$ 是叶子节点数量。
- $w_j$ 是第 $j$ 个叶子的输出值。
- $\gamma$ 惩罚叶子数量,防止树长得太复杂。
- $\lambda$ 惩罚叶子权重,防止叶子输出过大。
XGBoost 的一个重要特点是使用二阶泰勒展开。对第 $t$ 轮新增树 $f_t$,目标函数近似为:
$$
Obj^{(t)}\approx
\sum_{i=1}^{n}
\left[
g_i f_t(x_i)+\frac{1}{2}h_i f_t^2(x_i)
\right]
+\Omega(f_t)
$$
其中:
$$
g_i=\frac{\partial L(y_i,\hat{y}_i)}{\partial \hat{y}_i}
$$
$$
h_i=\frac{\partial^2 L(y_i,\hat{y}_i)}{\partial \hat{y}_i^2}
$$
也就是说,XGBoost 不只看一阶梯度,还看二阶梯度,能更精细地估计下一步怎么修正。
14.1 XGBoost 的实际应用
假设做信用风险预测,要判断一个用户是否可能违约。特征包括:
年龄、收入、负债率、历史逾期次数、消费行为、还款记录
XGBoost 会不断加树,每一棵树都修正当前模型对违约概率的判断。同时正则项会限制树不要太复杂,降低过拟合风险。
因此它常用于:
- 风控评分。
- 广告点击率预估。
- 用户流失预测。
- 比赛类表格数据任务。
15. LightGBM:更快的大规模 GBDT
LightGBM 的重点是快,尤其适合大规模表格数据。
它有几个典型思想:
15.1 直方图算法
传统 GBDT 找分裂点时,可能会枚举很多连续取值。LightGBM 会先把连续特征分桶。
例如“年龄”原本有很多取值:
18, 19, 20, 21, ..., 60
可以分成几个桶:
18-25, 26-35, 36-45, 46-60
这样找分裂点时,不用遍历每个具体数值,而是在桶上搜索,速度会快很多。
15.2 Leaf-wise 生长
普通树常见做法是 level-wise,也就是一层一层长。
LightGBM 使用 leaf-wise:每次优先选择能带来最大损失下降的叶子继续分裂。
直观理解:
哪里错得最严重,就优先细分哪里
这样通常能更快降低损失,但也更容易过拟合,所以常配合 num_leaves、max_depth 控制复杂度。
15.3 GOSS 和 EFB
LightGBM 还引入了 GOSS 和 EFB:
- GOSS:保留梯度大的样本,随机采样梯度小的样本。
- EFB:把互斥的稀疏特征合并,减少特征数量。
梯度大的样本通常表示当前模型还没学好,保留它们可以更高效地训练。
LightGBM 常用于:
- 大规模点击率预估。
- 推荐系统排序。
- 金融风控。
- 海量表格数据建模。
16. CatBoost:更擅长处理类别特征
很多表格数据里有大量类别特征,例如:
城市、职业、商品类别、商家 ID、用户等级
普通 GBDT 往往需要先手动做 One-Hot 编码、目标编码等处理。CatBoost 的优势之一就是对类别特征更友好。
16.1 类别特征的目标统计
例如有一个特征“城市”,标签是“是否购买”。
可以计算每个城市的购买率:
| 城市 | 购买率 |
|---|---|
| 北京 | 0.72 |
| 上海 | 0.68 |
| 深圳 | 0.75 |
然后用这个统计值代替原始类别。
但这里有一个风险:如果直接用全量数据计算购买率,某个样本自己的标签会参与编码自己,产生目标泄漏。
CatBoost 使用 Ordered Target Statistics 来降低这种泄漏风险。直观理解是:
给当前样本做类别编码时,只使用它之前样本的标签统计,不提前偷看当前样本标签。
16.2 Ordered Boosting
CatBoost 还使用 Ordered Boosting 来减少预测偏移。它通过有序训练方式,让模型在训练时尽量模拟“面对新样本”的状态。
CatBoost 常用于:
- 类别特征很多的业务数据。
- 电商、广告、推荐系统。
- 不想手动做大量类别特征编码的任务。
17. GBDT、XGBoost、LightGBM、CatBoost 对比
| 模型 | 核心特点 | 适合场景 |
|---|---|---|
| GBDT | 原始梯度提升树,思想清晰 | 学习原理、中小规模任务 |
| XGBoost | 二阶梯度、正则化、缺失值处理强 | 通用表格数据、比赛、风控 |
| LightGBM | 训练快、内存低、适合大数据 | 大规模数据、推荐、CTR |
| CatBoost | 类别特征处理友好 | 类别变量多的业务数据 |
简单选择建议:
- 学原理:先学 GBDT。
- 做通用表格任务:优先试 XGBoost 或 LightGBM。
- 数据量很大:优先试 LightGBM。
- 类别特征很多:优先试 CatBoost。
18. GBDT 的优缺点
优点
- 对表格数据效果很强。
- 可以处理非线性关系。
- 不太依赖特征缩放。
- 能做回归,也能做分类。
- 通过逐步修正错误,模型表达能力强。
缺点
- 训练通常比单棵树慢。
- 参数较多,需要调参。
- 对噪声和异常值可能比较敏感。
- 树很多时,可解释性下降。
- 原始 GBDT 对高维稀疏特征不一定最高效。
19. 实践建议
使用 GBDT 类模型时,可以注意:
- 先划分训练集、验证集、测试集,避免数据泄漏。
- 用较小学习率配合较多树,例如
learning_rate=0.05。 - 控制树深度,不要让单棵树太复杂。
- 使用早停,验证集效果不再提升时停止训练。
- 类别不平衡时,不要只看准确率。
- 对异常值和脏数据要谨慎,Boosting 容易关注难样本。
如果使用 XGBoost、LightGBM、CatBoost,一般都可以设置 early stopping:
如果验证集指标连续多轮没有提升,就停止加树
这能减少过拟合,也能节省训练时间。
20. 总结
GBDT 的核心可以概括为三句话:
- 当前模型先给出一个预测。
- 根据损失函数计算每个样本应该如何修正。
- 训练一棵新树去拟合这个修正方向,并把它加到整体模型里。
在平方误差下,这个修正方向就是残差;在一般损失函数下,这个修正方向是负梯度。
理解 GBDT 时,可以抓住这条主线:
初始预测 -> 计算负梯度 -> 训练回归树 -> 更新模型 -> 重复
XGBoost、LightGBM、CatBoost 都是在这条主线上做工程增强:XGBoost 加入更强的正则化和二阶信息,LightGBM 追求大规模训练效率,CatBoost 更擅长处理类别特征。
掌握 GBDT 之后,再看这些变体,就不会觉得它们是完全陌生的新算法,而是同一个思想在不同工程场景下的改进版本。
