简介基于深度学习算法实现电力负荷时间序列预测的完整Python源码包面向计算机相关专业学生、毕业设计/课程设计人员以及电力数据入门研究者解决负荷曲线建模与未来时段预测问题。项目覆盖数据读取、模型构建、训练评估与预测输出等环节内置Transformer、LSTM、GRU等深度学习模型并同步提供ARIMA、KNN、DT、RF经典方法对比注释详细便于对照理解时序预测完整流程。压缩包共20个文件以16个Python脚本为核心附加2个TXT说明与2个Markdown笔记整体仅43KB结构精简、便于阅读和二次修改。目前已有266人学习下载适合用作课程作业、毕设演示或初期项目立项参考也可在理解各模型原理后进一步扩展其他负荷预测场景。1. 电力负荷时间序列预测为什么这份深度学习 Python 源码值得照着跑一遍电力负荷预测是电网调度和电力交易里的老问题调度员手里拿着未来一天的 96 点负荷曲线预测偏了要么多备了旋转备用容量要么晚高峰时段还要紧急调机组。传统 ARIMA 和指数平滑只在平稳序列上表现尚可遇到气温骤变、节假日就明显跟不上。深度学习模型尤其是 LSTM 和 CNN-LSTM把负荷历史序列和温度等外生特征一起送进网络能学到周期与突变规律这就是“基于深度学习算法实现电力负荷的时间序列未来预测 python 源码(有详细注释)”这类源码包的实用价值。用 Python 跑通这套时间序列预测流程其实是在给业务装一个比传统统计模型更抗干扰的负荷外推器。这篇笔记适合电力数据分析师、做 Python 时序预测的工程师也适合想拿这份源码做毕业设计基线的同学。2. 把负荷预测拆成建模问题周期规律、滑窗切分与输入输出设计时间序列预测源码和图像分类源码最大的区别是图像直接有原图而负荷预测只有一串在时间轴上的数值神经网络不认识“时间”只认识矩阵。所以第一步永远不是选模型而是把负荷序列重构成监督学习格式用过去input_len个点预测未来pred_len个点。这一步做不对后面换什么网络都白搭——我见过太多人拿到源码先改模型结构结果指标上不去回头一看是滑窗边界直接截断了样本训练标签里混进了跨天错位的数据。2.1 先看懂负荷序列的三条可用规律日周期、工作日效应与天气耦合在做任何时间序列分析之前先把原始曲线画出来盯着看十分钟。电力负荷序列有三个稳定特征这决定了源码里的滑窗参数应该怎么设。第一个是周期分布15 分钟采集粒度下一天 96 个点曲线呈“早晚双峰”形态夜间低、白天高。这个强周期性让滑窗天然合理——用过去 96 个点去预测下一个 96 个点本质上是在让模型学习“同类日”的形态迁移。第二个是工作日与休息日的形态差异工作日晚高峰后移、波峰更高周末整体下移且午间低谷更平缓。很多正式源码的处理方式是分组建模或者把星期几、是否节假日做成特征拼进输入。第三个是天气耦合夏季降温负荷和温度强相关而且有数小时的滞后温度突变对负荷的影响不会立刻体现在当天同一时刻。因此正经一点的源码都会预留天气特征的合并接口而不是只喂负荷本身。读源码时先看数据处理函数有没有处理这三个维度比看模型结构更有价值。如果数据处理只是df[load].values直接塞进网络那这份源码的上限早就被锁死了。2.2 数据清洗与滑窗切分从原始表到可训练样本对的完整代码拿到手的第一份数据通常是两列时间戳和负荷值。先把缺失值和重复索引清掉再切窗口。下面是这类源码里最常见的数据处理写法。import numpy as np import pandas as pd # 默认列名ds 为时间戳load 为负荷值部分数据集用 date / time / value df pd.read_csv(load_data.csv, parse_dates[ds]) df df.set_index(ds).sort_index() # 缺失值处理少量缺口用线性插值连续缺口超过 3 个点建议直接删段 df[load] df[load].interpolate(methodlinear, limit3) # 重复索引同样会扰乱滑窗顺序这里直接去重 df df[~df.index.duplicated(keepfirst)] def sliding_window(data, input_len96, pred_len24, step1): 把一维负荷序列切成监督学习样本。 参数说明 data: 已经按时间排好序的一维数组 input_len: 输入历史窗口长度15分钟粒度下 96 1 天 pred_len: 预测未来长度24 未来 6 小时 step: 滑动步长步长越小样本量越大相邻样本重叠越多 X, y [], [] total len(data) for i in range(0, total - input_len - pred_len 1, step): X.append(data[i:i input_len]) y.append(data[i input_len:i input_len pred_len]) return np.array(X, dtypenp.float32), np.array(y, dtypenp.float32) X, y sliding_window(df[load].values, input_len96, pred_len24, step96) print(X.shape, y.shape)这段代码有两个关键边界。一是循环上界total - input_len - pred_len 1因为最后一个窗口不仅要凑够输入长度还要留出它的预测标签区间写法少了pred_len最后一批样本的标签会越界或错位。二是step的选择step96时相邻窗口完全不重叠样本量最少但训练最快step1时相邻样本高度重叠适合数据量少的场景但会显著拉长训练时间而且相邻样本几乎一样容易让模型对训练集过拟合。我一般先按业务口径定input_len和pred_len再用step换样本密度。2.3 输入输出长度怎么定预测步长、滞后特征与归一化顺序窗口长度是这份源码里最值得调的超参数不是模型隐藏层大小。15 分钟粒度的电网数据输入 96 点即过去一天输出 24 点是未来六小时这是调度最常用的短时口径如果想做未来一天预测输出就是 96 点。这里有个常见误用输出超过一定长度时有人先训练一个单步模型再把它递归预测、把上一步输出拼回输入继续推。这个做法误差会随步数累积跑出来的曲线越到后面越平Late 峰直接被磨掉。常见做法是让模型并行输出pred_len个点也就是把 LSTM 最后的全连接输出维度直接设成pred_len。归一化顺序是另一个高发坑点。用MinMaxScaler把负荷压到 0-1 之间本身没问题但必须先在训练段上fit再去transform全部数据。下面是正确和错误写法的对比。from sklearn.preprocessing import MinMaxScaler # 错误示范对全量数据先 fit 再 transform测试段的 max/min 被 scaler 记住了 # scaler MinMaxScaler().fit(all_load.reshape(-1, 1)) # all_norm scaler.transform(all_load.reshape(-1, 1)) # 正确做法先按时间切分再只对训练段 fit train_load df.loc[:2021-12-31, load].values test_load df.loc[2022-01-01:, load].values scaler MinMaxScaler().fit(train_load.reshape(-1, 1)) train_norm scaler.transform(train_load.reshape(-1, 1)).flatten() test_norm scaler.transform(test_load.reshape(-1, 1)).flatten()这段代码对应的原理是测试集在真实业务里是“未来数据”未来数据的统计量不可能提前知道。如果你让 scaler 在全量数据上拟合训练时模型就间接看见了测试集的范围验证指标会虚高——这是负荷预测源码里最常见的隐性数据泄漏后面第五章节会展开讲。3. 模型选型与源码结构从 LSTM 到 CNN-LSTM先跑通再对比负荷序列本质是带强周期成分的时间序列LSTM 类循环网络擅长捕捉长距离依赖所以这份源码的基线几乎都是 LSTM。但实际工程里LSTM 只是起点。现在最稳的升级套路是 CNN-LSTM一维卷积先在输入端压缩噪声、提取局部波形再交给 LSTM 建模时间依赖训练通常更稳、收敛也更快。这部分会把手感和参数讲细让你拿到源码后知道先跑哪个文件、改哪个变量。3.1 为什么基线选 LSTM再上 CNN-LSTM不同模型的取舍可以先看一张对比表再根据自己的数据特点去选。模型适用场景收敛速度抗噪能力说明LSTM滑窗长度适中、序列规律清晰的基线慢中等经典基线适合先验证滑窗是否合理CNN-LSTM输入噪声大、负荷曲线毛刺多较快强一维卷积有滤波和特征提取双重作用TCN超长输入、梯度路径要求短较快强因果膨胀卷积适合分钟级长序列Transformer数据量大、多变量特征复杂快强小数据量容易过拟合需要正则化盯紧如果源码里同时给了 LSTM 和 CNN-LSTM 两个模型文件先用 LSTM 把整条链路跑通再切换到 CNN-LSTM 对比验证集指标。不要一上来就改结构否则数据处理层的 bug 会和模型问题混在一起排查时极其痛苦。3.2 源码四件套结构数据加载、模型定义、训练循环、评估回放正经的时序预测源码目录结构是四件套这是我看这类压缩包时最在意的部分。文件职责如下。文件职责重点检查内容config.py集中管理全部超参数滑窗长度、学习率、批大小、早停口径data_loader.py加载 CSV、清洗、滑窗、切分、归一化是否只对训练段做 fit是否按时间切分model.py定义 LSTM / CNN-LSTM 网络结构输出维度是否等于 pred_lentrain.py训练主循环、验证、早停、保存最佳权重是否关 shuffle是否做梯度裁剪evaluate.py反归一化、计算 MAPE / MAE、绘图指标是算在反归一化之后还是之前源码的“详细注释”通常堆在 train.py 里因为训练循环才是最容易因为一行写错就静默产生坏模型的代码。下面是 PyTorch 版本的核心训练循环注释里标了三个关键动作。import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset # 按时间顺序构造数据集shuffleFalse 是关键 train_set TensorDataset(torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.float32)) train_loader DataLoader(train_set, batch_sizebatch_size, shuffleFalse) model LSTMPredictor(input_dim1, hidden_size64, num_layers2, pred_len24) optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, patience5, factor0.5) criterion nn.MSELoss() for epoch in range(epochs): model.train() train_loss 0.0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) # 输出形状(batch, pred_len) loss criterion(pred, yb) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 1.0) # 防 RNN 梯度爆炸 optimizer.step() train_loss loss.item() * len(xb) val_loss evaluate(model, X_val, y_val) # 每个 epoch 结束算一次验证 loss scheduler.step(val_loss)这里有两个容易被忽略的细节。shuffleFalse对时间序列非常重要随机打乱会让模型学到“从任意位置都能预测未来”的错误先验真实业务中预测对象永远是尚未发生的连续区间。clip_grad_norm_是 RNN 训练的保险丝不裁剪的话梯度一旦在长序列上累积爆炸loss 会直接跳到 nan整个训练白跑。源码注释里反复强调的早停本质上就是配合ReduceLROnPlateau验证 loss 连续 5-10 个 epoch 不降就减半学习率再连续 10 个 epoch 不降就保存当前最佳权重并终止。3.3 直接抄的超参数表批大小、学习率、隐藏层、早停时序预测源码里下面这份参数表可以直接作为第一组尝试的起点。参数推荐起始值调参方向与说明input_len9615 分钟粒度下等于 1 天短于 48 会丢失日周期信号pred_len24短时预测用 24日预测用 96不要用递归多步替代并行输出hidden_size64 或 12864 起步效果不够再翻倍超过 256 在小数据集上容易过拟合num_layers2超过 3 层在负荷序列上收益极小训练时间翻倍dropout0.2数据量大时降到 0.1过拟合明显时提到 0.3batch_size64数据量大可用 128批量翻倍时学习率同步调大learning_rate1e-3loss 锯齿振荡就降 1e-4平台不降就先检查数据再调结构epochs100 配早停不看固定 epoch看验证 loss 连续 10 轮不降就停这些参数之间有联动关系不要单独调。hidden_size 翻倍后模型容量变大了dropout 通常也要跟着上调否则验证集指标会先降后升、出现典型的过拟合 U 型曲线。学习率的经验是训练曲线像锯齿一样上下抖先降一个数量级如果训练 loss 平得像条直线那多半不是学习率问题而是滑窗或者归一化出了问题。4. 训练与验证的关键动作防数据泄漏、按时间切验证集、反归一化还原跑这类项目的血泪经验是模型“跑通”很快指标好看也不难难的是指标经得起推敲。这一章处理的是决定指标可信度的三件事数据泄漏、验证集划分、反归一化。这三件事没理顺后面所有分析都是对着一个幻觉在调参。4.1 数据泄漏是这类源码最容易翻车的地方数据泄漏这个词听着玄学实际操作里就两种来源。第一种是归一化泄漏scaler 在全量数据上fit把测试段的统计信息带进了训练。第二种是样本混排泄漏DataLoader开了shuffleTrue模型在训练时看到了未来样本。前者让验证指标偏低得离谱后者让模型在真实预测时完全失效。检查方法很简单跑通后记录训练集 MAPE 和验证集 MAPE。如果验证集比训练集还低一个量级几乎可以断定泄漏了。正确做法是跟在时间序列分析里做交叉验证的口径一致——只看过去不看未来。# 防泄漏的三个强制约定 # 1. scaler 只 fit 训练段验证/测试段只做 transform # 2. DataLoader 固定 shuffleFalse # 3. 模型训练好之后禁止再用全量数据二次 fit 任何预处理组件这三个约定写进代码注释里比写在文档里有用的多。因为跑实验的人换了一轮最容易重新引入的就是这三个问题。4.2 验证集按时间切不随机切很多图像分类项目习惯了train_test_split(random_state42)随机切分但在负荷预测里这是严重设计缺陷。负荷预测评估的是模型对不可见未来区间的外推能力不是“从全年随机挑一些日子来认”。如果验证集散落在全年各处模型其实已经见过验证集前后相邻日期的负荷形态日周期规律会帮助它“猜”出验证集的大致水平指标虚高。这份源码如果用了随机切分建议直接改成按日期切分。# 按时间顺序切分而不是随机切分 df df.sort_index() train df.loc[:2021-12-31] val df.loc[2022-01-01:2022-09-30] test df.loc[2022-10-01:] # 切完后再各自做滑窗三个区间互不重叠 X_train, y_train sliding_window(train[load].values, input_len96, pred_len24, step24) X_val, y_val sliding_window(val[load].values, input_len96, pred_len24, step24) X_test, y_test sliding_window(test[load].values, input_len96, pred_len24, step24)切分比例不用太机械。我一般习惯训练集占 60%-70%验证集 15%-20%测试集留最后 15%-20%而且测试集最好包含完整的四季或至少一个完整年度周期。只拿最后一个月做测试遇到季节性转折时会误判模型能力。4.3 预测结果还原成真实负荷值反归一化与指标计算模型输出的是归一化后的数值评估指标必须算在真实负荷的量纲上。否则你看到的 MAPE 数值完全失去业务意义也无法和调度员的经验判断对照。import numpy as np # 假设 pred_norm 是模型在测试集上的原始输出形状 (n_samples, pred_len) pred_inv scaler.inverse_transform( pred_norm.reshape(-1, 1)).reshape(-1, pred_len) true_inv scaler.inverse_transform( y_test.reshape(-1, 1)).reshape(-1, pred_len) mae np.mean(np.abs(true_inv - pred_inv)) mape np.mean(np.abs((true_inv - pred_inv) / (true_inv 1e-6))) * 100指标选择上有一条容易被忽略的规则MAPE 在凌晨低负荷时段会失控。真实负荷在深夜可能只有白天的三分之一分母变小后即使绝对误差只有几十千瓦MAPE 也会被拉得极高。所以业务评估时我更看重两个口径全天的 MAPE 用于横向对比模型晚高峰时段比如 18:00-21:00的 MAE 用于判断调度可用性。源码评估模块如果只打印一个 MAPE建议自己补上分时段 MAE——这个习惯能帮你避免被一个“看起来很合理”的数字骗过去。5. 时间序列预测源码落地的 5 个坑现象、原因与解决这部分全部来自实际跑负荷预测源码时的踩坑记录。每条按“现象 → 原因 → 解决”的顺序写可以直接对照自己跑出来的现象定位问题。5.1 坑一验证集 MAPE 异常低训练集却居高不下现象验证集 MAPE 只有 1%训练集却有 5% 以上怎么看都不合常理。模型不可能在没见过的数据上比训练集表现更好。原因90% 是归一化泄漏scaler 在切分之前 fit 了全量数据另外 10% 是DataLoader开了shuffleTrue验证样本的相似形态已经混进训练过程。解决把scaler.fit限制在训练段验证测试段只transform同时关掉 shuffle。改完后如果训练集指标下降、验证集指标轻度上升两条线回归合理范围说明之前的数字确实是假的。5.2 坑二预测曲线整体右移一个采样周期现象可视化测试集结果时预测曲线和真实负荷形状几乎一模一样但整体落后真实值一个采样点或一个周期看起来像把上一天的曲线平移到了今天。原因模型学会了复制输入曲线的尾部。在“输入 96 点、输出 24 点”的配置下日周期信号太强模型发现把输入尾端直接搬去输出就能把 loss 压得很低于是放弃了真正的外推。解决把预测值和输入序列最后 24 个点做相关性分析如果相关系数接近 0.99基本可以坐实复制行为。解决方式是换用多输出并行预测并引入外生变量温度、节假日特征打破纯周期的捷径也可以加大输出长度让模型没法直接平移。5.3 坑三节假日当天误差暴涨周五预测周一失效现象平时 MAPE 稳定在 3% 左右春节、国庆当天误差冲到 20%周五训练好的模型预测下周一形态完全错位。原因训练样本里普通工作日占绝大多数模型没见过节假日的负荷形态也学不会“节前工作日”和“节后首个工作日”之间的转移规律。解决构造节假日 one-hot 特征拼进输入而不是只喂负荷如果数据量允许把去年同时段的节假日样本复制进训练集增强。同时建议按“工作日 / 休息日 / 节日”三组分别评估 MAPE单独看节日误差而不是被平时的高分掩盖。5.4 坑四测试集第一段预测离谱过几个点才恢复正常现象预测起点附近误差极大向前推几步后逐渐恢复正常每个测试序列的开头都有一小段“歪曲线”。原因LSTM 的隐层状态初始化为零模型在前几个时间步处于预热状态。负荷序列这种强周期输入会放大预热偏差导致起点附近的输出明显偏离。解决做 warm-up用预测起点之前的真实数据先过一遍模型再开始预测。具体做法是把预测起点的位置向后挪让模型先用预测开始前 96 个点的真实序列跑一次前向得到有意义的隐层状态再从这个状态开始滚动输出。这块代码在源码里常常是缺失的需要自己补。5.5 坑五loss 长时间不降训练曲线像平台现象训练 30 个 epochloss 几乎不动或者在一个窄幅区间里来回锯齿震荡。原因学习率偏大且在滑窗长度较长时梯度穿过过多时间步更新方向互相抵消。如果输入从 96 点加到 192 点后出现同样的平台基本可以判断是滑窗过长放大了梯度不稳定性。解决先把学习率降一个数量级到 1e-4观察曲线是否脱离平台如果还是不动把输入从 96 降到 48 做对照实验确认问题出在序列长度而不是模型结构。6. 把这份源码用出自己的模型滚动预测与误差边界自查源码跑通后最终要落到真实业务用法上不是一次性预测某个固定区间而是每天滚动更新。下面是一段最常用的滚动预测写法它模拟了调度员每天拿到前一天真实负荷后对未来几天的预测过程。def rolling_predict(model, history, n_days, warm_up96, pred_len24): 滚动预测未来 n_days 天。 history: 已归一化的历史负荷序列长度至少 warm_up input_len warm_up: 让 LSTM 隐层进入稳定状态的预热点数 model.eval() with torch.no_grad(): # 先用真实历史做 warm-up防止隐层状态从零开始 _ model(torch.tensor(history[:warm_up]).unsqueeze(0)) history history[warm_up:] preds [] for _ in range(n_days): input_seq history[-96:].reshape(1, 96, 1) pred model(torch.tensor(input_seq, dtypetorch.float32)).squeeze(0).numpy() preds.append(pred) # 把今天的预测值接回历史作为明天的输入 history np.concatenate([history, pred]) return np.concatenate(preds)滚动预测有一个必须心里有数的边界误差会随预测天数累积因为每一步的预测残差都会作为下一步的输入。预测未来一天时模型输出基本可信预测到第三天、第四天曲线往往会越来越平滑、峰谷被磨平。这不算模型故障而是递归结构的固有误差传导。对自己训练好的模型做误差边界自查时我只看三件事。一是预测曲线有没有跟住晚高峰的峰谷位置——峰和谷的相位对齐了说明模型学到了日周期只对齐幅度不对齐时间说明周期相位特征还没学好。二是凌晨低负荷时段的 MAE 是否在业务可接受范围内——这里是 MAPE 的盲区必须单独量。三是一周内是否出现连续多天同方向的系统性偏移——如果连着三天预测都偏高不是巧合多半是模型对最近负荷趋势欠拟合需要把近期样本的权重加大或者把滑窗重心向近期偏移。我最早跑这种负荷预测源码时拿到的第一天指标漂亮得不正常验证集 MAPE 低到 1% 以下。排查了两天最终发现是 scaler 把测试段的统计信息也拟合了进去。那次翻车之后我给自己立了条规矩不管拿到谁的代码先检查验证集怎么切、归一化只拟合了谁、反归一化还原回来是什么形状再去动模型结构。这三个问题想清楚了模型调参才有意义。希望帮到你。本文还有配套的精品资源点击获取