8.5 LightGBM

文献:LightGBM: A Highly Efficient Gradient Boosting Decision Tree

官方文档

LightGBM的核心目标是通过减少数据量和特征维度来加速训练,同时保持模型精度。其创新点主要体现在GOSS(梯度单边采样)和EFB(互斥特征捆绑)两项技术上。

LightGBM相较于XGBoost,更适合在大数据或高维特征场合使用

8.5.1 原理

  1. Gradient-based One-Side Sampling

传统GBDT需扫描所有数据计算信息增益,计算成本高。而GOSS保留了梯度大的样本,并随机采样梯度小的样本,通过权重补偿修正数据分布偏差。如此,大梯度样本就能够主导信息增益计算,同时这种加权修正也能够近似原始分布。

  1. Exclusive Feature Bundling

在高维特征场合,存在“特征互斥”的现象,即某些特征永远不会同时非零(如独热编码)。鉴于此,将这些互斥的特征捆绑为单一特征,减少特征数量。

  1. 生长策略

LightGBM采用Leaf-wise的树生长策略,每次选择损失下降最大的叶子节点分裂,深度优先。因此,LightGBM能够更快降低损失,生成更复杂的不对称树。

XGBoost采取Level-wise的树生长策略,逐层分裂树,每层分裂所有叶子节点,广度优先。

  1. 直方图算法

LightGBM对连续特征离散化为直方图,降低计算复杂度。

XGBoost既支持预排序特征值,又支持直方图算法

8.5.2 实现

调参技巧:

  1. 控制树模型复杂度
  • num_leaves:叶子节点数量(最重要的参数)

    • 默认值:31

    • 建议范围:31 ~ 255

    • 越大模型越复杂,过拟合风险上升

    • 通常应满足:num_leaves <= 2^(max_depth)

  • max_depth:最大树深度

    • 默认值:-1(不限制)

    • 建议范围:3 ~ 10

    • 限制树深度可显著降低过拟合

  • min_data_in_leaf:叶子节点最小样本数

    • 默认值:20

    • 建议范围:20 ~ 100

    • 增大能平滑模型、提升泛化能力

  • min_sum_hessian_in_leaf:叶子节点最小Hessian和

    • 默认值:1e-3

    • 建议范围:1e-3 ~ 1e-1

    • 数据量大时可适度增大,防止过拟合

  1. 学习率与迭代次数(核心控制)
  • learning_rate

    • 默认值:0.1

    • 建议范围:0.01 ~ 0.1

    • 越小训练越慢但泛化更好

  • n_estimators

    • 默认值:100

    • 建议范围:500 ~ 5000

    • 通常与 learning_rate 组合调节

    • 建议开启早停:early_stopping_rounds = 50~200

  1. 防止过拟合的随机采样
  • feature_fraction(列采样率)

    • 默认值:1.0

    • 建议范围:0.6 ~ 0.9

    • 每棵树随机使用部分特征

  • bagging_fraction(样本采样率)

    • 默认值:1.0

    • 建议范围:0.6 ~ 0.9

    • 每次建树使用部分样本

  • bagging_freq

    • 默认值:0(禁用)

    • 建议值:5

    • 表示每 5 次迭代重新随机采样一次

  1. 正则化参数(控制权重大小)
  • lambda_l1:L1 正则化(稀疏化)

    • 默认值:0

    • 建议范围:0 ~ 10

  • lambda_l2:L2 正则化(平滑化)

    • 默认值:0

    • 建议范围:0 ~ 10

  • min_gain_to_split:节点分裂所需的最小增益

    • 默认值:0

    • 建议范围:0 ~ 0.2

    • 限制无效分裂,提高泛化能力

import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
import logging

# 关闭 LightGBM 日志输出
logging.getLogger("lightgbm").setLevel(logging.ERROR)

# ==============================================
# 1️⃣ 生成模拟数据
# ==============================================
np.random.seed(42)
n_samples = 2000
n_features = 10

# 生成特征矩阵
X = np.random.randn(n_samples, n_features)

# 构造非线性目标变量
y = (
    5 * np.sin(X[:, 0])
    + 3 * X[:, 1] ** 2
    + 2 * X[:, 2]
    + np.random.normal(0, 0.5, n_samples)  # 添加噪声
)

# 转换为DataFrame,带列名
feature_names = [f"f{i}" for i in range(n_features)]
X = pd.DataFrame(X, columns=feature_names)
y = pd.Series(y, name="target")

# ==============================================
# 2️⃣ 划分训练 / 验证 / 测试集
# ==============================================
X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_val, y_train_val, test_size=0.25, random_state=42)
# → 训练:60%,验证:20%,测试:20%

print(f"训练集: {X_train.shape}, 验证集: {X_valid.shape}, 测试集: {X_test.shape}")

# ==============================================
# 3️⃣ 构建 LightGBM 模型(sklearn 风格)
# ==============================================
model = lgb.LGBMRegressor(
    objective="regression",
    metric="rmse",
    learning_rate=0.05,
    n_estimators=2000,
    num_leaves=63,
    max_depth=6,
    min_child_samples=30,
    feature_fraction=0.8,
    bagging_fraction=0.8,
    bagging_freq=5,
    lambda_l2=1.0,
    verbose=-1  # 静默模式
)

# ==============================================
# 4️⃣ 模型训练(含 early stopping)
# ==============================================
model.fit(
    X_train, y_train,
    eval_set=[(X_valid, y_valid)],
    eval_metric="rmse",
    callbacks=[
        lgb.early_stopping(stopping_rounds=100, verbose=False),
        lgb.log_evaluation(period=0)
    ]
)

# ==============================================
# 5️⃣ 模型评估
# ==============================================
y_pred = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"✅ 测试集 RMSE: {rmse:.4f}")

# ==============================================
# 6️⃣ 可视化预测结果
# ==============================================
plt.figure(figsize=(8, 6))
plt.scatter(y_test, y_pred, alpha=0.6)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "r--")
plt.xlabel("True Values")
plt.ylabel("Predicted Values")
plt.title("LightGBM (sklearn-style, DataFrame) - Prediction vs True")
plt.grid(True)
plt.show()

# ==============================================
# 7️⃣ 特征重要性(可选)
# ==============================================
plt.figure(figsize=(8, 5))
lgb.plot_importance(model, max_num_features=10)
plt.title("Feature Importance (LightGBM)")
plt.tight_layout()
plt.show()