
简介这份资源面向电力系统负荷预测方向的学习者与工程实践者提供一套基于LSTM的短期电力负荷预测完整方案适合具备Python基础、希望掌握深度学习时序建模的中高级读者。压缩包共15个文件约5.46MB包含xls原始数据集、ipynb完整实验笔记、py预测脚本、pt训练模型、md说明文档及9张png结果图覆盖数据预处理、模型搭建与训练、结果分析全流程。资源依赖pytorch、pandas、numpy、matplotlib等库并借助chinese_calendar引入节假日信息xlwt用于导出Excel结果兼顾工程落地细节。读者可拿到可直接运行的源码与已训练模型通过预测脚本快速复现某日负荷曲线并借助准确率散点图、MSE曲线、负荷箱线图等可视化图表理解模型表现与误差分布。目前已有326人学习下载适合课程设计、科研入门或电力预测项目参考。1. 从一份能跑通的 LSTM 短期电力负荷预测源码说起电力负荷预测这个方向很多人第一次接触都是在课程设计或者工作里的一个临时需求手上有一列历史负荷数据领导或者导师要你给出未来一天 96 个点的负荷曲线。真上手才发现难点根本不在 LSTM 本身而在数据怎么清洗、日期特征怎么构造、预测结果怎么评估。这份LFforecast-main就是冲着这个场景来的——它把数据预处理、模型搭建训练、结果分析、单日预测整条链路都放在了一个 Python 工程里配套一份STLF_DATA_IN_1.xls数据集和训练好的model.pt属于那种下载下来改改路径就能跑的类型。适合刚入门 lstm时间序列预测python 的读者也适合手上有电力数据、想找个能复现的 lstm模型代码 当基线的人。2. 工程结构与数据流先搞清楚每个文件在干什么2.1 目录拆解与依赖清单拿到压缩包先别急着跑把目录结构过一遍能省掉后面一半的报错。这个工程的结构很典型src放代码data放数据img放过程图根目录放说明文件。路径作用是否要改data/STLF_DATA_IN_1.xls原始负荷数据集换自己数据时替换src/LF_Forecasting.ipynb预处理、建模、训练、分析全流程主要改动点src/model.pt训练好的模型权重直接加载或重训覆盖src/predict.py预测某一天负荷曲线改日期和路径img/*.png训练过程与结果图只读参考README.md/README.txt环境与文件说明只读依赖库一共六个装的时候建议单独建虚拟环境避免和系统里的老版本 pandas 打架python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install torch pandas numpy matplotlib xlwt chinesecalendar这里xlwt是用来导出 Excel 的chinesecalendar用来判断节假日——电力负荷对节假日极其敏感春节那几天的曲线和普通工作日完全不是一个形状这个库就是干这个的。注意chinesecalendar的包名和导入名不一样装的时候是chinesecalendar代码里import chinese_calendar第一次用的人经常在这卡一下。2.2 数据长什么样特征怎么构造STLF_DATA_IN_1.xls是典型的短期负荷STLFShort-Term Load Forecasting数据按时间戳排列的负荷值。LSTM 吃的是序列所以核心工作是把一维时间序列切成「输入窗口 → 预测目标」的样本对。常见做法是滑动窗口用过去 N 个时刻的负荷预测下一个时刻或者用过去若干天的同一时刻预测未来一天。import pandas as pd import numpy as np # 读取原始负荷数据 df pd.read_excel(./data/STLF_DATA_IN_1.xls) df.columns [timestamp, load] # 统一列名避免中文列名踩坑 df[timestamp] pd.to_datetime(df[timestamp]) df df.sort_values(timestamp).reset_index(dropTrue) # 缺失值处理负荷数据常见的是整点缺失用线性插值比均值填充更合理 df[load] df[load].interpolate(methodlinear) # 构造时间特征小时、星期、是否周末、是否节假日 df[hour] df[timestamp].dt.hour df[weekday] df[timestamp].dt.weekday df[is_weekend] (df[weekday] 5).astype(int) import chinese_calendar as cc df[is_holiday] df[timestamp].dt.date.apply( lambda d: int(cc.is_holiday(d)) ) print(df.head())这段代码做了三件事统一列名、按时间排序、补缺失值。参数上interpolate(methodlinear)适合连续缺失不超过几个点的场景如果某天整段缺失线性插值会拉出一条假直线这时候更稳妥的是直接丢掉那天或者用前一周同一天的数据补。is_holiday这个特征别小看很多基线模型预测春节前后误差爆炸就是因为没把节假日信息喂进去。2.3 归一化与滑动窗口切分LSTM 对输入尺度敏感负荷值动辄几千上万千瓦不归一化收敛会很慢甚至不收敛。工程里一般用 Min-Max 归一化把负荷压到 [0,1]。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) load_scaled scaler.fit_transform(df[[load]]) def make_windows(series, lookback96, horizon1): lookback: 用过去多少个点; horizon: 预测未来第几个点 X, y [], [] for i in range(len(series) - lookback - horizon 1): X.append(series[i:i lookback]) y.append(series[i lookback horizon - 1]) return np.array(X), np.array(y) X, y make_windows(load_scaled, lookback96, horizon1) print(X.shape, y.shape) # (样本数, 96, 1) (样本数, 1)lookback96是按 15 分钟一个采样点算的正好一整天这是短期负荷预测里很常见的窗口长度。如果你的数据是小时级那 lookback 设 24 更合适。horizon1表示预测下一个点想做「预测未来一天 96 个点」就把 horizon 循环到 96或者改成多输出结构。切分训练集测试集时一定要按时间顺序切不能 shuffle否则就是拿未来数据预测过去指标好看但毫无意义——这是时间序列里最经典的翻车点。3. 模型搭建与训练LSTM 结构怎么定、参数怎么调3.1 网络结构设计与 PyTorch 实现这个工程用的是标准 LSTM 回归结构输入维度 1单变量负荷也可以把时间特征拼进去变成多变量。下面是一个能直接替换进 notebook 的实现import torch import torch.nn as nn class LSTMForecaster(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout ) 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) model LSTMForecaster(input_size1, hidden_size64, num_layers2) print(sum(p.numel() for p in model.parameters()), params)参数说明hidden_size64是单变量负荷预测的常用起点数据量大可以加到 128num_layers2够用再深容易过拟合且训练慢dropout0.2只在多层 LSTM 之间生效单层时这个参数会被忽略别以为设了就有用。batch_firstTrue让输入形状是(batch, seq, feature)符合 pandas 转过来的直觉忘了设会导致维度对不上。3.2 训练循环与损失函数选择训练部分的关键是损失函数和早停。负荷预测常用 MSE 或 MAEMSE 对大误差惩罚重MAE 对异常值更稳。from torch.utils.data import TensorDataset, DataLoader X_t torch.tensor(X, dtypetorch.float32) y_t torch.tensor(y, dtypetorch.float32) split int(len(X_t) * 0.8) # 按时间顺序 8:2 切 train_ds TensorDataset(X_t[:split], y_t[:split]) test_ds TensorDataset(X_t[split:], y_t[split:]) train_loader DataLoader(train_ds, batch_size64, shuffleFalse) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) best_loss, patience, wait float(inf), 10, 0 for epoch in range(100): model.train() for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() optimizer.step() # 简单早停验证损失连续 patience 轮不降就停 model.eval() with torch.no_grad(): val_pred model(X_t[split:]) val_loss criterion(val_pred, y_t[split:]).item() if val_loss best_loss: best_loss, wait val_loss, 0 torch.save(model.state_dict(), ./src/model.pt) else: wait 1 if wait patience: print(fearly stop at epoch {epoch}) break print(fepoch {epoch} val_loss {val_loss:.6f})batch_size64是常规选择数据量小可以降到 32。shuffleFalse在训练集内部其实可以设 True但测试集绝对不能打乱。早停的patience10是经验值太小会欠训练太大浪费时间。保存模型用state_dict()而不是整个模型对象加载时更灵活也不容易因为类定义变动而报错。3.3 结果评估与可视化工程img目录里那几张图——real_pred.png、accu_scatter.png、train_test_mse.png——就是评估环节的产物。评估不能只看 MSE还要看 MAPE 和日负荷曲线的形状贴合度。import matplotlib.pyplot as plt model.eval() with torch.no_grad(): pred_scaled model(X_t[split:]).numpy() # 反归一化回原始量纲 pred scaler.inverse_transform(pred_scaled) true scaler.inverse_transform(y_t[split:].numpy()) mape np.mean(np.abs((true - pred) / true)) * 100 print(fMAPE: {mape:.2f}%) plt.figure(figsize(12, 4)) plt.plot(true[:96], labelreal) plt.plot(pred[:96], labelpred) plt.legend() plt.savefig(./img/real_pred_check.png, dpi150)MAPE 在负荷预测里比 MSE 更直观一般要求控制在 3% 以内算合格。注意inverse_transform的输入形状要和 fit 时一致传一维数组会报维度错误这是新手高频报错点。画图时取前 96 个点正好是一天方便肉眼判断曲线形状对不对。4. 单日预测脚本 predict.py从模型到可用输出4.1 加载模型与构造预测输入predict.py的作用是给定某一天输出那天的负荷曲线。它和训练脚本最大的区别是训练时数据是批量的预测时往往只有一条序列而且需要把最近的历史负荷喂进去。import torch import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler def load_model(path./src/model.pt, input_size1, hidden_size64, num_layers2): model LSTMForecaster(input_size, hidden_size, num_layers) model.load_state_dict(torch.load(path, map_locationcpu)) model.eval() return model def predict_day(model, history, scaler, lookback96): history: 最近 lookback 个点的原始负荷numpy 一维 seq scaler.transform(history.reshape(-1, 1)) x torch.tensor(seq, dtypetorch.float32).unsqueeze(0) # (1, lookback, 1) with torch.no_grad(): pred_scaled model(x).item() return scaler.inverse_transform([[pred_scaled]])[0][0]map_locationcpu是保险做法避免在有 GPU 的机器上训练、在没 GPU 的机器上加载时报错。unsqueeze(0)是给单条序列补上 batch 维度LSTM 要求输入至少是三维的漏了这步会直接抛维度异常。4.2 滚动预测一整天 96 个点预测一整天不能只调一次模型得滚动预测出下一个点把它拼回历史再预测下下个点。def predict_curve(model, history, scaler, steps96, lookback96): hist list(history[-lookback:]) preds [] for _ in range(steps): p predict_day(model, np.array(hist[-lookback:]), scaler, lookback) preds.append(p) hist.append(p) # 把预测值当作真实值继续滚动 return np.array(preds) curve predict_curve(model, history_raw, scaler, steps96) print(curve.shape) # (96,)滚动预测有个绕不开的问题误差会累积。预测到第 96 个点时前面 95 个预测值已经带进了输入误差被放大。缓解办法是控制滚动步数或者用 seq2seq 结构一次性输出 96 个点。这个工程用的是滚动方式简单直接适合做基线。如果发现曲线后半段明显跑偏多半就是误差累积不是模型坏了。4.3 导出预测结果到 Excel工程依赖xlwt就是为了把预测结果落成 Excel 交付。import xlwt wb xlwt.Workbook() ws wb.add_sheet(forecast) ws.write(0, 0, point) ws.write(0, 1, load) for i, v in enumerate(curve): ws.write(i 1, 0, i) ws.write(i 1, 1, float(v)) wb.save(./forecast_result.xls)xlwt只能写.xls老格式单个 sheet 上限 65536 行做日曲线完全够用。如果数据量大或者要写.xlsx换成openpyxl更合适。写的时候记得float(v)numpy 的 float32 直接写进去某些版本会报类型错误。5. 避坑与排查这几处不踩一遍不算跑通5.1 现象loss 一直不降甚至变成 nan原因通常是学习率太大或者数据没归一化。负荷值几千的量级直接喂进 LSTM梯度会炸。解决确认MinMaxScaler已经 fit 在训练集上学习率从1e-3往下调到1e-4试同时检查数据里有没有 inf 或超大异常值。5.2 现象预测曲线整体平移形状对但数值差一截这是反归一化用错了 scaler。常见错误是训练用一个 scaler预测时又新建了一个 fit 在预测数据上。解决把训练时的 scaler 用joblib.dump存下来预测时load回来保证变换一致。5.3 现象chinesecalendar导入报错或节假日判断不准包名是chinesecalendar导入是chinese_calendar装错名字会 ModuleNotFoundError。另外这个库只覆盖到特定年份超出范围会抛异常。解决装对包名并在调用处 try/except 兜底超出范围就按普通日处理。5.4 现象训练指标很好实际预测一塌糊涂八成是切分时 shuffle 了或者用了未来信息构造特征。解决所有切分按时间顺序特征只能用当前时刻及之前的信息节假日、星期这类静态特征没问题但别把「明天的负荷」这种泄漏特征混进去。5.5 现象model.pt加载报 key 不匹配训练和加载时的网络结构参数不一致比如训练用hidden_size64加载时写成 128。解决把结构参数抽成配置字典训练和预测共用同一份别在两处各写一遍。6. 进阶技巧把单变量 LSTM 改成多变量输入跑通基线之后最值得做的一步是加特征。单变量负荷预测的天花板不高把温度、星期、节假日拼进输入MAPE 通常能再降一两个点。做法是把input_size从 1 改成特征数构造窗口时每个时间步带上多个特征。# 假设特征列为 load, temp, weekday, is_holiday feat_cols [load, temp, weekday, is_holiday] data df[feat_cols].values scaler_mv MinMaxScaler() data_scaled scaler_mv.fit_transform(data) def make_windows_mv(arr, lookback96, horizon1): X, y [], [] for i in range(len(arr) - lookback - horizon 1): X.append(arr[i:i lookback]) # (lookback, n_feat) y.append(arr[i lookback horizon - 1, 0]) # 只预测负荷这一列 return np.array(X), np.array(y) Xm, ym make_windows_mv(data_scaled, lookback96) model_mv LSTMForecaster(input_sizelen(feat_cols), hidden_size64, num_layers2)关键点有两个一是input_size必须等于特征数忘了改会直接维度报错二是预测目标只取负荷那一列别把温度也当预测目标。多变量归一化要整体 fit不能每列单独 fit 再拼否则列间关系会被破坏。验证多变量是否真的有用别只看训练 loss要在测试集上对比单变量和多变量的 MAPE并且固定随机种子多跑几次取平均单次结果波动可能掩盖真实差异。我一般会写个小脚本把两组 MAPE 打出来对比确认稳定下降才保留新特征。从那以后我每次拿到一份时间序列源码都会先确认三件事切分有没有按时间、归一化 scaler 有没有复用、预测时有没有信息泄漏。这三条过了剩下的调参才有意义。希望帮到你。本文还有配套的精品资源点击获取