2.1 基于梯度的决策树(GBDT)基础回顾 本节摘要:GBDT(梯度提升决策树,Gradient Boosting Decision Tree)是 LightGBM 的母体——LightGBM 没有发明新模型,只是在 GBDT 这套"串行叠加弱学习器"的框架上做了工程与算法优化。本节回到源头,把三件事讲透:决策树如何把数据"分而治之"、梯度提升如何用"负梯度"给每棵树派活、以及"残差拟合"为什么是这一切的关键。读完你会拿到一把尺子,后面衡量 LightGBM 每一项创新时,都知道它省掉的是哪一步、牺牲的又是什么。
本节摘要:GBDT(梯度提升决策树,Gradient Boosting Decision Tree)是 LightGBM 的母体——LightGBM 没有发明新模型,只是在 GBDT 这套"串行叠加弱学习器"的框架上做了工程与算法优化。本节回到源头,把三件事讲透:决策树如何把数据"分而治之"、梯度提升如何用"负梯度"给每棵树派活、以及"残差拟合"为什么是这一切的关键。读完你会拿到一把尺子,后面衡量 LightGBM 每一项创新时,都知道它省掉的是哪一步、牺牲的又是什么。
阅读完本节,你应当能够:
先从一个朴素的观察说起:单棵决策树很好懂,但太"倔"。它把样本一路划分到底,边界生硬,稍微换一批数据,树的形状就可能大变,泛化能力有限。于是人们想,一棵树不够,多来几棵互相补一补,行不行?
这就引出了 Boosting 那句老话——"三个臭皮匠,顶个诸葛亮"。但它有个关键前提:后面的臭皮匠,得知道自己该补前面的哪个洞。随机森林让每棵树各自为政、最后投票,是"并行地猜";Boosting 走的是另一条路,让树一棵接一棵地来,后一棵专门盯着前一棵没做好的地方下功夫。GBDT 就是这条路上的代表。
那后一棵树怎么知道"哪里没做好"?最直接的信号,是真实值和当前预测值之间的差——残差。残差为正,说明还欠着;残差为负,说明补过头了。让下一棵树去拟合这份残差,等于在给上一棵树"擦屁股"。这个想法足够朴素,也足够管用,它的升级版,就是下面要讲的"拟合负梯度"。
顺带说一句,集成学习里还有另一条路叫随机森林,它让树彼此独立、并行训练,最后投票或平均。并行是它的优点,但它靠的是"让树各不相同"来降低方差,没有 GBDT 这种"后一棵纠前一棵的错"的定向修正。两种思路一个求"稳"、一个求"准",GBDT 的代价就是不能并行。
决策树的结构不复杂。一个根节点装着全部样本,内部节点提出一个问题(比如"年龄是否超过三十"),把样本按答案分流到不同子节点,一路分到叶子节点给出预测。分类树在叶子上给类别,回归树在叶子上给一个数值。
这背后是"分而治之":每次分裂,都希望切完之后,同一批子节点里的样本更"纯"——分类看类别是否更一致,回归看目标值是否更集中。衡量纯度的指标,分类常用基尼指数、信息增益,回归常用平方误差的下降幅度。决策树就在每一个内部节点上,从所有特征、所有切点里挑那个能让纯度提升最大的组合。
以分类为例,基尼指数衡量一个节点里类别的混杂程度:节点越纯,基尼指数越小。决策树在候选分裂点上比较"分裂前基尼"与"分裂后左右子节点基尼的加权和",挑下降最大的那个切点。比如某个节点里正负样本各占一半,基尼指数是 0.5;若按某个特征切一刀后,左边九成正样本、右边九成负样本,加权后的基尼能降到 0.1 左右,这个切点就是好切点。回归树同理,只是把"纯度"换成平方误差的下降。
但单棵树的问题也很明显。它把空间切成若干个死板的矩形块,对训练数据里的噪声过分当真,边界又硬,很容易过拟合。这正是需要集成的原因。
GBDT 的核心机制,是把"梯度下降"搬进 Boosting 框架。
我们熟悉的梯度下降,是在参数空间里找损失函数的最小值:先算损失对参数的梯度,再沿梯度的反方向挪一小步。GBDT 的思路如出一辙,只不过它优化的不是一串参数,而是一整个函数。当前模型就是"函数空间"里的一个点,每加一棵树,就是让这个点朝损失更小的方向挪一步。
往哪个方向挪?算当前模型在每个样本上的负梯度。负梯度指向损失下降最快的方向,于是它就是"下一棵树该去拟合的目标"。在平方损失下,负梯度恰好等于残差;换成别的损失函数,负梯度就成了残差的推广——所以文献里常叫它"伪残差"。
想象你站在山顶要下山,四周有雾,看不清整座山,只能凭脚下的坡度判断方向。梯度下降就是每一步都朝"脚下最陡的下坡方向"迈一小步——坡度是梯度,反方向是负梯度。GBDT 把样本点看成坐标、把损失看成高度,每加一棵树,就是让模型这个"点"朝损失更低处挪一小步。差别只在于,普通梯度下降挪的是参数,GBDT 挪的是一整个函数。
把上面的东西拼起来,GBDT 一轮迭代就四个动作:算残差、训练树、定步长、累加。
举个小例子。假设我们要预测三套房子的价格,真实值分别是 80 万、100 万、120 万。第一棵树先统一预测它们的均值 100 万,于是三套房的残差分别是负 20、0、正 20。第二棵树不再预测房价本身,而是去拟合这个残差向量。如果第二棵树把残差拟合得不错,比如输出负 18、正 2、正 16,那么加上学习率 0.5 之后,模型预测就更新成 91 万、101 万、108 万,比原来更接近真实值。第三棵树再接着拟合新的残差……如此循环,残差越来越小,模型越来越准。
反复做 M 轮,最终模型就是初始值加上 M 棵树的加权和。学习率通常取 0.01 到 0.1 之间的小值——步子小一点,每棵树只贡献一点点,多走几轮,反而更稳,泛化也更好。什么时候停?可以预设轮数 M,也可以在验证集误差不再下降时提前收手,也就是早停,第 3 章的早停参数讲的正是这件事。
下面这张图把整条迭代链画了出来:

加法模型加前向分步,决定了 GBDT 每棵树都依赖前一棵的结果。第 m 棵树要拟合的残差,是前 m 减 1 棵树共同算出来的。所以树和树之间没法并行,这是 GBDT 先天的"慢性子"。数据一大、特征一多,这个慢性子就被放大成真正的瓶颈。LightGBM 要做的,不是把串行改成并行(那是另一条路子),而是让"串行里的每一步"都更省。
这里有个容易混淆的点:GBDT 的"串行"指的是树与树之间;而在单棵树内部,找每个特征的分裂点是彼此独立的,这一层是可以并行的。LightGBM 正是抓住树内部的并行空间,再叠加直方图等优化,把单棵树的构建也压到最快。分清"树间串行、树内并行",是理解后续并行优化的一把钥匙。
GBDT 的"残差"长相完全取决于损失函数。下面这张表把常见的几种摆出来:
| 任务 | 损失函数 | 负梯度(残差) | 特点 |
|---|---|---|---|
| 回归 | 平方损失 | 真实值减预测值 | 简单直接,对异常值敏感 |
| 回归 | 绝对损失 | 残差的正负号 | 抗异常值,梯度不连续 |
| 二分类 | 对数损失 | 真实标签与预测概率之差 | 输出可当概率,需归一化 |
| 多分类 | 多类对数损失 | 各类别概率的误差向量 | 每类一棵树或每轮多棵树 |
这张表的价值在于提醒你:残差不是固定公式,而是跟着损失函数走的。同一个问题换一种损失,负梯度的形状就完全不同。这也解释了为什么 GBDT 能同时做回归、二分类、多分类,甚至排序——只要损失函数可导,就能算出负梯度,就能套进同一套加法模型。框架没变,变的只是每一轮"该拟合什么"。
GBDT 容易过拟合,压制手段有三处:限制树深(浅树即可,深度三到五层是常见起点)、调小学习率(同时要加大迭代轮数补偿)、以及减少轮数或用早停。三者互相牵制,第 3 章会专门展开。
有意思的是,GBDT 里的"弱学习器"不是越强越好——单棵树如果太深太强,反而容易把噪声也记住。所以实践中常见的是大量浅树加小学习率的组合,树的深度往往只有三到五层,靠"数量"而非"单棵树的强度"取胜。这一点和很多人"树越深越好"的直觉正好相反。
问题:都说 GBDT 拟合残差,为什么又冒出个负梯度?
回答:两者是"特例与一般"的关系。平方损失求导后,负梯度化简下来恰好等于真实值减预测值,也就是残差;所以对回归里的平方损失,说"拟合残差"和说"拟合负梯度"是一回事。一旦换成对数损失、绝对损失,负梯度就不再是这个简单差值了,但它仍然是"损失下降最快的方向"。工程上为了统一表述,就用"负梯度"这个更一般的说法。
⚠️ 常见坑:看到"GBDT 就是拟合残差"就以为残差永远是"真实值减预测值"。这句话只在平方损失下成立。换成对数损失、绝对损失,拟合的是对应的负梯度。面试和调包都容易在这个点翻车。
💡 关键直觉:把 GBDT 想成"一个会自我纠错的函数"。每一轮它先量一量自己还差多少(算负梯度),再派一棵小树去补这个缺口,补的时候只敢迈一小步(学习率)。慢,但稳。
下一节我们把这些"靶子"逐个拆开——看 LightGBM 用直方图、单边梯度采样、互斥特征捆绑和叶子生长,把 GBDT 那几处又慢又费的地方一一换掉。