LightGBM 是一种高效的梯度提升树(GBDT)框架,专为大规模数据与高维稀疏特征设计。它在标准 boosting 框架上,通过**叶子优先的生长策略(leaf-wise growth)、直方图加速(histogram-based split)、基于梯度的一侧采样(GOSS)和互斥特征捆绑(EFB)**四项优化,大幅提升训练速度和模型精度。
四大优化
- 叶子优先生长(leaf-wise):每次选择增益最大的叶子继续切分,以更少的分裂得到更深的树结构,通常带来更好的拟合能力——需配合
max_depth控制复杂度(对比 level-wise 按层生长,leaf-wise 更容易过拟合,需要调参约束)。 - 直方图算法(histogram):将连续特征离散到少量桶中,按桶聚合梯度与二阶信息,避免逐点扫描,大幅减少计算量与内存占用。
- GOSS(Gradient-based One-Side Sampling):保留大梯度样本、对小梯度样本随机抽样并重加权,兼顾计算效率与估计无偏性。
- EFB(Exclusive Feature Bundling):将互斥的稀疏特征打包成单一特征,显著降低维度。
核心原理:二阶泰勒展开
设第 t-1 次迭代前模型为 F_{t-1}(x),本轮要学习一个新的树函数 f_t(x),优化目标:
L_t = Σ L(y_i, F_{t-1}(x_i) + f_t(x_i)) + Ω(f_t)
其中 L 为可微损失,Ω 是树的正则项(如叶子个数、叶子权重的惩罚)。对 L 做二阶泰勒展开,记一阶梯度 g_i 与二阶梯度 h_i:
g_i = ∂L(y_i, F_{t-1}(x_i)) / ∂F
h_i = ∂²L(y_i, F_{t-1}(x_i)) / ∂F²
则近似目标为:
L̃_t = Σ [ g_i · f_t(x_i) + ½ h_i · f_t(x_i)² ] + Ω(f_t)
对于树的某个叶子 j(样本集合 I_j),其最优叶子权重为:
w_j* = -G_j / (H_j + λ)
其中 G_j = Σ g_i、H_j = Σ h_i,λ 为 L2 正则。一个候选分裂的增益可写为:
Gain = ½ [ G_L²/(H_L+λ) + G_R²/(H_R+λ) - (G_L+G_R)²/(H_L+H_R+λ) ] - γ
其中 γ 为叶子惩罚项。当使用最常见的平方损失 L = ½(y - F)² 时,g_i = F_{t-1}(x_i) - y_i,即负梯度就是残差;h_i = 1。这说明每一棵新树都在"拟合上一轮的残差/负梯度",逐步校正误差。
GOSS 与重加权
GOSS 的核心思想:保留所有"大梯度"样本集合 A,并从"小梯度"样本中随机抽样集合 B,对 B 进行重加权以保证估计无偏:
V_j(d) = (1/a) · Σ_{A} g_i + (1-b)/b · Σ_{B} g_i
其中 a 是保留的大梯度比例,b 是小梯度的抽样比例。这样既保留了信息量最大的样本(大梯度 ≈ 拟合得差的样本),又对小梯度样本降采样,兼顾速度与精度。
Python 实现
用正弦曲线加噪声数据,通过 LGBMRegressor 展示不同迭代次数(树数)下预测曲线如何逐步逼近真实函数,以及训练误差随迭代次数的下降:
import numpy as np
import matplotlib.pyplot as plt
from lightgbm import LGBMRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
np.random.seed(0)
# 生成一维回归数据:y = sin(x) + 噪声
X = np.linspace(0, 10, 600).reshape(-1, 1)
y_true = np.sin(X).ravel()
y = y_true + np.random.normal(0, 0.20, size=X.shape[0])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
# 训练 LightGBM 回归模型(scikit-learn 风格接口)
model = LGBMRegressor(
n_estimators=300, # boosting 迭代次数(树的数量)
learning_rate=0.1, # 学习率
num_leaves=31, # 叶子数(控制复杂度)
max_depth=-1, # 不限制深度(可改为正数以限制)
subsample=0.8, # 行采样
colsample_bytree=0.8, # 列采样
reg_lambda=1.0, # L2 正则
random_state=0,
)
model.fit(X_train, y_train)
# 在密集网格上画曲线
X_grid = np.linspace(0, 10, 800).reshape(-1, 1)
y_truth_grid = np.sin(X_grid).ravel()
# 挑选不同迭代次数的预测(体现"逐步逼近")
iters_to_plot = [1, 10, 50, 300]
preds = {n: model.predict(X_grid, num_iteration=n) for n in iters_to_plot}
# 训练集 RMSE 随迭代次数变化
rmse_curve = []
for n in range(1, model.n_estimators + 1):
y_train_pred_n = model.predict(X_train, num_iteration=n)
rmse_curve.append(np.sqrt(mean_squared_error(y_train, y_train_pred_n)))
# 绘图:左图不同迭代的拟合曲线,右图训练 RMSE 下降趋势
plt.figure(figsize=(12, 5))
# 左:数据与预测曲线
ax1 = plt.subplot(1, 2, 1)
ax1.scatter(X_train, y_train, s=12, alpha=0.6, label="训练数据")
ax1.plot(X_grid, y_truth_grid, linewidth=2.5, label="真实函数 sin(x)")
for n in iters_to_plot:
ax1.plot(X_grid, preds[n], linewidth=2, label=f"迭代 {n}")
ax1.set_title("LightGBM 逐步拟合残差:预测曲线逼近真实函数")
ax1.legend(loc="best")
# 右:训练 RMSE vs 迭代次数
ax2 = plt.subplot(1, 2, 2)
ax2.plot(range(1, model.n_estimators + 1), rmse_curve, linewidth=2.5)
ax2.set_title("训练误差随迭代次数下降(Boosting 原理)")
ax2.set_xlabel("迭代次数(树数)")
ax2.set_ylabel("训练 RMSE")
plt.tight_layout()
plt.show()
# 测试集性能
y_test_pred = model.predict(X_test)
rmse_test = np.sqrt(mean_squared_error(y_test, y_test_pred))
print(f"测试集 RMSE: {rmse_test:.4f}")
左图可见,随着迭代次数 1 → 10 → 50 → 300,预测曲线逐步贴近真实的正弦形状,体现"每一棵新树拟合上一轮的残差(负梯度),不断纠正误差"的核心机制;右图显示训练误差随迭代次数单调下降(在适当的学习率与正则化下),这是梯度提升通过二阶近似与最优分裂增益迭代优化目标的直接结果。
分类场景
如果任务是分类(如二分类的对数损失),LightGBM 使用对应的 g_i 与 h_i(来自 logistic 的一阶与二阶导数),过程同理:每一轮用新的树拟合负梯度,并按增益选择最佳分裂。配合 GOSS 和直方图算法,LightGBM 能在大数据上更快地找到近似最优的分裂点与叶子权重。
原文:公众号「Python和机器学习初学者」《导师:“你懂集成算法吗?“我:“不懂,但我用 Codex 跑出来了!“导师:“可以重点理解LightGBM!"》