Skip to content

梯度提升为什么逐轮拟合修正量 ​

上一节已经能训练一棵树。现在的问题是:如果每棵树容量有限,能不能让后面的树修正前面模型还没解决的部分?

本页依赖树的训练与预测。学习目标是解释每轮训练标签从哪里来,并手算两轮预测。使用之前的 X=[[1],[2],[3],[4]]、y=[1,1,3,3],没有换题。

1. 把一个模型写成多次修正 ​

最终模型是:

FM(x)=F0(x)+η∑m=1Mfm(x).

F0 是初始常数,fm 是第 m 棵修正树,η 是学习率。样本要经过每一棵树,各自得到一个叶子值,再累加。

这与随机森林的核心训练关系不同:随机森林通过样本和特征随机化训练多棵树并聚合;梯度提升中新一棵树的目标依赖当前整个模型。GBDT 的“顺序”指训练依赖,不表示预测一定只能串行执行。

2. 平方损失下,为什么修正目标是残差 ​

使用单样本损失:

ℓ(y,F)=12(y−F)2.

注意变量是当前预测值 F,不是树的阈值。求导:

gi=∂ℓ(yi,F)∂F|F=Fm−1(xi)=Fm−1(xi)−yi.

沿降低损失的方向移动,就使用负梯度 ri=−gi=yi−Fm−1(xi)。在平方损失下它恰好是通常所说的残差。

如果可以为每个训练样本自由增加 ri,就能一步把它改成答案。但我们需要一个能处理新输入的函数,因此训练一棵树,使 fm(xi) 接近这些修正目标。树只能在每个叶子内输出同一个值,这就对修正函数施加了结构限制。

3. 完整手算两轮 ​

平方损失的最佳初始常数是标签均值,所以 F0=2。为观察多轮变化,固定树深度 1、学习率 η=0.5。

步骤x=1x=2x=3x=4
真值 y1133
初始 F₀2222
第一轮残差 y−F₀-1-111
第一棵树 f₁-1-111
F₁=F₀+0.5f₁1.51.52.52.5
第二轮残差 y−F₁-0.5-0.50.50.5
第二棵树 f₂-0.5-0.50.50.5
F₂=F₁+0.5f₂1.251.252.752.75

两棵树都在 2.5 切分,但它们的叶子值不同。第二轮一定用 F1 计算新残差,不能继续使用最初的残差,也不能只减去上一棵树。

训练 MSE 从 1 变成 0.25,再变成 0.0625。这里报告的是不带 1/2 的 MSE;求导所用损失带 1/2,只为让梯度表达式简洁,两者在本例中的优化方向一致。

运行 python3 examples/python/tree_boosting.py 可以逐行核对表格。把学习率改为 1 时,这个特别简单的数据一轮即可拟合;这不代表实际问题应该总用 1。

4. 一般损失还叫残差吗 ​

对于一般可微损失,修正目标是:

ri(m)=−∂ℓ(yi,F)∂F|F=Fm−1(xi).

例如二分类时,令 F 是原始分数,p=1/(1+e−F) 是概率,y∈{0,1}。对交叉熵求导得到 g=p−y,负梯度是 y−p。新树的输出加在原始分数 F 上,不能把树值直接当作概率相加。

一般梯度提升还可以在分区确定后,为每个叶子按原损失寻找合适的步长:

γjm=arg⁡minγ∑xi∈Rjmℓ(yi,Fm−1(xi)+γ).

本章手写版本限定平方损失,此时残差均值就是叶子最优修正;LightGBM 的常见平滑目标则使用梯度与二阶信息建立近似优化,后面专门推导。有关加法模型和目标函数的完整定义可查 scikit-learn 梯度提升说明。

5. 容量、学习率和轮数共同作用 ​

深树能在一轮学习更复杂的修正,也更容易拟合偶然误差。学习率缩小每棵树的影响,通常需要更多轮才能达到相近训练误差。轮数增加让模型更灵活,但训练误差下降不保证验证误差下降。

三者不能分开理解:只把学习率调小又固定极少轮数,模型可能还没学够;只看训练误差不断加树,可能在追逐噪声。早停是在验证数据上选择轮数,不是保证泛化的数学证明。

小结、自测与下一步 ​

  1. 第二轮的训练目标为什么不是原始 y?
  2. 二分类能否直接执行 probability += tree_prediction?
  3. 本例学习率 0.5、深度 1,第三轮 MSE 应为多少?
参考答案
  1. 新树负责修正当前集成模型,平方损失下目标为 y-F_current。
  2. 不能。应在原始分数空间更新,再用 sigmoid 转成概率。
  3. 残差由 ±0.25 减为 ±0.125,因此 MSE 为 0.015625。这依赖本例每轮都能精确拟合残差的特殊结构。

下一步:从零实现平方损失 GBDT。

最近更新