简介这份资源面向电力系统负荷预测方向的初学者与工程实践者提供一套基于LSTM的短期电力负荷预测完整方案帮助读者掌握从数据预处理到模型训练、结果分析的全流程。压缩包共15个文件约5.46MB包含xls格式的原始数据集、ipynb格式的建模笔记、py预测脚本、pt训练好的模型权重以及9张png结果图表和说明文档覆盖数据、代码、模型与可视化各环节。项目依赖pytorch、pandas、numpy、matplotlib等常用库并借助chinese_calendar引入节假日信息xlwt用于导出Excel结果。读者可拿到可直接运行的完整源码与数据学习LSTM网络搭建、训练调参、负荷曲线预测及误差评估方法还能参考散点图、箱线图等分析图表理解模型表现。目前已有326人学习下载适合作为课程设计、毕业设计或电力预测入门项目的参考模板。1. 从一份历史负荷曲线说起LSTM 短期电力预测到底在解决什么电网调度中心每天早上八点要报出未来 24 小时、每 15 分钟一个点的负荷计划96 个数字错一个点就可能让调峰机组多烧几十吨煤。传统做法是拿历史同期均值加人工修正遇到连续高温、节假日调休、大工业突然停产曲线就偏得离谱。基于 LSTM 的短期电力预测要干的事很具体用过去若干天的负荷序列加上温度、星期、节假日标记训练一个能记住「昨天下午三点为什么冲高」的模型把未来一天到一周的负荷点预测出来。它适合有 Python 基础、手头有 SCADA 或电表历史数据的运维和算法同学不需要深度学习科班背景但需要你愿意把数据清洗和特征工程做扎实。这篇笔记按「数据长什么样 → 模型怎么搭 → 参数怎么调 → 哪里会翻车」的顺序走一遍代码和数据组织方式都能直接抄。2. 电力负荷数据长什么样字段、粒度与预处理清单2.1 一份能直接喂给 LSTM 的数据表结构短期电力预测的原始数据通常来自两个地方一是电表/SCADA 导出的负荷记录二是气象接口拿到的温度湿度。我一般把它们对齐成一张宽表时间戳做索引粒度固定 15 分钟或 1 小时。字段不用多但每个都要有明确含义否则后面加特征时自己都记不清。字段名类型含义是否必填timestampdatetime采样时刻带时区是loadfloat有功负荷单位 kW 或 MW是temperaturefloat对应时刻气温摄氏度建议有humidityfloat相对湿度可选is_holidayint0 工作日 / 1 节假日建议有dayofweekint0–6是hourint0–23是load 这一列是预测目标其余是外生变量。注意单位要统一我见过有人把 MW 和 kW 混在一张表里模型训练 loss 直接飙到 1e6排查半天才发现是量纲问题。2.2 缺失值、异常值和重采样三步清洗脚本真实数据几乎没有干净的。电表掉线、通信中断、检修停电都会在序列里留下空洞或 0 值。下面这段清洗逻辑是我反复用过的先重采样到固定频率再处理缺失和异常。import pandas as pd import numpy as np # 读取原始数据timestamp 解析为索引 df pd.read_csv(load_raw.csv, parse_dates[timestamp]) df df.set_index(timestamp).sort_index() # 1. 重采样到 15 分钟缺失处先标记为 NaN df df.resample(15min).mean() # 2. 线性插值填补短缺口4 个点即 1 小时 df[load] df[load].interpolate(methodlinear, limit4) # 3. 用 3-sigma 识别异常并替换为 NaN再插值 mu, sigma df[load].mean(), df[load].std() mask (df[load] - mu).abs() 3 * sigma df.loc[mask, load] np.nan df[load] df[load].interpolate(methodlinear, limit_directionboth) # 4. 删除仍然缺失的行 df df.dropna(subset[load]) print(df.shape, df[load].describe())逻辑说明resample(15min).mean()把不规则时间戳对齐到标准频率这是 LSTM 输入张量形状固定的前提。interpolate的limit4控制插值范围超过 1 小时的缺口不要硬插宁可丢弃否则会引入虚假的平滑趋势。3-sigma 那步针对的是电表跳变和通信误码产生的尖峰替换成 NaN 再插值比直接截断更稳。参数上limit和 sigma 倍数要根据你的采样频率调15 分钟粒度用 4 和 3 是保守值1 小时粒度可以把 limit 放到 2。2.3 归一化与滑动窗口把序列切成 LSTM 能吃的形状LSTM 对输入尺度敏感load 动辄几千 kW直接喂进去梯度会炸。我一般对 load 做 Min-Max 归一化到 [0,1]温度和湿度单独标准化。然后按滑动窗口切样本用过去 96 个点24 小时预测未来 96 个点这是短期预测最常见的设定。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler() df[load_scaled] scaler.fit_transform(df[[load]]) def make_windows(data, lookback96, horizon96): X, y [], [] for i in range(len(data) - lookback - horizon 1): X.append(data[i:ilookback]) y.append(data[ilookback:ilookbackhorizon]) return np.array(X), np.array(y) feature_cols [load_scaled, temperature, humidity, is_holiday] arr df[feature_cols].values X, y make_windows(arr, lookback96, horizon96) print(X.shape, y.shape) # (样本数, 96, 4) (样本数, 96)lookback96对应 24 小时历史horizon96对应未来 24 小时。特征维度 4 是上面选的列数。切完记得按时间顺序划分训练/验证/测试集不能随机打乱否则未来信息泄漏验证 loss 会假性偏低。常见做法是 7:1:2 按时间切。3. 搭一个能跑通的 LSTM 预测模型从层数到损失函数3.1 为什么选 LSTM 而不是 ARIMA 或 TransformerARIMA 对线性趋势和周期建模够用但它处理不了多变量外生输入温度突降这种非线性影响它抓不住。Transformer 表达能力强但短期电力预测的样本量通常只有几千到几万条注意力机制容易过拟合训练成本也高。LSTM 的门控结构天然适合捕捉负荷的日周期和衰减记忆参数量适中在几千条样本上就能收敛。我的经验是数据少于 5 万条、特征不超过 20 维LSTM 是性价比最高的选择。如果数据量上到百万级且有 GPU再考虑 Transformer 或 TCN。3.2 PyTorch 版模型定义与训练循环下面是一个两层 LSTM 加全连接输出的最小可用模型。输入维度对应特征数隐藏层 64输出 96 个点。import torch import torch.nn as nn class LSTMForecaster(nn.Module): def __init__(self, input_dim, hidden_dim64, num_layers2, output_dim96): super().__init__() self.lstm nn.LSTM( input_sizeinput_dim, hidden_sizehidden_dim, num_layersnum_layers, batch_firstTrue, dropout0.2 ) self.fc nn.Linear(hidden_dim, output_dim) def forward(self, x): # x: (batch, lookback, input_dim) out, _ self.lstm(x) # 取最后一个时间步的隐藏状态 last out[:, -1, :] return self.fc(last) model LSTMForecaster(input_dim4, hidden_dim64, num_layers2, output_dim96) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) # 训练循环 for epoch in range(50): model.train() for xb, yb in train_loader: pred model(xb) loss criterion(pred, yb) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step()逻辑说明batch_firstTrue让输入形状是 (batch, seq, feature)符合 pandas 切出来的数组。dropout0.2加在 LSTM 层间抑制过拟合。clip_grad_norm_是防梯度爆炸的后悔药LSTM 在长序列上很容易遇到。损失用 MSE因为负荷预测关心的是数值误差。参数上hidden_dim 从 32 到 128 都常见64 是稳妥起点num_layers 超过 3 在几千样本上基本没收益lr 用 1e-3配合 ReduceLROnPlateau 在验证 loss 不降时减半。3.3 训练集/验证集划分与早停策略时间序列不能随机划分我一般按 7:1:2 切并且用验证集做早停。早停的耐心值设 8 到 10 个 epoch监控验证集 MSE。best_val float(inf) patience, wait 8, 0 for epoch in range(100): train_loss train_one_epoch(model, train_loader, optimizer, criterion) val_loss evaluate(model, val_loader, criterion) if val_loss best_val: best_val val_loss torch.save(model.state_dict(), best_lstm.pt) wait 0 else: wait 1 if wait patience: print(fEarly stop at epoch {epoch}) break验证 loss 连续 8 轮不降就停保存验证集最优权重。这一步能省掉大量无效训练时间也避免过拟合后测试集崩掉。注意验证集要覆盖至少一个完整周周期否则早停判断会偏。4. 参数怎么调、指标怎么看避开短期电力预测的常见坑4.1 输入窗口、预测步长与批大小的联动关系这三个参数不是独立的。lookback 太短模型看不到完整日周期预测早高峰会偏低太长序列里噪声占比上升训练变慢。horizon 越长误差累积越明显96 点24 小时是短期预测的甜点区超过 168 点建议改用滚动预测。batch_size 在几千样本下用 32 或 64太小梯度震荡太大泛化差。我一般先固定 lookback96、horizon96调 hidden_dim 和 lr最后再微调 batch。4.2 评价指标MAPE、RMSE 和峰值误差负荷预测不能只看一个指标。MAPE 反映整体相对误差但负荷接近 0 时会爆炸RMSE 对大误差敏感适合看尖峰峰值误差单独算因为调度最关心早晚高峰。指标公式适用场景注意MAPEmean(abs((y-p)/y))整体精度剔除 y阈值 的点RMSEsqrt(mean((y-p)^2))尖峰误差单位同 load峰值误差max(abs(y-p))/max(y)早晚高峰单独统计我通常要求 MAPE 在 3% 以内、峰值误差 5% 以内才算可用。如果 MAPE 好但峰值误差大说明模型在高峰段欠拟合需要加温度特征或加权损失。4.3 避坑清单五条血泪经验现象验证 loss 正常测试集 MAPE 突然翻倍。原因随机打乱划分导致未来信息泄漏或者归一化用了全量数据。 解决严格按时间切分scaler 只在训练集上 fit再 transform 验证和测试集。现象预测曲线整体平移形状对但数值偏高。原因负荷基线漂移训练集和测试集均值不一致。 解决做差分或去趋势或者加入近期均值作为特征。现象训练 loss 降到很低验证 loss 震荡不降。原因过拟合模型记住了训练集的噪声。 解决加 dropout、减小 hidden_dim、增加训练数据或加 L2 正则。现象早晚高峰预测总是偏低。原因MSE 损失对高峰段和低谷段一视同仁高峰样本占比小。 解决对高峰时段样本加权或用 Huber loss 替代 MSE。现象换一个季节数据模型完全失效。原因训练数据只覆盖单一季节模型没学到温度与负荷的非线性关系。 解决训练集至少覆盖一整年温度特征做标准化必要时分季节建模。5. 把模型推到能用的程度滚动预测与误差修正5.1 滚动预测用预测值喂回输入单次预测 96 点误差会随时间累积。实际部署时我改用滚动方式每次只预测未来 4 个点1 小时然后把预测值拼回输入序列滑动窗口继续预测下一段。这样模型始终基于最新观测误差不会滚雪球。def rolling_forecast(model, init_seq, steps96, step_size4): model.eval() seq init_seq.copy() # (lookback, feature) preds [] with torch.no_grad(): for _ in range(steps // step_size): x torch.tensor(seq[-96:], dtypetorch.float32).unsqueeze(0) out model(x).squeeze().numpy()[:step_size] preds.extend(out) # 把预测的 load 写回特征第一列其余特征沿用最后已知值 for v in out: new_row seq[-1].copy() new_row[0] v seq np.vstack([seq, new_row]) return np.array(preds)逻辑说明step_size4控制每次前推的步数越小越准但推理次数越多。写回时只更新 load 列温度和节假日沿用最后已知值这是工程上的简化如果气象预报可用应该替换成预报值。这个函数在测试集上跑一遍MAPE 通常比单次预测低 0.5 到 1 个百分点。5.2 误差修正用残差模型补一刀LSTM 输出后我会再训一个轻量模型比如线性回归或小 GBDT去拟合残差输入是时间特征和温度。残差往往有系统性偏差比如模型在周末总是偏高修正模型能把这部分吃掉。做法很简单拿验证集的预测值和真实值算残差用时间特征训一个回归推理时把残差加回去。这一步不需要复杂调参通常能再降 0.3% 到 0.8% 的 MAPE。5.3 上线前必须做的三件事第一用最近一个月的数据做回测不要只看测试集那一段。第二把预测结果和实际负荷画在同一张图上肉眼过一遍很多问题看图比看指标快。第三设置误差告警阈值MAPE 连续三天超过 5% 就触发重训。我自己的习惯是每次重训前先检查数据管道有没有断点十次里有三次是数据问题而不是模型问题。希望帮到你。本文还有配套的精品资源点击获取