外观
从决策树到 LightGBM:用负荷预测看懂机器如何学习
LightGBM可以先理解成一句话:
它用很多棵回归决策树连续修正预测误差,并通过直方图、Leaf-wise等方法让训练更快。
这句话包含三层:
- 决策树:把样本按特征和阈值分组;
- GBDT:不断增加新树,修正当前模型的错误;
- LightGBM:保留GBDT的学习目标,优化数据扫描、分裂和树生长方式。
LightGBM由微软研究团队提出,论文发表于2017年的NIPS会议。它要解决的不是“GBDT不能预测”,而是GBDT面对大量样本和大量特征时,训练时间长、内存占用高的问题。¹
下面先看完整演进,再拆开每一步。
一组数据贯穿全文
假设我们要预测某区域的小时负荷,已经把原始记录整理成下面的训练表。
| 时刻 | 温度 | 节假日 | 实际负荷 |
|---|---|---|---|
| 6 | 18°C | 否 | 420 MW |
| 7 | 20°C | 否 | 460 MW |
| 12 | 26°C | 否 | 700 MW |
| 14 | 32°C | 否 | 900 MW |
| 18 | 30°C | 是 | 820 MW |
| 20 | 24°C | 是 | 620 MW |
前三列是特征,最后一列是要学习的目标值。
特征不是算法凭空创造的。它们通常来自:
- 业务系统记录的时间、温度、天气、节假日;
- 对时间做的小时、星期、月份等转换;
- 历史负荷产生的前一小时负荷、前一天同一时刻负荷、移动平均值;
- 设备、价格和计划等外部数据。
LightGBM会自动决定使用哪个特征、在哪个阈值分叉,但数据提供者仍要完成字段含义、时间对齐、标签构造和数据质量检查。
一、回归决策树:把相似负荷放到一起
1. 树的结构
一棵回归决策树由三种部分组成:
- 判断节点:例如“温度 ≤ 25°C吗”;
- 分支:条件成立走左边,否则走右边;
- 叶子:给出一个数值预测。
“回归”表示输出是连续数值,例如420 MW或806.7 MW;如果输出是“高负荷/低负荷”这样的类别,就是分类树。
2. 候选阈值从哪里来
以温度为例,先排序:
18、20、24、26、30、32
相邻值的中点可以成为候选阈值:
19、22、25、28、31
算法不需要尝试任意实数,只需要尝试能改变样本分组的位置。每个可用特征都会产生自己的候选分法。
3. 一个阈值如何评分
尝试“温度 ≤ 25°C”:
- 左边负荷:420、460、620,平均值为500;
- 右边负荷:700、820、900,平均值为806.7。
使用平方误差和SSE评分:
text
左侧SSE = (420-500)² + (460-500)² + (620-500)²
= 22,400
右侧SSE = (700-806.7)² + (820-806.7)² + (900-806.7)²
≈ 20,267
总SSE ≈ 42,667误差越小,说明分组后的负荷越接近各自叶子的预测值。
4. 算法如何选择特征
三个特征各自先选出最好的分法:
| 特征的最佳分法 | 分裂后SSE |
|---|---|
| 时刻 ≤ 9.5 | 47,200 |
| 温度 ≤ 25°C | 42,667 |
| 节假日:否/是 | 170,400 |
因此根节点选择“温度 ≤ 25°C”。
这不是算法先认定“温度最重要”,而是它在当前节点、当前样本和当前评分规则下赢得了比赛。
5. 下一层怎么选
根节点分裂后,左右子节点分别拿自己的样本重新比赛:
- 重新检查这些样本中的可用特征;
- 重新产生候选阈值;
- 重新计算误差下降;
- 选择当前节点最好的分裂。
同一个特征可以重复出现。例如根节点判断“温度 ≤ 25°C”,下一层仍可以判断“温度 ≤ 22°C”或“温度 ≤ 28°C”。每个节点解决的是不同局部样本的分组问题。
点击动画中的“下一步”,可以看到从特征参赛到完整两层树的过程。
6. 叶子预测值是什么
平方误差回归树中,叶子通常输出该叶子训练样本目标值的平均值。
例如叶子中有420和460:
text
叶子预测 = (420 + 460) / 2 = 440 MW树的边没有保存一个需要训练的权重。真正决定结果的是:
- 节点使用的特征;
- 节点使用的阈值;
- 样本最终到达的叶子值。
7. 一棵树的局限
一棵很浅的树容易遗漏细节;一棵很深的树又容易记住训练数据中的偶然波动。
而且决策树通常采用贪心策略:每个节点选择当前最好的分裂,确定后继续向下生长,一般不会频繁回退到根节点重做全局组合搜索。
这就产生了一个新思路:
与其要求一棵树一次做到完美,不如让多棵小树逐轮纠错。
二、Boosting:为什么需要多棵树
Boosting不是某一种固定树结构,而是一种组织弱模型的方法:
先建立一个不完美的模型,再让新模型重点解决当前还没有解决的问题。
如果不用Boosting,可以只训练一棵树,或训练多棵彼此独立的树。但单棵树要同时表达主要规律、局部差异和异常情况,往往只能在“太简单”和“太复杂”之间妥协。
1. 初始预测
平方误差回归中,最合适的初始常数是所有目标值的平均值:
text
F₀ = (420+460+700+900+820+620) / 6
= 653.3 MW一开始,所有样本都预测为653.3 MW。
2. 为什么记录误差
第一轮误差为:
| 实际值 | 当前预测 | 误差:实际−预测 |
|---|---|---|
| 420 | 653.3 | -233.3 |
| 460 | 653.3 | -193.3 |
| 700 | 653.3 | +46.7 |
| 900 | 653.3 | +246.7 |
| 820 | 653.3 | +166.7 |
| 620 | 653.3 | -33.3 |
如果新树再次直接学习绝对负荷,它会重复学习已经掌握的653.3 MW基线。学习误差则是在回答:
当前预测应该向上还是向下修正多少?
3. 第一棵修正树
第一棵树使用原来的时刻、温度、节假日做特征,但把“误差”当成当前学习目标。
若它选择“温度 ≤ 25°C”:
- 左叶子的平均误差:-153.3 MW;
- 右叶子的平均误差:+153.3 MW。
叶子输出的不是绝对负荷,而是修正值。
4. 学习率
如果完全加入修正,模型可能走得太快。学习率η控制每棵树只贡献多少:
text
新预测 = 旧预测 + 学习率 × 树的修正值取η=0.3:
text
低温组:653.3 + 0.3×(-153.3) = 607.3
高温组:653.3 + 0.3×(+153.3) = 699.3通常所有树使用同一个学习率。学习率越小,单棵树迈出的步子越小,往往需要更多树。
5. 第二棵树
第一棵树加入后,重新计算全部样本的误差:
-187.3、-147.3、+0.7、+200.7、+120.7、+12.7
第二棵树面对的是这组新目标,因此特征和阈值的比赛结果可能改变。它不是在第一棵树上继续加节点,而是一棵全新的树。
所以“第一棵树找主要特征、第二棵树找次要特征”并不准确。更准确的说法是:
每棵树都使用当前剩余误差重新选择最有价值的特征和阈值。
6. 最终预测
多棵树组成加法模型:
text
F(x) = F₀ + ηT₁(x) + ηT₂(x) + … + ηTₘ(x)下面的动画从初始平均值开始,展示两棵树如何连续纠错。
三、GBDT:把“纠错”推广到不同损失函数
Boosting说明了“逐轮改进”的思想。GBDT进一步规定:
每一轮用一棵回归树拟合当前损失函数的负梯度方向。
1. 损失函数是什么
损失函数把“预测得有多差”变成一个数。训练的目标就是降低这个数。
负荷回归常见的平方误差为:
text
L(y,F) = 1/2 × (y-F)²它有三个直观特点:
- 预测正确时损失为0;
- 误差越大,惩罚增长越快;
- 导数简单,便于计算下降方向。
但平方误差不是唯一选择。数据中存在强异常值时,也可以使用绝对误差、Huber等损失函数。损失函数体现了业务对不同错误的态度。
2. 什么是负梯度
梯度表示“预测稍微增加时,损失往哪个方向变化”。负梯度表示让损失下降的方向。
平方误差下:
text
-∂L/∂F = y-F因此负梯度刚好等于我们前面使用的残差。两者在这里数值相同,但概念不同:
- 残差是平方误差下的一个具体结果;
- 负梯度是适用于其他可微损失函数的统一训练方向。
3. GBDT完整循环
- 选择损失函数;
- 计算初始预测F₀;
- 用全部训练样本计算当前负梯度;
- 用一棵回归树拟合负梯度;
- 计算各叶子的最优修正值;
- 乘学习率后加入模型;
- 重新计算全部样本的负梯度;
- 增加下一棵树;
- 达到停止条件后结束。
每训练一棵新树,通常都需要让训练样本参与梯度统计和候选分裂计算。新增一批训练数据后,标准批量训练通常重新训练模型,而不是简单把新数据接到旧树上。
四、LightGBM:GBDT的高效工程实现
LightGBM继承了GBDT的核心:
- 多棵回归树组成加法模型;
- 每轮根据损失函数计算梯度;
- 新树学习当前模型需要修正的方向;
- 使用学习率控制每棵树的贡献。
它主要优化的是:
- 如何表示连续特征;
- 如何快速统计候选分裂;
- 下一步扩展哪个叶子;
- 如何减少样本和特征计算量;
- 如何直接处理类别和缺失值。
1. Histogram:不逐个尝试所有原始数值
传统精确算法可能按排序后的原始取值检查大量候选阈值。LightGBM先把连续值离散到有限数量的桶中。
例如温度:
text
桶0:18、20
桶1:24
桶2:26
桶3:30、32之后主要检查桶边界,并在每个桶中累计:
- 样本数量;
- 一阶梯度之和;
- 二阶梯度之和。
因此LightGBM不是把所有可能的实数阈值都试一遍。每个参与训练的特征仍会被检查,但检查的是压缩后的直方图边界。LightGBM还可以用“父节点直方图−一个子节点直方图”得到另一个子节点的直方图,减少重复统计。²
2. 如何决定特征和阈值
在一个待分裂叶子中,LightGBM大致执行:
- 为每个候选特征读取直方图;
- 从左到右扫描桶边界;
- 累计左右两侧的梯度与二阶梯度;
- 计算每个边界带来的损失下降;
- 选择增益最大的“特征+边界”组合。
一种简化的分裂增益形式是:
text
Gain ≈ 1/2 × [
G左²/(H左+λ)
+ G右²/(H右+λ)
- G父²/(H父+λ)
] - γ其中G是一阶梯度之和,H是二阶梯度之和,λ和γ用于正则化。
3. 叶子修正值
一个叶子的典型最优输出为:
text
叶子值 = -G / (H + λ)平方误差且忽略正则项时,可以直观理解为叶子中残差的平均值。使用其他损失函数时,梯度和二阶梯度会改变,叶子值也不一定再是简单平均值。
4. Leaf-wise生长
常见的Level-wise生长会把同一层的节点依次展开。LightGBM默认采用Leaf-wise:
- 检查当前所有叶子还能获得多少分裂增益;
- 选择增益最大的那一个叶子继续分裂;
- 其他叶子暂时不动。
这样往往能更快降低训练损失,但树也可能长得不平衡。因此需要通过叶子数、最大深度、叶子最小样本数等参数限制复杂度。
5. GOSS与EFB
LightGBM论文还提出两种优化:
- GOSS:保留梯度较大的样本,只抽取一部分梯度较小的样本,并修正统计权重;
- EFB:把很少同时取非零值的稀疏特征合并到同一个特征束中。
它们分别减少需要处理的样本数和有效特征数。它们是可选择的优化策略,不等于每次训练都必须开启。
6. 类别特征和缺失值
LightGBM能够直接处理类别特征,不必总是把一个类别展开成大量one-hot列。类别仍需编码成非负整数,并明确标记为类别字段。
缺失值可以在训练时自动寻找更合适的默认分支方向。但“能处理缺失”不表示可以忽略数据质量:需要区分真实缺失、采集故障、未发生和无意义零值。
7. 如何增加新树、何时停止
每一轮Boosting固定增加一棵新树:
text
当前模型
→ 计算全部样本的梯度
→ 用LightGBM方式训练一棵树
→ 学习率缩放叶子输出
→ 加入当前模型常见停止条件包括:
- 达到预设树数;
- 验证集损失连续多轮不再改善;
- 分裂增益不足;
- 叶子数、深度或最小样本数达到限制。
完整流程可以在下面逐步查看。
五、训练完成后如何预测
训练阶段选择好了每棵树的特征、阈值和叶子值。预测新样本时,不再重新训练,也不需要计算真实误差。
假设新样本为:
text
时刻 = 19
温度 = 29°C
节假日 = 否预测过程是:
- 从初始值F₀开始;
- 样本沿第一棵树的判断路径到达一个叶子;
- 取出第一棵树的叶子修正值并乘学习率;
- 对第二棵、第三棵直到最后一棵树重复;
- 把所有结果相加。
text
预测负荷 = F₀
+ η×树1叶子值
+ η×树2叶子值
+ …
+ η×树M叶子值最终预测不是所有树叶子值的均值,而是初始预测与各棵树修正值的加权累加。
六、把整条逻辑压缩成三个问题
LightGBM训练时不断重复三个问题:
- 当前模型还错在哪里?
- 哪个特征和哪个边界最能降低这部分错误?
- 怎样用更少的扫描和内存找到这个分裂?
决策树回答第二个问题,GBDT把第一个问题变成逐轮学习,LightGBM重点优化第三个问题。
这就是“决策树 → GBDT → LightGBM”的完整关系。
参考资料
- Guolin Ke et al., LightGBM: A Highly Efficient Gradient Boosting Decision Tree, NIPS 2017。
- LightGBM官方文档,Features:直方图、Leaf-wise、类别特征等实现特点。
- LightGBM官方文档,Advanced Topics:类别特征、缺失值和相关高级主题。
- Jerome H. Friedman, Greedy Function Approximation: A Gradient Boosting Machine, The Annals of Statistics, 2001。