掌握时序预测day 02 -LightGBM原理介绍
LightGBM(轻量级梯度提升机) 是微软开源的另一个王牌算法。它的底层原理和 XGBoost 完全一样,都是让多棵树接力去学习不正确的部分(残差)。
但微软当年在开发它时,心里憋着一个大招。他们觉得 XGBoost 性能好是好,但在处理海量大数据时,太慢了、太吃内存了。于是,微软对树模型的建树过程进行了大刀阔斧的“偷懒式”改造。
LightGBM 之所以能做到“又快又准”,全靠它的三大核心绝招(而且这三招在后台是同时发力、打组合拳的):
第一招:直方图算法(Histogram)—— 寻找切分点时的高明偷懒
树模型的核心工作是寻找在哪一个数值进行切分最合适(比如在时序特征中:昨天的负荷是否 $> 1500$ 兆瓦?)。
-
XGBoost 的笨办法(预排序):把全城过去几年的所有负荷数据,严格按照从小到大排好序。然后在这个队伍的每一个空隙都切一刀试试,找出最完美的那一刀。数据量只要一上百万,电脑内存直接爆满,跑起来像蜗牛爬。
-
LightGBM 的聪明办法(直方图):它采用了“把数字装进桶里”的思想。比如,把用电量划分成 10 个大桶/垃圾箱(Bins):$0-100$度是 1 号桶,$101-200$度是 2 号桶……
不管你有几百万条还是几千万条数据,它们都会被归类到这 10 个桶里。
通俗点说:LightGBM 在找切分点时,不再去遍历几百万条数据,而是只遍历这 10 个桶! 这一偷懒,让它的运行速度直接飙升了近 10 倍,内存暴省 80%,而且神奇的是,因为数据的大体分布没变,预测精度几乎没有任何下降。
第二招:GOSS 算法 —— 专门淘汰“成绩好的数据”
既然要通过学习残差来纠正错误,那每一棵新树诞生前,都要把所有历史数据算一遍残差。
LightGBM 掐指一算:不对啊,那些残差已经非常小的数据(说明模型已经预测得很准了),为什么每棵树还要重复去算它们?这不是浪费劳动力吗?
于是它发明了 GOSS(基于梯度的单边采样):
-
它把历史数据按照“不正确程度(残差大小)”排个序。
-
残差大的数据(差生):说明模型还没学好,全量保留,让下一棵树重点照顾。
-
残差小的数据(优等生):说明模型已经懂了,它就随机抽一小部分留在队伍里充充场面,剩下的直接“放假”。
通过这种方式,LightGBM 大大减少了每一轮训练要算的数据量,速度进一步起飞。
第三招:Leaf-wise 生长策略 —— 精准打击,绝不齐头并进
这是它在结构上和 XGBoost 最明显的区别。
-
XGBoost(按层生长 Level-wise):像教官训练士兵,所有人必须排成一排,整齐划一地向下分裂。不管某些叶子节点有没有潜力,大家都必须一起长一层。
-
LightGBM(按叶子生长 Leaf-wise):它是绝对的实用主义。它每次分裂时,会在当前所有的叶子里面找:“到底谁的残差最大、最不准?” 然后只盯着这一个节点疯狂向下切分,其他长得好的节点直接不管。
通俗点说:XGBoost 追求“满堂红”,大家都得整齐;LightGBM 追求“定点清除”,谁错得厉害我连夜加班给它纠正。这种按叶子生长的策略,能用更少的树、更深的结构,更快地把残差降到最低,因此精度极高。
⚠️ 注意:盯着一个地方疯狂往下长,容易导致树长得太深,从而“死记硬背”历史(过拟合)。所以实战中我们必须要限高(限制最大深度
max_depth)。
💥 LightGBM 的“三连招”组合拳现场:
-
第一步:数据瘦身(直方图算法 Histogram 启动) 模型刚拿到海量的时序特征数据,直接大刀阔斧地把那些连续的负荷数字、温度数字全部塞进指定的“桶”里。这一步,直接把数据量从几百万条的恐怖级别,精简成了区区几百个桶的计算量,完成了内存暴省。
-
第二步:人员精简(GOSS 算法 启动) 在每一轮准备建新树纠正残差时,模型看了一眼上一轮的结果。它发现 80% 的历史日子预测得很准(残差接近 0),于是给这 80% 的优等生“放假”,只留下那 20% 预测不准的“差生”重点补课。这一步,完成了训练速度的二次起飞。
-
第三步:精准狙击(Leaf-wise 策略 启动) 现在,只剩下一小部分“差生”数据和精简完的“桶”。新树开始生长。它不搞 XGBoost 那种齐头并进的排场,而是盯着最不准的那一个叶子节点,疯狂地、精准地向下连切几刀,用最快的速度把残差清零。这一步,完成了预测精度的高效收敛。
代码示例
import warnings
import lightgbm as lgb
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 忽略不必要的警告信息,让控制台输出更干净
warnings.filterwarnings("ignore")
# ==========================================
# 1. 模拟生成一份真实的电力负荷数据 (以小时为单位)
# ==========================================
np.random.seed(42)
# 生成 300 个小时的时间序列(大约12.5天的数据量)
time_index = pd.date_range(start="2026-05-01", periods=300, freq="H")
# 模拟基础负荷,并叠加每天的周期性用电规律(中午高峰,凌晨低谷)
base_load = 200
simulated_load = []
for i, t in enumerate(time_index):
# 用正弦函数模拟每日用电规律
daily_pattern = 50 * np.sin(2 * np.pi * t.hour / 24)
# 加上随机噪声
noise = np.random.normal(0, 8)
simulated_load.append(base_load + daily_pattern + noise)
# 组装成 Pandas DataFrame,时间作为索引
df = pd.DataFrame(data={"load": simulated_load}, index=time_index)
# ==========================================
# 2. 特征工程:重塑数据,准备好“答卷特征”
# ==========================================
def create_lightgbm_features(data):
"""构建 LightGBM 所需的时间、滞后和滚动统计特征"""
df_feat = data.copy()
# --- 提取时间戳特征 ---
df_feat["hour"] = df_feat.index.hour # 提取小时 (0-23),用于捕捉日周期
df_feat["dayofweek"] = df_feat.index.dayofweek # 提取星期几 (0-6),用于捕捉周工作日规律
# --- 构建滞后特征 (Lag Features) ---
# 假设我们只提前 1 小时预测下一小时,所以最小可以用 lag_1
df_feat["lag_1"] = df_feat["load"].shift(1) # 1小时前的负荷(代表眼前的惯性)
df_feat["lag_2"] = df_feat["load"].shift(2) # 2小时前的负荷
df_feat["lag_24"] = df_feat["load"].shift(24) # 昨天同一时刻的负荷(强力捕捉日周期规律)
# --- 构建滚动统计特征 (Rolling Features) ---
# 基于过去已经发生的数据(lag_1),滚动计算过去 12 小时的平均用电量
df_feat["rolling_mean_12h"] = df_feat["lag_1"].rolling(12).mean()
return df_feat
# 执行特征工程,生成表格特征
df_with_features = create_lightgbm_features(df)
# 因为 shift(24) 会导致前 24 行没有历史数据而产生空值 (NaN),必须无情删掉
df_with_features = df_with_features.dropna()
# ==========================================
# 3. 严格按时间先后划分训练集和测试集
# ==========================================
# 定义丢给树模型去走迷宫的特征列
FEATURES = ["hour", "dayofweek", "lag_1", "lag_2", "lag_24", "rolling_mean_12h"]
# 定义模型要预测的未来标准答案
TARGET = "load"
# 划分数据集:前 250 个小时用于训练,最后 26 个小时用于测试
train_df = df_with_features.iloc[:250]
test_df = df_with_features.iloc[250:]
X_train, y_train = train_df[FEATURES], train_df[TARGET]
X_test, y_test = test_df[FEATURES], test_df[TARGET]
# ==========================================
# 4. 配置 LightGBM 的三大绝招参数
# ==========================================
# 将数据打包成 LightGBM 专用的 Dataset 格式,提升底层运行效率
lgb_train = lgb.Dataset(X_train, y_train)
params = {
"objective": "regression", # 告诉模型这是一个回归任务(预测连续的用电量)
"metric": "rmse", # 使用均方根误差来评估模型纠正残差的效果
"learning_rate": 0.05, # 学习率,控制每棵新树修正残差的步伐大小
# --- 绝招一:GOSS 算法 ---
"boosting_type": "goss", # 开启 GOSS,让模型给优等生放假,重点扣住残差大的“差生”数据
# --- 绝招二:直方图算法 ---
"max_bin": 255, # 把连续数字切分成最多 255 个桶,极大节省内存和寻找切分点的时间
# --- 绝招三:Leaf-wise 生长策略 ---
"num_leaves": 31, # 限制一棵树最多拥有 31 个叶子节点,精准切分残差最大的分支
"max_depth": 5, # 配合 Leaf-wise 进行“限高”,防止树长得太深导致死记硬背历史(过拟合)
"verbose": -1, # 减少不必要的日志打印,保持控制台清爽
}
# ==========================================
# 5. 串行训练:让多棵树接力学习残差
# ==========================================
# num_boost_round=150: 代表一共有 150 棵树排队串行接力,后一棵树永远在纠正前一棵树的残差
model = lgb.train(params, lgb_train, num_boost_round=150)
# ==========================================
# 6. 预测未来并可视化比对
# ==========================================
# 带着未知的测试集特征(X_test)去走训练好的树群迷宫,每棵树吐出的数字相加得到最终预测值
predictions = model.predict(X_test)
# 转换成带时间索引的 Series,方便画图对齐
predictions_series = pd.Series(predictions, index=test_df.index)
# 绘图比对结果
plt.figure(figsize=(12, 5))
plt.plot(train_df.index[-48:], train_df["load"][-48:], label="History (Last 48h)", color="blue")
plt.plot(test_df.index, y_test, label="Actual Future (Truth)", color="green", linewidth=2)
plt.plot(
test_df.index,
predictions_series,
label="LightGBM Forecast",
color="red",
linestyle="--",
linewidth=2,
)
plt.title("Electricity Load Forecasting using LightGBM (GOSS & Leaf-Wise)")
plt.xlabel("Time")
plt.ylabel("Load (MW)")
plt.legend()
plt.grid(True)
plt.show()
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)