多特征LSTM电力负荷预测:从数据组织到滚动验证的完整实战

发布时间:2026/10/12 15:47:47
多特征LSTM电力负荷预测:从数据组织到滚动验证的完整实战 简介这份资源是面向高校学生与深度学习入门者的电力负荷预测实战项目基于Python与LSTM搭建多特征预测模型可用于毕业设计、期末大作业或课程设计场景帮助读者理解时序预测的完整流程与工程实现。压缩包共8个文件约830KB包含3个py脚本分别负责数据处理、模型训练与结果可视化2个md说明文档讲解项目思路与运行方式2个csv与1个xlsx提供原始负荷数据及多特征字段代码附有注释新手也能对照理解。目前已有239人学习下载。项目从数据清洗、特征构造到LSTM建模与预测评估形成闭环读者可据此掌握多特征输入的组织方式、模型参数配置与结果展示方法并在此基础上替换数据集或调整网络结构快速完成自己的课题任务。1. 多特征 LSTM 电力负荷预测为什么单变量模型总在早高峰翻车做过电力负荷预测的同行大概率都经历过这个场景用历史负荷单变量喂给 LSTMMAPE 看着挺漂亮一到早高峰和晚高峰就集体翻车预测曲线比实际曲线慢半拍。原因不复杂——负荷从来不是自己跟自己玩它同时被温度、湿度、节假日类型、前一小时负荷、峰谷时段这些外生变量推着走。单变量模型只能靠历史惯性外推遇到突变点必然滞后。这个标题讲的就是把多特征输入和 LSTM 时序建模结合起来做电力负荷预测的完整程序输入侧同时接入负荷序列和气象、日历等协变量模型侧用 LSTM 捕捉多变量之间的时序依赖输出侧给出未来时刻的负荷值。它适合两类人一类是刚学完 LSTM 理论、想找一个能跑通、能改、能对比的真实数据集练手的 Python 学习者另一类是手上有负荷数据、想快速搭一个可复现基线再往上叠模型的工程从业者。下面按「数据怎么组织 → 模型怎么搭 → 训练怎么调 → 坑在哪 → 怎么验证」的顺序讲透。2. 多特征电力负荷预测的数据组织与 LSTM 输入构造2.1 多特征到底加哪些、为什么加这些电力负荷预测里特征不是越多越好加错特征比不加还糟。常见做法是分三组第一组是负荷自身的历史特征。除了原始负荷值通常还会构造前 1 小时、前 24 小时、前 168 小时同周同时刻的滞后项。前 24 小时抓日周期前 168 小时抓周周期这两个是负荷序列里最强的两个周期信号不加基本等于放弃。第二组是气象特征。温度对负荷的影响是非线性的——夏天高温推高制冷负荷冬天低温推高制热负荷中间舒适区反而平稳。所以温度不能只给原始值常见做法是同时给原始温度和温度的平方项或者做分段处理。湿度和体感温度在南方夏季负荷里权重也不低。第三组是日历特征。星期几、是否节假日、是否调休工作日这些直接决定负荷基线的形状。注意日历特征要做成 one-hot 或周期性编码不能直接给 1 到 7 的整数否则模型会误以为周日和周一「距离」比周六和周日更近。提示特征选择阶段建议先做一遍皮尔逊或互信息筛选把和负荷相关性低于阈值的特征剔掉再进模型。多特征不等于全特征。2.2 滑动窗口构造监督学习样本LSTM 吃的是序列但训练时需要监督学习的 (X, y) 对所以要用滑动窗口把时间序列切成样本。假设用过去 24 个时间步预测未来 1 个时间步窗口就是 24预测步长是 1。import numpy as np import pandas as pd def make_supervised(data, target_col, feature_cols, window24, horizon1): data: 按时间排序后的 DataFrame target_col: 预测目标列名通常是负荷 feature_cols: 参与输入的特征列 window: 回看步数 horizon: 预测未来第几步 arr_x data[feature_cols].values arr_y data[target_col].values X, y [], [] for i in range(len(data) - window - horizon 1): # 输入窗口过去 window 步的所有特征 X.append(arr_x[i:i window]) # 标签窗口结束后第 horizon 步的负荷 y.append(arr_y[i window horizon - 1]) return np.array(X), np.array(y)这段代码的逻辑是对每个时间点 i取[i, iwindow)区间的全部特征作为输入取iwindowhorizon-1时刻的负荷作为标签。参数上window决定模型能看多远的历史太小抓不到周周期太大训练慢且容易过拟合24 到 168 之间是常见区间horizon决定预测提前量日前预测一般取 24实时预测取 1 到 4。2.3 归一化必须按特征分别做且只能用训练集统计量多特征输入里负荷可能是几千到几万 kW温度是零下十几到四十度湿度是 0 到 100量纲差了几个数量级。不归一化LSTM 的梯度会被大数值特征主导小数值特征等于没加。from sklearn.preprocessing import MinMaxScaler # 按时间顺序切分不能随机打乱 train_size int(len(data) * 0.7) val_size int(len(data) * 0.15) train data.iloc[:train_size] val data.iloc[train_size:train_size val_size] test data.iloc[train_size val_size:] scaler_x MinMaxScaler() scaler_y MinMaxScaler() # 关键scaler 只在训练集上 fit train_x_scaled scaler_x.fit_transform(train[feature_cols]) val_x_scaled scaler_x.transform(val[feature_cols]) test_x_scaled scaler_x.transform(test[feature_cols]) train_y_scaled scaler_y.fit_transform(train[[target_col]]) val_y_scaled scaler_y.transform(val[[target_col]]) test_y_scaled scaler_y.transform(test[[target_col]])这里最容易翻车的地方是很多人图省事先对全量数据 fit_transform 再切分。这样验证集和测试集的统计信息泄漏进了训练过程离线指标会虚高上线就露馅。正确做法是 scaler 只在训练集 fit验证和测试只 transform。另外预测结果要记得用scaler_y.inverse_transform还原回真实量纲否则算出来的 MAPE 没有意义。3. LSTM 模型搭建从单层到多特征输入的参数怎么定3.1 输入张量形状与 batch 组织PyTorch 里 LSTM 的默认输入形状是(seq_len, batch, input_size)如果设了batch_firstTrue就变成(batch, seq_len, input_size)。多特征场景下input_size等于特征列的数量seq_len等于滑动窗口长度。这个对应关系搞错模型不报错但学不到东西是典型的黑匣子式翻车。import torch import torch.nn as nn class LoadLSTM(nn.Module): def __init__(self, input_size, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0 ) self.fc nn.Linear(hidden_size, 1) def forward(self, x): # x: (batch, seq_len, input_size) out, (h_n, c_n) self.lstm(x) # 取最后一个时间步的隐状态做回归 last out[:, -1, :] return self.fc(last)input_size必须等于特征列数这是多特征模型和单变量模型唯一的接口差异。hidden_size控制记忆容量64 到 128 是负荷预测的常用区间太小欠拟合太大在几千条样本上直接过拟合。num_layers超过 2 层在中小规模负荷数据上收益很小反而增加训练难度一般 1 到 2 层够用。dropout只在多层时生效单层 LSTM 加 dropout 是无效的。3.2 训练循环与损失函数选择负荷预测是回归任务损失函数常用 MSE 或 MAE。MSE 对大误差敏感适合关注峰值预测MAE 对异常值鲁棒适合数据里有毛刺的场景。如果业务上更关心百分比误差可以用 MAPE 做评估但训练仍用 MSE因为 MAPE 在负荷接近零时不稳定。from torch.utils.data import DataLoader, TensorDataset # 构造 Dataset train_ds TensorDataset( torch.tensor(train_x_scaled, dtypetorch.float32), torch.tensor(train_y_scaled, dtypetorch.float32) ) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) model LoadLSTM(input_sizelen(feature_cols)) optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.MSELoss() for epoch in range(100): model.train() total_loss 0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() # 梯度裁剪防止 LSTM 梯度爆炸 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() total_loss loss.item() print(fepoch {epoch}, loss {total_loss / len(train_loader):.6f})batch_size取 32 到 128 之间样本量小就取小一点。lr用 1e-3 起步训练不收敛就降到 1e-4。梯度裁剪是 LSTM 训练的后悔药max_norm设 1.0 能挡掉大部分梯度爆炸导致的 loss 变 NaN。训练轮数不要死盯 100配合验证集早停更稳。3.3 验证集评估与早停训练 loss 下降不代表模型有用必须每个 epoch 在验证集上算一次真实量纲的误差否则你调的全是玄学。def evaluate(model, X, y_true, scaler_y): model.eval() with torch.no_grad(): pred model(torch.tensor(X, dtypetorch.float32)).numpy() pred_inv scaler_y.inverse_transform(pred) y_inv scaler_y.inverse_transform(y_true.reshape(-1, 1)) mae np.mean(np.abs(pred_inv - y_inv)) mape np.mean(np.abs((pred_inv - y_inv) / y_inv)) * 100 return mae, mape评估时一定要先inverse_transform再算指标否则算出来的是归一化空间的误差和业务方看到的数字对不上。早停策略一般是验证集 MAPE 连续 10 个 epoch 不下降就停保留验证集最优的权重。4. 多特征 LSTM 负荷预测的避坑与排查清单4.1 预测曲线整体平移一个时间步现象预测曲线形状和实际几乎一样但整体提前或滞后一个时间步MAPE 卡在 5% 以上下不去。原因滑动窗口构造时标签对齐错了。常见错误是y.append(arr_y[i window])而不是arr_y[i window horizon - 1]horizon 为 1 时两者恰好差一位。解决打印前三条样本的输入窗口最后一步和标签值人工核对时间戳是否连续。对齐问题只能靠肉眼验证没有捷径。4.2 验证集 loss 远高于训练集且持续上升现象训练 loss 一路降到很低验证 loss 先降后升MAPE 在验证集上比训练集高出一大截。原因过拟合。多特征输入维度高样本量不够时模型会记住训练集的噪声。解决先降hidden_size和num_layers再加大dropout还不行就加 L2 正则Adam 的weight_decay参数。如果特征里有强共线性的列先做相关性筛选剔掉冗余特征。4.3 加了气象特征后效果反而变差现象单变量模型 MAPE 8%加了温度和湿度后变成 10%。原因气象特征没做滞后对齐。负荷对温度的反应有延迟当前时刻的温度影响的是几小时后的负荷直接把当前温度拼到当前负荷上等于给模型喂了错位信息。解决给气象特征做滞后处理常见做法是把温度整体后移 1 到 3 小时再拼进特征矩阵。具体移多少用互信息或交叉相关函数扫一遍取相关性最高的滞后阶数。4.4 loss 变成 NaN 或突然爆炸现象训练几个 epoch 后 loss 打印出 nan或者从 0.01 跳到几千。原因LSTM 的梯度爆炸或者学习率设太大或者输入里有未处理的缺失值NaN 参与运算会污染整个 batch。解决先检查输入数据有没有 NaN用data.isnull().sum()扫一遍缺失值用前向填充或插值补上。然后加梯度裁剪把学习率降到 1e-4。三步做完基本能压住。4.5 测试集指标好但上线后误差大现象离线 MAPE 6%实际跑起来误差 15% 以上。原因数据泄漏。最常见的是归一化时用了全量数据 fit或者滑动窗口跨越了训练集和测试集的边界把测试集的信息漏进了训练样本。解决切分数据要在构造滑动窗口之前做切分点前后留一段 gap比如 24 小时不参与任何一侧避免窗口跨界。归一化严格只在训练集 fit。5. 用消融对比和滚动预测验证多特征 LSTM 到底值不值得上模型跑通只是起点真正决定这个方案值不值得投入的是它比基线强多少、强在哪。我一般会做两组验证。第一组是消融对比。固定 LSTM 结构和超参只改输入特征组合跑三组纯负荷单变量、负荷加日历、负荷加日历加气象。每组跑三次取平均记录验证集 MAPE。如果加气象后 MAPE 没有明显下降说明你的气象特征滞后对齐没做好或者该地区负荷对气象本身就不敏感这时候硬加特征只是增加维护成本。特征组合验证集 MAPE训练耗时结论负荷单变量8.2%快基线负荷 日历6.5%中日历特征收益明显负荷 日历 气象5.8%慢气象有增益但需对齐第二组是滚动预测验证。不要只在一个测试集上算一次指标用滚动窗口的方式每次预测未来 24 小时然后窗口向前滑 24 小时连续跑一个月的数据统计每天的 MAPE 分布。这样能看出模型在周末、节假日、极端天气下的稳定性比单一指标有说服力得多。def rolling_forecast(model, data, feature_cols, target_col, scaler_x, scaler_y, window24, horizon24, step24): 滚动预测每次预测未来 horizon 步窗口向前滑 step 步 results [] for start in range(0, len(data) - window - horizon 1, step): seg data.iloc[start:start window horizon] x_input scaler_x.transform(seg[feature_cols].iloc[:window]) x_tensor torch.tensor(x_input, dtypetorch.float32).unsqueeze(0) model.eval() with torch.no_grad(): pred model(x_tensor).numpy() pred_inv scaler_y.inverse_transform(pred)[0, 0] true_val seg[target_col].iloc[window] results.append((seg.index[window], pred_inv, true_val)) return pd.DataFrame(results, columns[time, pred, true])这段滚动预测代码的关键参数是step它决定窗口每次向前滑多少。step等于horizon时是标准的日前滚动预测step小于horizon时预测区间有重叠适合做更密集的误差分析。跑完把pred和true画在同一张图上重点看峰谷时段的偏差那才是业务真正在意的地方。最后说个我自己的习惯每次改完特征或超参我都会把当次的验证集 MAPE、训练轮数、特征列表记在一个表格里跑够十组再回头看哪类改动真正有效。多特征 LSTM 负荷预测这个方向模型结构本身没有太多花活胜负手几乎全在特征工程和数据切分的细节上。把消融和滚动验证做扎实比盲目堆层数、调学习率有用得多。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询