1204.机器学习-集成学习-2.Boosting-5.LightGBM

训练数据到了千万行:每轮提升还要反复扫全表找分裂,内存和对钟都吃紧。GBDT 思想没错,但「精确预排序每个特征」在工业规模上贵。

轻量梯度提升机(Light Gradient Boosting Machine,LightGBM)仍是梯度提升树,重点改怎么找分裂、怎么长树、怎么少看一些样本/特征:直方图分箱、按叶子生长、单边梯度采样(GOSS)、互斥特征捆绑(EFB)等,让 GBDT 在大数据上更快更省内存。

段末注释:LightGBM 由微软团队提出(NeurIPS 2017);与 XGBoost、CatBoost 同属 GBDT 实现族。本系列按发展顺序排在 CatBoost(同年稍早公开材料)之后。后文沿用 LightGBM。

配图目录:./1204.机器学习-集成学习-2.Boosting-5.LightGBM/


1. 一句话定位

维度 一句话
学习范式 监督学习;GBDT 族实现
输入 → 输出 表格特征(可直接声明类别列)→ 多棵树分数之和
在优化什么 与 GBDT 相同的加法提升;用近似与采样把每轮分裂算得起

出现背景:Ke 等,LightGBM: A Highly Efficient Gradient Boosting Decision Tree(NeurIPS 2017)。当时预排序式 GBDT(如早期 XGBoost 路径)在海量行上内存与扫描成本高;LightGBM 用直方图、Leaf-wise、GOSS/EFB 等把每轮分裂算得起,面向工业级数据规模。

比喻:不在连续轴上毫米级找刀口,而先把特征值倒进若干桶(bin)里再比较桶边界;长树时也不强迫「同一层所有叶子一起分裂」,而是专挑当前增益最大的那片叶子再长一截。


2. 直觉:两处关键加速

图 1 直方图算法示意

图 2 Level-wise 整层长 vs Leaf-wise 选最大增益叶

机制 在干什么
Histogram 特征离散进 bin,在直方图上累积梯度统计再找分裂
Leaf-wise 每次分裂增益最大的叶(需 max_depth/num_leaves 防过深)
GOSS 保留大梯度样本,小梯度样本下采样并补偿,少算仍近似增益
EFB 把近似互斥的稀疏特征捆成更少「特征包」,降有效维数

直方图做差:兄弟叶直方图可用「父 − 兄」得到,再省一半构造开销(见同目录 Histogram_subtraction.png)。


3. 核心链路

提升主链路与 GBDT/XGBoost 同构:每轮对每个样本算当前损失的负梯度(及海森),再训一棵树 $f_m$,累加 $\nu f_m$。
LightGBM 的差异几乎全在树怎么造得起、怎么长、少看哪些行/列:bin / 直方图加速找分裂,Leaf-wise 决定先长哪片叶,GOSS 少看样本,EFB 少扫特征。

3.1 Bin:把连续特征先收成有限档,通过Histogram做差统计找分裂点

目的:候选切点从「该特征几乎每个不同取值」降到「最多约 max_bin 个边界」,主要针对千万级数据量大训练中,把特征值通过分组,压缩到 bin 个不同分组中,减少训练阶段特征选择的计算量。

bin 的分组只在最开始的初始化阶段进行,后续训练阶段 bin 的分组范围固定不再调整:

  1. 按特征取值分布(可对 bin_construct_sample_cnt 采样)划出分箱边界,使各 bin 样本量大致均匀;上限由 max_bin(默认常 255)等参数控颗粒度。
  2. 每条样本在该特征上得到一个 bin id(离散档),原浮点值可不再反复参与切点枚举。
  3. bin 只用于每轮找特征切点;损失与梯度仍按样本算

读参数:max_bin / max_bin_by_feature / min_data_in_bin;改分箱通常需重建 Dataset

按特征分组的 bin,统计累加落入该 bin 的 $\sum g$、$\sum h$、count,再只在 bin 边界上算增益、选最优切。兄弟叶直方图可用「父 − 兄」做差,少造一半直方图。损失本身仍是样本维;直方图是把已算好的逐样本梯度按 bin 加总来加速选刀。

3.2 Leaf-wise:每次只分裂「当前增益最大」的叶

对照图 2:常见 Level-wise(按层) 同一深度的叶子尽量一起长,树较整齐;LightGBM 默认 Leaf-wise(按叶 / best-first)

目的

  • 叶子数相近时,优先把分裂预算花在「减损最多」的那片叶上,同 #leaf 下往往比按层生长降损失更快。
  • 代价:树易长成深浅不一;小数据上更容易过拟合,必须用叶子数/深度等卡住。

原理

设当前已有若干叶子,每片叶都能在直方图上算出「若在此叶再切一刀」的最佳增益 $\Delta$。
Level-wise:本层所有叶都切(或按层推进),不管某叶增益是否很小。
Leaf-wise:在所有候选叶里取

$$
\ell^*=\arg\max_{\ell},\Delta(\ell)
$$

只分裂 $\ell^*$,其余叶先不动;新长出的两片子叶再进入候选池,重复直到触达停止条件。
因此分裂次数相同时,预算集中在高增益区域;树可以一边很深、另一边很浅。

实现方式(训练循环内,每棵树)

  1. 根节点入候选;用直方图算其最佳分裂与增益。
  2. 当叶子数 $<$ num_leaves,且仍有增益足够的候选:弹出增益最大的叶 → 执行分裂 → 两子叶各自估增益后入堆/列表。
  3. 若某叶达到 max_depth、或样本数 / 海森和低于 min_data_in_leaf(及同类约束)、或增益不足,则不再分裂该叶。
  4. 注意:设了 max_depth 仍是 Leaf-wise——只是过深的枝被禁止再长,并不是改回 Level-wise。

复杂度旋钮(实务)

参数 角色
num_leaves 主控一棵树最多多少叶;比单独调深度更贴 Leaf-wise
max_depth 深度上限,防单枝过深;宜与 num_leaves 同设,且常令 num_leaves $< 2^{\texttt{max_depth}}$
min_data_in_leaf 叶太碎则停,抑过拟合

3.3 GOSS:梯度单边采样,少算样本仍近似增益

GOSS(Gradient-based One-Side Sampling,基于梯度的单边采样)。

目的

造直方图、估分裂增益时,不必每轮扫完全部行。大梯度样本对增益贡献大,整留;小梯度样本贡献小,随机抽一部分并放大权重,使 $\sum g$、$\sum h$ 仍接近全量估计,从而加速。

原理

记本轮各样本梯度强度从大到小。设保留大梯度比例为 $a$(top_rate),从小梯度池再随机保留比例为 $b$(other_rate,相对全样本量 $n$):

  1. 取梯度最大的约 $a\cdot n$ 条,全部进入本轮训练子集。
  2. 其余小梯度样本中,再随机抽约 $b\cdot n$ 条。
  3. 对抽中的小梯度样本,将其梯度/海森乘放大系数

$$
\frac{1-a}{b}
$$

(实现里按整数截断后的实际条数微调),避免「少抽了弱样本」导致分布偏斜。大梯度侧不放大。直方图统计是加权子集上的近似无偏估计,不是朴素丢掉弱样本。

直觉:改卷优先盯大错;小错抽查几份并按比例加权,总分仍能代表全班。

实现方式

  1. 开启:boosting_type='goss'(默认 gbdt 时不走 GOSS)。
  2. 每轮提升(常在若干预热轮之后)按当前梯度重采样;与 Dataset 分箱采样、与普通 bagging 不是同一套开关。
  3. 在采样子集上建直方图、Leaf-wise 长树;未抽中样本本轮不进直方图,模型仍是全局加法模型。
  4. 主旋钮:top_rate(常默认 $0.2$ 量级)、other_rate(常默认 $0.1$ 量级)。$a$ 过大加速有限;$b$ 过小估计更噪。

注意:GOSS 改的是「本轮用哪些行估分裂」,不是改标签语义;小数据上噪声可能盖过收益,未必默认就开。

3.4 EFB:互斥特征捆绑,少扫特征维

EFB(Exclusive Feature Bundling,互斥特征捆绑)。

目的

高维稀疏表(大量 one-hot、文本词袋等)上,很多特征几乎不同时非零。把近似互斥的若干列捆成一个特征包,直方图要扫的有效特征数从 $d$ 降到约「捆数」,加速且省内存。

原理

  • 互斥:同一行上至多一个特征取非零。捆在一起时,对参与列加不同 offset,把取值映到不重叠数值段,合成一列仍能区分「来自哪条原特征、取何值」。
  • 近似互斥:允许少量同时非零(冲突)。冲突率小则对增益估计影响有界;捆得越狠越快,冲突过大则信息搅在一起。
  • 求最少捆数可化为图着色近似:特征为点,非互斥(常同时非零)则连边;同色点可尝试捆成一包(贪心着色)。

实现方式

  1. 多在 Dataset 构造阶段做捆绑规划与编码(与 bin 边界一样,偏预处理进训练格式);训练循环里对更少的捆绑特征建直方图。
  2. 捆绑列内部:加 offset 错开取值区间,再写入单一 bundled 特征。
  3. 开关:enable_bundle(常默认开启)。论文冲突上限在当前实现中多为内部启发式(历史上曾暴露 max_conflict_rate,现一般不当常规调参);稠密数值表收益有限,稀疏高维表收益大。
  4. 与 GOSS 正交:GOSS 减,EFB 减;可同时作用在直方图路径上。

抓住一句:Bin/直方图定刀缝;Leaf-wise 定砍哪片叶;GOSS 定本轮看哪些行;EFB 定直方图要扫哪些(捆后的)列。 损失与逐样本梯度语义不变,变的是估计增益时的计算图。

读参数顺序建议:learning_rate + num_iterations(早停)→ num_leaves(及必要时 max_depth)→ 大数据再考虑 boosting_type=gosstop_rate/other_rate → 稀疏高维确认 enable_bundle;类别列用 categorical_feature 声明。


4. 手算完整实例:直方图分箱 + 2 轮叶均值

A. 问题与原始表

一维特征 $x$,回归标签 $y$(示意)。

$i$ $x$ $y$
1 1.0 1
2 1.2 2
3 3.5 8
4 3.8 9
5 2.0 3

B. 初始化

把 $x$ 分进 2 个 bin:$x<2.5$ → bin0,$x\ge2.5$ → bin1(直方图候选分裂只比较 bin 边界)。
$F_0=\bar y=4.6$;$\nu=1$。每轮在 bin 上累计残差和与计数,叶值=该 bin 残差均值。

C. 训练过程

第 1 轮:$r=y-4.6$。

bin 样本 $\sum r$ 计数 叶值 $f_1$
0 1,2,5 $-7.8$ 3 $-2.6$
1 3,4 $+7.8$ 2 $+3.9$

$F_1=F_0+f_1$ ⇒ bin0 上 $F_1=2.0$,bin1 上 $F_1=8.5$。

第 2 轮:$r=y-F_1$。

$i$ $F_1$ $r$
1 2.0 −1.0
2 2.0 0.0
5 2.0 +1.0
3 8.5 −0.5
4 8.5 +0.5
bin $\sum r$ 叶值 $f_2$
0 0.0 0.0
1 0.0 0.0

本例两轮后残差在 bin 内已对称抹平;$F_2=F_1$(示意「直方图上累加统计 → 更新叶」;实装会换分裂、加多 bin / Leaf-wise)。

D. 可部署对象

分箱规则 + $F_2(x)=4.6+f_1(\mathrm{bin}(x))+f_2(\mathrm{bin}(x))$。

E. 预测 / 推断

  • 训练内($i=1$,$x=1.0$):bin0 → $F_2=2.0$。
  • 新样本 $x=3.0$:bin1 → $F_2=8.5$。

5. 适用 / 不适用

维度 判定 要求或边界 具体例子
特征 适用 大宽表;稀疏/互斥类别多;可声明 categorical 推荐日志:海量 id 类特征 + 数值上下文,用 EFB/类别分裂
特征 不适用 极小表还强上复杂采样;或必须精确到每个浮点分裂点 500 行实验设计数据,精确 CART 或线性模型更简单
训练目标 适用 回归/分类/排序等 GBDT 常见任务 搜索排序、CTR、销量预测
训练目标 不适用 只要极致可解释的单棵浅树 业务只要三问决策树海报,不必上 LightGBM
训练数据 适用 行数大、要训练快、内存紧;可分布式 ent级样本离线训练日更模型
训练数据 不适用 num_leaves 很大且无早停;噪声点多还 Leaf-wise 拉满 错标不少时过深 leaf-wise 把噪声叶子切得很细

6. 优缺点与常见坑

优点:大数据上通常更快更省内存;原生类别支持;并行方案成熟。
缺点:Leaf-wise 更需防过拟合;直方图是近似;小数据上相对 XGBoost 优势不明显。

:把 num_leaves 开到 $2^{\mathrm{max_depth}}$ 量级却以为「和深度等价」;类别高基数无约束;对比 XGBoost 时未对齐早停与学习率。


7. 最小可运行示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"""LightGBM 二分类:Dataset → train → predict。"""
import lightgbm as lgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)

train_set = lgb.Dataset(X_train, label=y_train)
valid_set = lgb.Dataset(X_test, label=y_test, reference=train_set)

params = {
"objective": "binary",
"metric": "auc",
"learning_rate": 0.05,
"num_leaves": 31,
"verbosity": -1,
"seed": 42,
}
model = lgb.train(
params,
train_set,
num_boost_round=300,
valid_sets=[valid_set],
callbacks=[lgb.early_stopping(30), lgb.log_evaluation(0)],
)
pred = model.predict(X_test, num_iteration=model.best_iteration)
print("AUC:", roc_auc_score(y_test, pred))

说明:公开数据集示例,与上文直方图手算表无关。

重要配置参数(LightGBM)

参数(库内常用名) 训练中的作用与影响 参考起点 / 常用范围 配置指导
num_leaves 叶数上限(Leaf-wise 主复杂度旋钮);过大极易过拟合 31~127;起点可 31 过拟合优先减 num_leaves,不要只减 num_boost_round
learning_rate 步长;小更稳 0.03~0.1;起点可 0.05 配早停;lr↓ 则放宽最大轮数
max_bin 直方图分箱数;大更细、更慢、更占内存 默认常够;内存紧可略降 极值噪声多时可略降;精度不够再升
min_data_in_leaf 叶最小样本;越大越保守 视样本量;过拟合可增大 小数据尤需防止叶过碎
boosting_type / top_rate / other_rate goss 时大梯度全留、小梯度抽样并加权;减行加速 大数据可试 gosstop_rate≈0.2other_rate≈0.1 与普通 bagging 不同;小数据慎开
feature_fraction / bagging_fraction 列/行随机采样(非 GOSS);抗过拟合、加速 0.7~1.0;bagging 需 bagging_freq>0 goss 勿混为一谈
enable_bundle EFB:近似互斥稀疏特征捆绑,减有效列 常默认开 稠密纯数值收益小;稀疏高维保持开启
早停 early_stopping 验证停滞停训 20~50 预测用 best_iteration,与 XGBoost 同理

8. 和近邻算法怎么挑

需求 更优先考虑
教学:加重错分 AdaBoost
残差提升框架 GBDT
通用数值表强基线 XGBoost
类别多、防目标编码泄漏 CatBoost
行数极大、要更快训练 LightGBM
要整段预测分布 NGBoost

9. 小结

  • LightGBM = GBDT 主链路 + 直方图 / Leaf-wise / GOSS / EFB 等加速。
  • 大数据表格训练的常用首选之一;小数据先对齐验证方式再和 XGBoost 比速度。
  • 最易踩的坑:num_leaves 过大 + 无早停

参考文献

  1. Ke G. et al. LightGBM: A Highly Efficient Gradient Boosting Decision Tree. NeurIPS 2017.
  2. LightGBM 文档
  3. Microsoft LightGBM GitHub
-------------本文结束感谢您的阅读-------------