电力负荷预测深度学习实战:LSTM/GRU/Transformer 七模型源码与避坑指南

发布时间:2026/10/12 18:14:24
电力负荷预测深度学习实战:LSTM/GRU/Transformer 七模型源码与避坑指南
简介基于深度学习算法实现电力负荷时间序列未来预测的Python项目源码面向计算机、数据科学、人工智能等专业在校生与从业者既可作课程设计、毕业设计也适合深度学习时序建模入门与实践。代码包含LSTM、GRU、Transformer、ARIMA、KNN、RF、DT等多种可对照模型LSTM、GRU等神经网络方法与ARIMA、KNN等传统方法并置便于观察不同算法在负荷预测中的效果差异同时配备详细注释覆盖数据预处理、特征构造、训练与预测流程。压缩包共20个文件其中16个Python脚本为主力实现2个TXT说明文件辅助运行要点2个Markdown文档梳理项目结构整体约43KB轻量易用。项目代码完整且已验证可稳定运行已有266人学习下载既适合从基线对比到调参优化的系统学习也可按需二次开发扩展负荷预测功能。注意解压后使用英文路径运行避免中文路径引发解析问题。1. 电力负荷预测的深度学习实战这份带注释的 Python 源码到底怎么用电力负荷预测是典型的时序预测问题而深度学习在这个领域已经成了绝对主力。这份基于 LSTM、GRU、Transformer、ARIMA、KNN、决策树、随机森林的 Python 源码包覆盖了从统计模型到深度模型的完整对比路线每个模型都有详细中文注释非常适合毕业设计、课程设计以及想快速上手时序预测的从业者。我拆过不少类似的项目这套代码的完整度确实值得认真过一遍——它不是只给你一个模型而是把七种算法的实现、数据预处理和结果对比都串在了一起等于把一条完整的实验链路摆在你面前。很多做毕设的同学上来就想跑 Transformer但实际落地时 LSTM 和 GRU 才是首选——参数量小、收敛快、对电力负荷这种周期性强的数据特别友好。这个项目的高明之处就在于它把 ARIMA 这种传统统计模型和深度学习模型放在同一个框架里让你能亲手验证为什么深度学习在长序列预测上更占优势。适合的人群很明确正在做毕设或课设的在校生想系统了解时序预测算法对比的入门开发者以及需要一套完整代码做基线方案的工程师。接下来我会按数据准备、模型实现、实验对比到避坑指南的顺序把这套代码的使用逻辑完整拆开。2. 数据准备与预处理时序预测的地基决定模型上限2.1 训练集划分的关键不能随机打乱必须按时间顺序切时序预测和普通的分类回归最大的区别在于数据的时间依赖性。普通的机器学习任务里数据之间是独立的可以随机打乱后划分训练集和测试集但电力负荷数据是按小时或分钟采样的连续序列如果把明天上午九点的数据混进训练集再用今天上午九点的数据做预测模型等于偷看了答案。这个项目里最值得先研究的就是它的数据集划分逻辑。我一般会这样加载电力负荷数据并做最基本的划分import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler # 加载电力负荷数据假设有两列时间戳和负荷值 df pd.read_csv(load_data.csv, parse_dates[timestamp], index_coltimestamp) load_values df[load].values.reshape(-1, 1) # 按时间顺序划分绝对不能随机打乱 train_size int(len(load_values) * 0.8) train_data load_values[:train_size] test_data load_values[train_size:] # 归一化必须用训练集的参数去转换测试集防止信息泄露 scaler MinMaxScaler(feature_range(0, 1)) train_scaled scaler.fit_transform(train_data) test_scaled scaler.transform(test_data)这里的核心是scaler.fit_transform(train_data)和scaler.transform(test_data)的配合——fit_transform在训练集上计算均值和极差而transform直接套用训练集的参数。如果你对训练集和测试集分别调用fit_transform训练集和测试集的分布会不一致模型在测试集上的表现会失真。参数方面feature_range(0, 1)是 LSTM 这类网络最常用的归一化区间因为tanh或sigmoid激活函数的输出范围刚好落在这个区间附近梯度传播更稳定。2.2 滑动窗口的构建输入长度如何决定预测精度滑动窗口是时序预测的核心机制。它的含义是用过去 N 个时间点的负荷值去预测未来 M 个时间点的值。N 就是窗口大小M 是预测步长。窗口太短模型看不到足够的周期性规律窗口太长输入维度增加导致训练变慢而且过远的负荷数据对预测当前时刻的贡献会衰减。这个项目里不同模型共用一个数据构建逻辑通常长这样def create_sequences(data, lookback24, forecast_horizon1): X, y [], [] for i in range(lookback, len(data) - forecast_horizon 1): X.append(data[i - lookback:i, 0]) y.append(data[i:i forecast_horizon, 0]) return np.array(X), np.array(y) # 电力负荷数据通常按小时采样lookback24 表示用过去 24 小时预测未来 1 小时 lookback 24 forecast_horizon 1 X_train, y_train create_sequences(train_scaled, lookback, forecast_horizon) X_test, y_test create_sequences(test_scaled, lookback, forecast_horizon) # LSTM 要求输入格式为 (样本数, 时间步长, 特征维度) X_train X_train.reshape((X_train.shape[0], X_train.shape[1], 1)) X_test X_test.reshape((X_test.shape[0], X_test.shape[1], 1))这里create_sequences函数返回的每个样本是一个长度为lookback24的向量代表连续 24 小时的负荷曲线。forecast_horizon1表示预测下一个时间点。如果你要预测未来 24 小时就把forecast_horizon改为 24但要注意输出层结构和损失函数要同步调整。电力负荷数据有很强的日周期性——早晚高峰、午间低谷——窗口取 24 能让模型看到完整的日周期这是我觉得这个参数设置比较合理的原因。2.3 项目里数据集通用处理流程梳理这个项目的数据处理代码分布在各个模型文件中但核心逻辑是一致的先读 CSV 或 Excel 格式的负荷数据按时间排序然后做归一化和滑动窗口切分。项目里标注了数据集格式和字段含义如果你的数据是别的格式需要先做字段映射。我拆项目时习惯先画一张数据流转图原始负荷序列 → 归一化 → 滑窗切片 → 划分训练测试 → 按模型要求 reshape → 送入模型训练。这个流程看着简单但每一步都有坑。比如 reshape 这一行LSTM 要求(samples, timesteps, features)三维输入而普通 Dense 层要求二维输入如果模型报维度错误百分之九十九是这一步出了问题。还有一个常见问题是数据里的缺失值——电力负荷数据经常出现采集中断最简单的做法是线性插值df[load] df[load].interpolate(methodlinear)3. 深度模型实战LSTM、GRU 与 Transformer 的实现和调参3.1 LSTM 模型搭建记忆单元如何抓住负荷的周期性规律LSTM长短期记忆网络是电力负荷预测中最常用的深度模型它的核心在于引入了门控机制——遗忘门、输入门和输出门——让网络能够选择性地记住或丢弃历史信息。电力负荷数据有很强的周期性LSTM 的遗忘门能自动学会在工作日和非工作日之间切换记忆策略这是普通 RNN 做不到的。项目里的lstm_.py结构一般按三层 LSTM 堆叠加 Dense 输出层搭建核心代码如下from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping model Sequential() # 第一层 LSTM返回完整序列供下一层使用 model.add(LSTM(units64, return_sequencesTrue, input_shape(X_train.shape[1], X_train.shape[2]))) model.add(Dropout(0.2)) # 第二层 LSTM返回最后一个时间步的输出 model.add(LSTM(units32, return_sequencesFalse)) model.add(Dropout(0.2)) # 输出层预测值通常为单变量所以 units1 model.add(Dense(units1)) model.compile(optimizeradam, lossmse, metrics[mae]) # 早停机制验证集 loss 连续 10 个 epoch 不再下降就停止训练 early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) history model.fit( X_train, y_train, validation_split0.1, epochs100, batch_size32, callbacks[early_stop], verbose1 )参数说明units64是第一层 LSTM 的记忆单元数代表网络对序列特征的抽象能力。单元数越大拟合能力越强但过拟合风险也会增加。return_sequencesTrue表示把每个时间步的隐藏状态都传给下一层最后一层 LSTM 设为return_sequencesFalse只输出最后一个时间步的结果然后接 Dense 层。Dropout(0.2)随机丢弃 20% 的神经元连接这里的取值我一般先试 0.2如果过拟合再往 0.3、0.4 调。patience10的含义是模型连续 10 个 epoch 验证集没有改善就停止这比硬跑 100 轮省时得多。3.2 GRU 的轻量化优势为什么说它的效果与 LSTM 相当但更省资源GRU门控循环单元是 LSTM 的一种简化变体把遗忘门和输入门合并成了更新门参数量大约减少三分之一。在电力负荷这种中等规模的数据集上GRU 的效果和 LSTM 基本持平但训练速度更快。如果你的机器没有 GPU或者训练时间紧张GRU 是更好的起点。from tensorflow.keras.layers import GRU model_gru Sequential() # GRU 与 LSTM 输入格式相同但参数量更少 model_gru.add(GRU(units64, return_sequencesTrue, input_shape(X_train.shape[1], X_train.shape[2]))) model_gru.add(Dropout(0.2)) model_gru.add(GRU(units32, return_sequencesFalse)) model_gru.add(Dropout(0.2)) model_gru.add(Dense(units1)) model_gru.compile(optimizeradam, lossmse, metrics[mae])对比两个模型的参数量GRU 的参数量大约是 LSTM 的 75% 到 80%。这个差距在单层时看着不大但叠到三层之后差别就很明显。实际项目中我一般会先用 GRU 跑通整个流程再用 LSTM 做精调——因为 GRU 训练一轮的时间更短调参反馈更快。3.3 Transformer 的时序预测实现位置编码填补了顺序缺失Transformer 本来是 NLP 领域的模型但用在时序预测上也很有潜力项目里的transformer_.py把它迁移到了电力负荷场景。Transformer 的核心是自注意力机制能直接建模任意两个时间点之间的依赖关系比 LSTM 的链式结构看得更远。但它有一个关键差异注意力机制本身对位置不敏感必须加位置编码Positional Encoding来注入时序信息。Transformer 在时序预测中的结构通常是 Encoder 加 Decoder 或者更简化的 Encoder 加全连接输出。对于单变量负荷预测我习惯用 Encoder 加 Dense 层的简化结构import tensorflow as tf from tensorflow.keras.layers import MultiHeadAttention, LayerNormalization, GlobalAveragePooling1D def transformer_encoder(input_shape, head_size8, num_heads4, ff_dim128, dropout0.1): inputs tf.keras.Input(shapeinput_shape) # 自注意力层多头注意力捕捉序列内部关系 attention_output MultiHeadAttention( num_headsnum_heads, key_dimhead_size )(inputs, inputs) attention_output Dropout(dropout)(attention_output) # 残差连接 层归一化稳定训练 norm1 LayerNormalization(epsilon1e-6)(inputs attention_output) # 前馈网络 ff_output Dense(ff_dim, activationrelu)(norm1) ff_output Dense(input_shape[-1])(ff_output) norm2 LayerNormalization(epsilon1e-6)(norm1 ff_output) return norm2 def create_transformer_model(lookback, features1): inputs tf.keras.Input(shape(lookback, features)) # 位置编码让模型知道时间顺序 pos_enc tf.range(lookback, dtypetf.float32)[:, None] / tf.pow(10000.0, tf.range(0, features, 2, dtypetf.float32) / features) pos_enc tf.tile(pos_enc[None, :, :], [1, 1, features])[: , :, :1] x inputs pos_enc x transformer_encoder(x.shape[1:]) x GlobalAveragePooling1D()(x) x Dense(32, activationrelu)(x) outputs Dense(1)(x) model tf.keras.Model(inputs, outputs) model.compile(optimizeradam, lossmse, metrics[mae]) return model这段代码里的位置编码是简化版实现实际上更标准的做法是使用正弦和余弦的交替编码。MultiHeadAttention的key_dim8控制了每个注意力头的维度num_heads4表示 4 个注意力头并行计算。Transformer 在数据量特别大或者序列特别长的时候优势明显但如果你的训练数据只有几千条LSTM 和 GRU 的效果反而可能更好——这是选型时要权衡的地方。4. 传统模型与机器学习对比ARIMA、KNN、决策树、随机森林的定位4.1 ARIMA 的差分逻辑非平稳序列如何变成平稳序列ARIMA 是统计时序预测的经典模型全称是自回归积分滑动平均模型。它的核心思想是把非平稳序列通过差分变成平稳序列然后对平稳序列建立回归模型。电力负荷数据受温度和人类活动影响有明显的季节性和趋势性属于典型的非平稳序列所以 ARIMA 的差分阶数是必调的参数。项目里的arima_.py和arima__.py是 ARIMA 的两个版本实现核心代码逻辑如下from statsmodels.tsa.arima.model import ARIMA from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 对原始序列做一阶差分 diff1 np.diff(load_values.flatten(), n1) # 通过 ADF 检验验证平稳性 from statsmodels.tsa.stattools import adfuller adf_result adfuller(diff1) print(fADF 统计量: {adf_result[0]}, p 值: {adf_result[1]}) # p 值小于 0.05 说明差分后序列平稳 # 根据 ACF 和 PACF 图确定 p 和 q 的取值然后训练 model_arima ARIMA(load_values.flatten(), order(2, 1, 2)) model_fit model_arima.fit() # 预测未来 24 小时 forecast model_fit.forecast(steps24)ARIMA 参数(p, d, q)中p 是自回归阶数d 是差分阶数q 是滑动平均阶数。确定 p 和 q 的方法通常是画 ACF自相关函数和 PACF偏自相关函数图看截尾和拖尾特征。d1表示做了一阶差分。这里有个常见误区ARIMA 适合短期预测预测步数越长误差累积越严重所以我不建议用它预测超过 48 小时的距离但作为基线对比非常合适。4.2 KNN 在时序预测中的特殊用法用相似历史片段投票KNN 在分类任务里遍地都是但用在时序预测里很多人会觉得奇怪——其实它的原理很直观找到历史上和当前最相似的负荷曲线片段把那些片段后续的负荷值作为预测结果。如果今天是工作日的早上八点KNN 会找到过去几十年里所有工作日早上八点前后的负荷片段然后平均它们下一时刻的值作为预测。from sklearn.neighbors import KNeighborsRegressor from sklearn.metrics import mean_squared_error # KNN 回归器用相似片段做平均预测 knn_model KNeighborsRegressor(n_neighbors5, weightsdistance) knn_model.fit(X_train, y_train) # 预测并反归一化 y_pred_knn knn_model.predict(X_test) y_pred_knn_orig scaler.inverse_transform(y_pred_knn.reshape(-1, 1)) rmse_knn np.sqrt(mean_squared_error( scaler.inverse_transform(y_test[:len(y_pred_knn)].reshape(-1, 1)), y_pred_knn_orig )) print(fKNN RMSE: {rmse_knn:.4f})n_neighbors5表示查找 5 个最相似的历史片段weightsdistance表示距离更近的样本权重更高。KNN 的参数可调空间不大但它的意义在于验证一个观点如果 KNN 的效果和 LSTM 差不多说明你的数据可能没有复杂到需要上深度模型或者你喂给深度模型的特征还不够。4.3 决策树与随机森林适合做特征重要性分析不适合直接预测长序列决策树和随机森林本质上是非时序模型它们不关心数据的前后顺序。在这个项目里它们的输入是滑窗后的二维特征矩阵——即用过去 24 小时的负荷值作为 24 个特征预测下一时刻的负荷。因为特征之间没有顺序概念决策树和随机森林无法捕捉负荷曲线的时序形状效果通常不如 LSTM 和 GRU。但它们的价值在于两个方面一是跑得快几秒钟就能出结果适合做基线和最终对比实验二是能分析特征重要性比如判断一天中哪些时间点的负荷值对预测明天同时刻的负荷最重要。from sklearn.ensemble import RandomForestRegressor from sklearn.tree import DecisionTreeRegressor # 随机森林回归 rf_model RandomForestRegressor( n_estimators100, max_depth10, min_samples_split4, random_state42 ) rf_model.fit(X_train, y_train.ravel()) # 特征重要性看哪些历史时间点的负荷对预测贡献最大 feature_importance rf_model.feature_importances_ for i in range(lookback): print(f前 {lookback - i} 个时刻的重要性: {feature_importance[i]:.4f})n_estimators100表示 100 棵决策树max_depth10限制每棵树的最大深度来防止过拟合。随机森林和决策树在这个项目里更多是作为对照组的角色帮助你在论文或报告中做一个多模型效果对比表——这部分往往是毕设评分的重要依据。5. 避坑指南电力负荷预测源码复现中的六个高频翻车点5.1 路径中文导致模块加载失败现象项目解压后运行lstm_.py直接报ModuleNotFoundError或者FileNotFoundError但你确认文件都存在。原因Python 在 Windows 下对中文路径的编码支持不完善项目文件夹名或文件路径中一旦包含中文相对路径解析就会出问题。很多开源项目的load_data.csv路径是硬编码的相对路径中文父目录会导致解析失败。解决把整个项目文件夹重命名为纯英文字母比如从电力负荷预测源码改为load_forecast并且把路径调整到没有中文的目录下例如D:/projects/load_forecast/。这个操作成本最低但能排除一半以上的运行问题。5.2 数据归一化测试集参数用了独立的 fit_transform现象模型训练阶段 loss 正常下降但测试集评估效果极差RMSE 比训练集高出一个量级。原因对测试集也调用了fit_transform而不是继承训练集的transform。这样测试集的归一化分布和训练集不一致相当于训练和测试用的是两套坐标系。解决严格保证只有训练集调用fit_transform验证集和测试集都只调用transform。我习惯在代码里加一行注释标记这个坑防止过两天自己都忘了。推荐在数据切分代码处添加断言# 断言测试集归一化使用的是训练集的参数 assert np.isclose(scaler.data_min_, scaler.data_min_[0])5.3 LSTM 输入维度报错三维张量的形状问题现象运行model.fit(X_train, y_train)时报错提示Input 0 of layer lstm is incompatible with layer形状不匹配。原因LSTM 的输入必须是(样本数, 时间步数, 特征数)的三维张量。直接把二维数据喂进去就会报错这通常发生在你自己构造数据而没经过create_sequences函数的时候。解决检查X_train.shape如果输出是(样本数, 24)而不是(样本数, 24, 1)就执行X_train X_train.reshape((X_train.shape[0], X_train.shape[1], 1))。项目里DT_.py、RF_.py不要求三维输入但lstm_.py、gru_.py必须有这一步。5.4 Transformer 训练不收敛学习率和注意力头数需要同步调整现象Transformer 模型训练时 loss 剧烈震荡或始终无法降到预期范围而 LSTM 可以正常收敛。原因Transformer 对学习率非常敏感默认的learning_rate0.001可能偏大另外num_heads必须能被key_dim整除否则多头注意力的分头逻辑出错。解决把优化器换成带学习率衰减的Adam初始学习率降到0.0001试试同时确认num_heads4、key_dim8时key_dim能被num_heads整除。我在本地跑 Transformer 时为了排查这个问题加了 TensorBoard 回调观察 loss 曲线形态比只看终端输出更有价值。5.5 ARIMA 报错或预测结果为常数现象ARIMA 在部分数据上报LinAlgError或者预测出来的结果是一条水平直线。原因数据的季节性强且非平稳程度高ARIMA 模型无法捕捉日周期性预测退化为最近值的水平延伸。解决如果 ARIMA 只是作为对比基线这个结果本身也有引用价值——说明统计模型在复杂负荷数据上的局限性。但如果你想让它跑得更正常可以试试SARIMAX加入季节性参数或者先对数据做傅里叶变换再建模。项目里的arima__.py相对arima_.py的改进一般就体现在差分阶数的自适应选择上。5.6 预测结果有偏移有一个时间步的滞后现象把预测值和真实值画在同一张图里曲线形状几乎一致但预测曲线整体向右平移了一个时间步。原因模型学到的实际上是一个恒等映射——用当前的负荷值预测下一个时刻的负荷时因为负荷变化是连续的模型发现最简单的策略就是输出输入序列的最后一个值。这是时序预测的经典问题尤其在forecast_horizon1的时候。解决不要只看 RMSE要画出预测曲线和真实曲线的对比图肉眼观察滞后程度。缓解方法包括增大lookback窗口、在损失函数中加入差分惩罚项、或者改用更长的forecast_horizon做多步预测后再提取第一步结果。6. 模型效果对比与验证技巧正确评估你的预测结果六种模型的对比是这个项目的核心目标但只跑完一遍拿个 RMSE 数字交差是远远不够的。你需要一套统一的评估流程才能公平地判断哪种模型在这个数据集上真正表现更好。评估指标我建议同时看三个RMSE、MAE、MAPE。from sklearn.metrics import mean_squared_error, mean_absolute_error def evaluate_model(y_true_orig, y_pred_orig): rmse np.sqrt(mean_squared_error(y_true_orig, y_pred_orig)) mae mean_absolute_error(y_true_orig, y_pred_orig) # MAPE 需要避免除零负荷数据一般没有零值 mape np.mean(np.abs((y_true_orig - y_pred_orig) / y_true_orig)) * 100 return rmse, mae, mape # 假设 y_true_orig 和 y_pred_orig 都是反归一化后的真实负荷值 rmse, mae, mape evaluate_model( scaler.inverse_transform(y_test[:len(y_pred)]), scaler.inverse_transform(y_pred) ) print(fRMSE: {rmse:.4f}, MAE: {mae:.4f}, MAPE: {mape:.2f}%)RMSE 对大误差敏感MAE 反映平均误差水平MAPE 让你了解误差占真实值的百分比。电力负荷预测通常 MAPE 在 2% 到 5% 之间算不错。我一般会按小时和星期把测试预测结果分组看——工作日早上八点的预测误差和周末凌晨两点的预测误差完全不是一个量级只有分维度看才能找到模型的薄弱点。这个项目的文件夹里同时有项目源码提交备份和项目说明.md说明作者是有意识做了版本管理的。我实际操作的时候会把所有模型跑出来的预测结果、评估指标存成同一个格式的 CSV统一做对比表格。这份源码适合作为基础框架换成其他行业的数据集也能用——只需要替换数据加载部分和调整窗口参数。我自己拆过类似项目这类多模型对比的代码框架最大的价值在于省去了搭基准实验的大半工作量。希望这篇文章能帮你把这套源码跑顺让你把时间花在理解模型行为和分析结果上而不是耗在报错和调环境上。本文还有配套的精品资源点击获取