跳转到正文

从决策树到 LightGBM:用负荷预测看懂机器如何学习

LightGBM可以先理解成一句话:

它用很多棵回归决策树连续修正预测误差,并通过直方图、Leaf-wise等方法让训练更快。

这句话包含三层:

  1. 决策树:把样本按特征和阈值分组;
  2. GBDT:不断增加新树,修正当前模型的错误;
  3. LightGBM:保留GBDT的学习目标,优化数据扫描、分裂和树生长方式。

LightGBM由微软研究团队提出,论文发表于2017年的NIPS会议。它要解决的不是“GBDT不能预测”,而是GBDT面对大量样本和大量特征时,训练时间长、内存占用高的问题。¹

下面先看完整演进,再拆开每一步。

一组数据贯穿全文

假设我们要预测某区域的小时负荷,已经把原始记录整理成下面的训练表。

时刻温度节假日实际负荷
618°C420 MW
720°C460 MW
1226°C700 MW
1432°C900 MW
1830°C820 MW
2024°C620 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.547,200
温度 ≤ 25°C42,667
节假日:否/是170,400

因此根节点选择“温度 ≤ 25°C”。

这不是算法先认定“温度最重要”,而是它在当前节点、当前样本和当前评分规则下赢得了比赛。

5. 下一层怎么选

根节点分裂后,左右子节点分别拿自己的样本重新比赛:

  1. 重新检查这些样本中的可用特征;
  2. 重新产生候选阈值;
  3. 重新计算误差下降;
  4. 选择当前节点最好的分裂。

同一个特征可以重复出现。例如根节点判断“温度 ≤ 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. 为什么记录误差

第一轮误差为:

实际值当前预测误差:实际−预测
420653.3-233.3
460653.3-193.3
700653.3+46.7
900653.3+246.7
820653.3+166.7
620653.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完整循环

  1. 选择损失函数;
  2. 计算初始预测F₀;
  3. 用全部训练样本计算当前负梯度;
  4. 用一棵回归树拟合负梯度;
  5. 计算各叶子的最优修正值;
  6. 乘学习率后加入模型;
  7. 重新计算全部样本的负梯度;
  8. 增加下一棵树;
  9. 达到停止条件后结束。

每训练一棵新树,通常都需要让训练样本参与梯度统计和候选分裂计算。新增一批训练数据后,标准批量训练通常重新训练模型,而不是简单把新数据接到旧树上。


四、LightGBM:GBDT的高效工程实现

LightGBM继承了GBDT的核心:

  • 多棵回归树组成加法模型;
  • 每轮根据损失函数计算梯度;
  • 新树学习当前模型需要修正的方向;
  • 使用学习率控制每棵树的贡献。

它主要优化的是:

  • 如何表示连续特征;
  • 如何快速统计候选分裂;
  • 下一步扩展哪个叶子;
  • 如何减少样本和特征计算量;
  • 如何直接处理类别和缺失值。

1. Histogram:不逐个尝试所有原始数值

传统精确算法可能按排序后的原始取值检查大量候选阈值。LightGBM先把连续值离散到有限数量的桶中。

例如温度:

text
桶0:18、20
桶1:24
桶2:26
桶3:30、32

之后主要检查桶边界,并在每个桶中累计:

  • 样本数量;
  • 一阶梯度之和;
  • 二阶梯度之和。

因此LightGBM不是把所有可能的实数阈值都试一遍。每个参与训练的特征仍会被检查,但检查的是压缩后的直方图边界。LightGBM还可以用“父节点直方图−一个子节点直方图”得到另一个子节点的直方图,减少重复统计。²

2. 如何决定特征和阈值

在一个待分裂叶子中,LightGBM大致执行:

  1. 为每个候选特征读取直方图;
  2. 从左到右扫描桶边界;
  3. 累计左右两侧的梯度与二阶梯度;
  4. 计算每个边界带来的损失下降;
  5. 选择增益最大的“特征+边界”组合。

一种简化的分裂增益形式是:

text
Gain ≈ 1/2 × [
  G左²/(H左+λ)
  + G右²/(H右+λ)
  - G父²/(H父+λ)
] - γ

其中G是一阶梯度之和,H是二阶梯度之和,λ和γ用于正则化。

3. 叶子修正值

一个叶子的典型最优输出为:

text
叶子值 = -G / (H + λ)

平方误差且忽略正则项时,可以直观理解为叶子中残差的平均值。使用其他损失函数时,梯度和二阶梯度会改变,叶子值也不一定再是简单平均值。

4. Leaf-wise生长

常见的Level-wise生长会把同一层的节点依次展开。LightGBM默认采用Leaf-wise:

  1. 检查当前所有叶子还能获得多少分裂增益;
  2. 选择增益最大的那一个叶子继续分裂;
  3. 其他叶子暂时不动。

这样往往能更快降低训练损失,但树也可能长得不平衡。因此需要通过叶子数、最大深度、叶子最小样本数等参数限制复杂度。

5. GOSS与EFB

LightGBM论文还提出两种优化:

  • GOSS:保留梯度较大的样本,只抽取一部分梯度较小的样本,并修正统计权重;
  • EFB:把很少同时取非零值的稀疏特征合并到同一个特征束中。

它们分别减少需要处理的样本数和有效特征数。它们是可选择的优化策略,不等于每次训练都必须开启。

6. 类别特征和缺失值

LightGBM能够直接处理类别特征,不必总是把一个类别展开成大量one-hot列。类别仍需编码成非负整数,并明确标记为类别字段。

缺失值可以在训练时自动寻找更合适的默认分支方向。但“能处理缺失”不表示可以忽略数据质量:需要区分真实缺失、采集故障、未发生和无意义零值。

7. 如何增加新树、何时停止

每一轮Boosting固定增加一棵新树:

text
当前模型
→ 计算全部样本的梯度
→ 用LightGBM方式训练一棵树
→ 学习率缩放叶子输出
→ 加入当前模型

常见停止条件包括:

  • 达到预设树数;
  • 验证集损失连续多轮不再改善;
  • 分裂增益不足;
  • 叶子数、深度或最小样本数达到限制。

完整流程可以在下面逐步查看。


五、训练完成后如何预测

训练阶段选择好了每棵树的特征、阈值和叶子值。预测新样本时,不再重新训练,也不需要计算真实误差。

假设新样本为:

text
时刻 = 19
温度 = 29°C
节假日 = 否

预测过程是:

  1. 从初始值F₀开始;
  2. 样本沿第一棵树的判断路径到达一个叶子;
  3. 取出第一棵树的叶子修正值并乘学习率;
  4. 对第二棵、第三棵直到最后一棵树重复;
  5. 把所有结果相加。
text
预测负荷 = F₀
         + η×树1叶子值
         + η×树2叶子值
         + …
         + η×树M叶子值

最终预测不是所有树叶子值的均值,而是初始预测与各棵树修正值的加权累加

六、把整条逻辑压缩成三个问题

LightGBM训练时不断重复三个问题:

  1. 当前模型还错在哪里?
  2. 哪个特征和哪个边界最能降低这部分错误?
  3. 怎样用更少的扫描和内存找到这个分裂?

决策树回答第二个问题,GBDT把第一个问题变成逐轮学习,LightGBM重点优化第三个问题。

这就是“决策树 → GBDT → LightGBM”的完整关系。

参考资料

  1. Guolin Ke et al., LightGBM: A Highly Efficient Gradient Boosting Decision Tree, NIPS 2017。
  2. LightGBM官方文档,Features:直方图、Leaf-wise、类别特征等实现特点。
  3. LightGBM官方文档,Advanced Topics:类别特征、缺失值和相关高级主题。
  4. Jerome H. Friedman, Greedy Function Approximation: A Gradient Boosting Machine, The Annals of Statistics, 2001。

内容与代码许可证待项目确认