资讯动态

Python实现深度学习电力负荷预测:从数据处理到评估避坑

发布时间:2026/10/1 5:39:11 来源:尧图企业网站定制
简介面向电力系统与深度学习交叉领域的课程设计及期末大作业需求项目压缩包提供了一个已获导师指导并通过的97分高分完整方案。基于Python实现区域电力负荷预测模型覆盖数据预处理、特征工程、模型构建、训练评估与结果可视化等完整流程可直接运行、无需修改适合深度学习初学者及需要快速完成课设任务的高校学生。压缩包共62个文件核心代码以39个py脚本构成同时包含训练过程图表jpg/png、说明文档md以及少量编译缓存整体大小3.72MB目录结构清晰便于按模块阅读和二次开发。已有150人学习下载。从中可获取完整的模型源码、项目说明文档及效果展示图既能支撑课程设计报告的撰写也能帮助理解深度学习中时序预测任务从数据处理到模型部署的实际落地流程。1. 这个 zip 里装的不是“调参神话”而是一条可复现的预测闭环很多人拿到Python实现基于深度学习的预测区域电力负荷模型源码项目说明高分项目.zip之后第一反应都是解压、跑起来、看一眼预测曲线。真正让这类项目从课程设计变成可复现场景的其实不是某个网络结构有多深而是数据怎么切、评估怎么算、预测结果怎么回到原始负荷量纲。区域电力负荷预测是典型的时序回归问题输入历史负荷、温度、时间特征输出未来一小时或未来一天的区域总负荷。这种任务适合用 Python 做因为数据清洗、特征工程和深度学习训练都在同一个生态里。适合谁看准备做课程设计、毕业设计、AI 竞赛或者说想转入电网数据分析和时序预测方向的新手工程师。读这类项目时别一上来就调网络结构按数据、模型、评估、避坑四个层面看才能真正复现出“高分”效果。2. 先想清楚输入输出再谈深度学习数据处理与窗口构造任何一种时序预测模型在喂进网络之前都要先确定三件事预测目标是什么、预测步长是多少、输入窗口取多长。常见做法是数据按时间排序用过去一段负荷去预测未来一段负荷。区域负荷数据的粒度通常是 15 分钟、1 小时或 1 天课程设计里 90% 以上选小时级数据于是history_len168、horizon24成了最常见的配置用过去一周的规律预测未来一天。为什么是 168 而不是 24 或 72因为电力负荷有强日周期和星期周期窗口太短学不到星期规律窗口太长会引入太多低频变化训练也更慢。这不是固定答案但足够作为起点。2.1 负荷预测的输入输出单步预测与多步预测的差别单步预测只预测下一时刻负荷常用于滚动调度。多步预测一次输出未来 24 小时负荷适合日前计划。两者的模型差别不大但最后一层输出的神经元数量、损失函数、评估方式都不一样。标题里讲区域电力负荷预测更常见的是多步预测也就是每个样本的标签长度是 24而不是 1。任务输入长度输出长度典型场景单步预测168 小时1实时调度多步预测168 小时24日前计划在写代码前先把这件事定义清楚后面整个项目都围绕这个形状展开。否则会出现很尴尬的情况输入窗口和输出长度对不上训练时 loss 一直不降还以为是模型问题其实是标签构造错了。2.2 从 CSV 到训练样本时间特征、缺失值、归一化与滑窗拿到原始数据后第一步不是建模而是把 CSV 读进来按时间排序把时间特征补上。下面这段是数据处理里最常用的一小段import pandas as pd import numpy as np df pd.read_csv(data/raw/load.csv, parse_dates[timestamp]) df df.sort_values(timestamp).reset_index(dropTrue) # 负荷连续变化用线性插值比直接删除更稳 df[load] df[load].interpolate(methodlinear, limit_directionboth) hour df[timestamp].dt.hour df[hour_sin] np.sin(2 * np.pi * hour / 24) df[hour_cos] np.cos(2 * np.pi * hour / 24) weekday df[timestamp].dt.weekday df[weekday_sin] np.sin(2 * np.pi * weekday / 7) df[weekday_cos] np.cos(2 * np.pi * weekday / 7)这里要解释一下为什么不用df[hour]这个整数。模型会把 23 点和 0 点看成完全不相邻的两个数但真实世界它们只差一小时。把小时转成sin/cos编码后周期距离会被拉近模型更容易理解“每天凌晨是一个低谷”这种规律。星期维度同理。接下来是构造滑窗。滑窗的意思是从连续的特征序列里切出一段过去配上一段未来组成(X, y)样本def make_sequences(data_x, data_y, history_len168, horizon24): X, y [], [] for i in range(len(data_x) - history_len - horizon 1): X.append(data_x[i : i history_len]) y.append(data_y[i history_len : i history_len horizon]) return np.array(X), np.array(y)data_x的形状是(总步数, 特征数)data_y是一维数组取未来 24 个时刻的负荷值。输出X的形状是(样本数, 168, 特征数)y的形状是(样本数, 24)。如果数据量大这个 Python 循环会比较慢可以用numpy.lib.stride_tricks.sliding_window_view优化但作为源码项目先用循环保证可读性。2.3 切分原则为什么时序数据不能随机打乱时序预测和图像分类最本质的区别就是不能随机切分。随机切分会让训练集里出现未来数据模型无形中学会了“偷看答案”验证集指标非常漂亮换一段新数据就崩。切分时要按时间先后切成连续块并且只允许用前 70%、中间 15%、最后 15% 这种方式n_train int(len(df) * 0.7) n_val int(len(df) * 0.85) train_df df.iloc[:n_train].copy() val_df df.iloc[n_train:n_val].copy() test_df df.iloc[n_val:].copy() x_scaler MinMaxScaler() y_scaler MinMaxScaler() x_scaler.fit(train_df[feature_cols]) y_scaler.fit(train_df[[load]]) train_x x_scaler.transform(train_df[feature_cols]) val_x x_scaler.transform(val_df[feature_cols]) test_x x_scaler.transform(test_df[feature_cols]) train_y y_scaler.transform(train_df[[load]]).ravel() val_y y_scaler.transform(val_df[[load]]).ravel() test_y y_scaler.transform(test_df[[load]]).ravel() X_train, y_train make_sequences(train_x, train_y) X_val, y_val make_sequences(val_x, val_y) X_test, y_test make_sequences(test_x, test_y)这里的x_scaler和y_scaler是分开的。x_scaler管输入特征y_scaler管负荷目标。后面评估时需要y_scaler.inverse_transform把预测值变成真实的 MW 数值。如果样本量很小也可以用 scikit-learn 的TimeSeriesSplit做多折验证from sklearn.model_selection import TimeSeriesSplit tscv TimeSeriesSplit(n_splits5) for train_idx, test_idx in tscv.split(df): print(ftrain: {len(train_idx)}, test: {len(test_idx)})需要注意最后一层“时间切分”时最好按天切不要按小时硬切。比如原始数据是小时级把n_train int(len(df) * 0.7)可能把某个周二切到一半导致训练集和验证集都有半个周二周期特征被割裂。稳妥做法是先按天分组再按天数比例切。3. 模型主体为什么选 LSTM结构、参数和评估指标区域负荷预测的源码里最常出现的深度学习模型就是 LSTM 和 GRU。原因不是 LSTM 在所有时序任务上都最厉害而是这个任务本身有很清楚的序列结构负荷每天一个周期每周一个周期LSTM 的隐藏状态正好能跨时间步记住这些模式。相比 TransformerLSTM 在小规模数据上更容易收敛也更适合课程设计和入门工程复现。3.1 负荷序列的周期性决定了什么网络更合适电力负荷有日周期、周周期还有节假日和天气带来的突发变化。如果只看当天数据一个简单机器学习模型也能做但要预测未来 24 小时模型必须理解“今天周五晚上和上周五晚上的规律更接近而不是和周三下午更接近”。LSTM 的门控机制能做到这一点输入门决定记什么遗忘门决定丢什么这样一个 168 步的输入窗口不会被过长的历史冲散。GRU 比 LSTM 少一个门参数更少训练更快效果在中等数据量下差别不大。如果项目里出现 GRU没必要觉得比 LSTM 低一档它只是用更少的显存换来了相近的效果。TCN 这类时序卷积网络也很稳定但调参和 debug 门槛比 LSTM 高一些。要是项目标题只写了“深度学习”最稳妥的实现就是 LSTM 加一到两层全连接这也是最容易让读者跟着复现的选择。3.2 model 结构两层 LSTM 加回归头的参数细节一个小而完整的负荷预测模型可以写成这样import torch.nn as nn class LoadForecastLSTM(nn.Module): def __init__(self, n_features, hidden_size64, num_layers2, horizon24, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizen_features, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout ) self.regressor nn.Sequential( nn.Linear(hidden_size, hidden_size // 2), nn.ReLU(), nn.Dropout(dropout), nn.Linear(hidden_size // 2, horizon) ) def forward(self, x): out, _ self.lstm(x) return self.regressor(out[:, -1, :])这里最容易被新手忽略的是batch_firstTrue。PyTorch 的 LSTM 默认输入形状是(seq_len, batch, n_features)不设置batch_first的话代码里喂进去的(batch, seq_len, n_features)就会被理解错轻则报维度错重则能跑但结果很奇怪。所以只要数据形状是(样本数, 时间步, 特征数)就必须加这个参数。num_layers2表示两层 LSTM 堆叠。第一层更关注短周期规律第二层在高层抽象上捕捉星期和节假日的残差。hidden_size64是折中值。样本数只有几千时64 已经足够如果数据量到几十万可以把hidden_size加到 128但也要配合更大的dropout不然验证集 loss 会先降后升。取out[:, -1, :]是拿最后一个时间步的隐藏状态作为整段序列的总结。这个操作是合理的因为 LSTM 已经把所有历史信息压进了最后一步的隐藏状态。再通过回归头输出 24 个值就是未来 24 小时的负荷预测。3.3 损失函数与指标MAPE 的注意点回归任务可以直接用 MSE但 MSE 对大偏差特别敏感会逼着模型去追峰值牺牲掉平稳段的精度。负荷数据里有明显的早晚高峰一个峰值点的偏差会把 loss 拉得很大。更稳的做法是用 HuberLoss也叫 Smooth L1 Loss它对小偏差用平方、大偏差用绝对值不容易被极端点带偏criterion nn.HuberLoss(delta1.0)delta1.0是在归一化之后的数据尺度上设的。如果样本缩放到了 0~1 区间1.0 算一个比较合理的分界线。如果直接用原始 MW 做训练delta要调成负荷峰值的 1% 左右。评估指标则要在原始尺度上算。很多高分项目说明书里写 MAPE 小于 3%但读者复现时经常会发现 MAPE 超过 20%。问题不一定在模型而在指标计算方式。深夜负荷接近零哪怕绝对误差只有 0.5 MWMAPE 也会被放大到离谱。所以评估时最好带一个阈值过滤def compute_metrics(y_true_raw, y_pred_raw, low_threshold0): rmse float(np.sqrt(np.mean((y_true_raw - y_pred_raw) ** 2))) mae float(np.mean(np.abs(y_true_raw - y_pred_raw))) if low_threshold and low_threshold 0: mask y_true_raw low_threshold else: mask np.ones_like(y_true_raw, dtypebool) mape float(np.mean(np.abs((y_true_raw - y_pred_raw)[mask] / (y_true_raw[mask] 1e-6))) * 100) return {rmse: rmse, mae: mae, mape: mape}指标侧重适合场景注意MSE放大异常偏差希望惩罚峰值偏大时数值被平方不直观MAE平均绝对误差普通误差报告无法反映大偏差MAPE相对误差报告给业务方低谷负荷容易爆表4. 用 PyTorch 跑通训练闭环项目结构、训练循环与评估拿到 zip 解压后不要急着运行入口脚本。先看目录里的 README 和数据文件再把代码按职责拆开。一个能稳定复现的负荷预测项目通常会按数据、模型、训练、评估四部分组织load_forecast/ ├── data/raw/load.csv ├── src/ │ ├── model.py │ ├── dataset.py │ ├── train.py │ └── evaluate.py └── README.md依赖通常就是pandas、numpy、scikit-learn、torch这几个先用 pip 把环境装好pip install pandas numpy scikit-learn torch有了环境之后整个闭环分三步走把数据做成 Dataset把模型接上训练循环最后在测试集上反归一化计算指标。4.1 Dataset 与 DataLoader 的写法用 PyTorch 时把之前生成好的X_train、y_train包进 Dataset。这里不需要重写滑窗逻辑直接传张量就行from torch.utils.data import Dataset, DataLoader class LoadDataset(Dataset): def __init__(self, X, y): self.X torch.tensor(X, dtypetorch.float32) self.y torch.tensor(y, dtypetorch.float32) def __len__(self): return len(self.X) def __getitem__(self, idx): return self.X[idx], self.y[idx] train_dataset LoadDataset(X_train, y_train) val_dataset LoadDataset(X_val, y_val) test_dataset LoadDataset(X_test, y_test) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) val_loader DataLoader(val_dataset, batch_size128, shuffleFalse) test_loader DataLoader(test_dataset, batch_size128, shuffleFalse)训练集可以shuffleTrue因为每个样本本身已经是按时间滑窗切出来的打乱样本顺序不会把未来数据混进历史窗口反而能让每个 epoch 的更新顺序更稳定。验证集和测试集必须shuffleFalse不然后面画时序曲线时顺序是乱的评估结果也没法对齐时间点。4.2 训练循环与早停回调训练循环里最重要的不是花哨的学习率策略而是梯度和早停。LSTM 反向传播容易出现梯度爆炸输入数据稍微没处理好loss 就会变 NaN。最常见的手段是梯度裁剪import torch import torch.nn as nn model LoadForecastLSTM( n_featuresX_train.shape[2], hidden_size64, num_layers2, horizony_train.shape[1], dropout0.2, ) optimizer torch.optim.AdamW(model.parameters(), lr1e-3) criterion nn.HuberLoss(delta1.0) best_val_loss float(inf) patience 10 bad_epochs 0 max_epochs 60 def evaluate(model, loader, criterion): model.eval() total_loss 0.0 with torch.no_grad(): for X_batch, y_batch in loader: pred model(X_batch) total_loss criterion(pred, y_batch).item() * len(X_batch) return total_loss / len(loader.dataset) for epoch in range(1, max_epochs 1): model.train() train_loss 0.0 for X_batch, y_batch in train_loader: optimizer.zero_grad() loss criterion(model(X_batch), y_batch) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() train_loss loss.item() * len(X_batch) train_loss / len(train_loader.dataset) val_loss evaluate(model, val_loader, criterion) print(fepoch {epoch:02d} | train_loss {train_loss:.4f} | val_loss {val_loss:.4f}) if val_loss best_val_loss: best_val_loss val_loss bad_epochs 0 torch.save(model.state_dict(), best_model.pt) else: bad_epochs 1 if bad_epochs patience: print(early stop) break这段代码里max_norm1.0是对整个模型参数做梯度裁剪防止梯度爆炸。学习率从1e-3起步是安全的。如果训练集只有几千个窗口把hidden_size降到 32dropout调到 0.3否则很容易过拟合。早停保存的是验证集 loss 最低的那份权重而不是最后一轮的权重。这一点很关键不然后面你可能拿着一个已经过拟合的模型去做评估。4.3 评估脚本在原始尺度上算 RMSE 和 MAPE评估时最容易被忽视的一步是把归一化后的预测值逆变换回原始负荷尺度。很多项目里模型输出的是一个 0~1 的小数直接拿去和真实 MW 对比RMSE 小得惊人但没有任何业务意义model.load_state_dict(torch.load(best_model.pt)) model.eval() preds, trues [], [] with torch.no_grad(): for X_batch, y_batch in test_loader: preds.append(model(X_batch).numpy()) trues.append(y_batch.numpy()) preds np.concatenate(preds, axis0) trues np.concatenate(trues, axis0) preds_raw y_scaler.inverse_transform(preds) trues_raw y_scaler.inverse_transform(trues) metrics compute_metrics(trues_raw, preds_raw, low_threshold5) print(metrics)前提是y_scaler还在。项目运行完不能只存模型权重还应该把y_scaler和x_scaler用pickle或joblib存一份。很多“高分项目”源码里只给了.pt文件没有 scaler导致别人复现时预测值全在 0~1 之间根本不知道真实负荷是多少。这个坑几乎每个后来者都会踩一次。5. 避坑与排查让预测结果不再好看但没法用模型跑通不难难的是评估结果在换数据、换时间段之后还能立住。下面这五条是我在处理这类源码项目时经常看到的共性问题按“现象 → 原因 → 解决”写清楚。5.1 归一化泄漏验证集指标像“作弊”现象验证集 RMSE 很低MAPE 不到 2%但把模型拿到新的一个月数据上预测误差突然翻倍。原因有人把全量数据的 MinMaxScaler 写在了切分之前。fit的时候已经看到了验证集和测试集的最小值、最大值等于让模型提前知道了数据范围。解决缩放器只能在训练集上fit验证集和测试集只做transform而且要在滑窗之前完成。x_scaler.fit(train_df[feature_cols]) val_x x_scaler.transform(val_df[feature_cols]) test_x x_scaler.transform(test_df[feature_cols])不要在df整体上先fit_transform再切分这是这个坑里最常见的错误代码。5.2 测试集 DataLoader shuffleTrue现象预测曲线和真实曲线画出来是一堆乱序点看不出早晚高峰但数值误差却不大。原因测试 DataLoader 里开了shuffleTrue每个 batch 的样本顺序被打乱画图时没有按时间对齐。解决测试时shuffleFalse并且把每个样本对应的起始时间保存下来。test_loader DataLoader(test_dataset, batch_size128, shuffleFalse)绘图时用pd.date_range对每个样本重建未来 24 小时的时间索引否则曲线完全没法看。5.3 深夜低负荷让 MAPE 失真现象总 MAPE 只有 3%拆到凌晨 2 点到 4 点MAPE 变成 40%业务方觉得这个模型是“假的”。原因凌晨实际负荷很小哪怕绝对误差只有几兆瓦相对误差也会被放大。解决不只看总 MAPE按时间分时段报告。hour_of_day pd.to_datetime(trues_time).hour peak_mask (hour_of_day 8) (hour_of_day 22) offpeak_mask ~peak_mask mape_peak np.mean(np.abs((trues_raw[peak_mask] - preds_raw[peak_mask]) / (trues_raw[peak_mask] 1e-6))) * 100或者直接在compute_metrics里加low_threshold把低于某个负荷阈值的样本过滤掉。这个阈值一般取峰值的 5%~10%。5.4 多步预测误差累积现象一次输出未来 24 小时前 6 个小时误差小后 18 个小时误差越来越大曲线越来越平。原因训练时每个输入窗口里都是真实历史值但推理时如果把模型输出再拼回窗口误差会一步一步累积。更隐蔽的问题是有些源码在测试时把真实未来值当成输入这相当于 teacher forcing预测出来的是“插值结果”而不是未来预测。解决单独做一次纯自回归滚动预测把模型每步输出拼到窗口末尾继续预测下一步看每一步的真实误差。5.5 训练 loss 变成 NaN 或一直不降现象第一个 epoch loss 很大第二个 epoch 变成 NaN或者 loss 一直在 0.7 左右几乎不动。原因输入数据里有 NaN、学习率太大、梯度爆炸、目标值没有归一化。解决先检查df.isna().sum()再确认训练数据里没有 inf然后把学习率降到3e-4加上梯度裁剪。如果 loss 卡住不动检查模型输出层是不是加了sigmoid。回归任务的输出层不应该加激活函数加了就相当于把负荷预测限制在 0~1 之间天然不合理。6. 再往前走一步滚动预测验证与分时段误差分析模型在测试集上输出 24 小时预测平均误差看着不错但真正部署时往往需要不断把预测结果滚进输入窗口。所以我的习惯是最后补一个滚动预测脚本专门用来检查预测误差的累积速度。下面这段代码取测试集的第一个窗口滚动预测未来 48 小时history_len X_test.shape[1] initial_window test_x[:history_len].copy() future_feats test_x[history_len:history_len 48] load_col feature_cols.index(load) input_window initial_window.copy() preds_norm [] model.eval() with torch.no_grad(): for step in range(48): x torch.from_numpy(input_window).float().unsqueeze(0) pred model(x)[0, 0].item() preds_norm.append(pred) input_window np.roll(input_window, shift-1, axis0) input_window[-1, :] future_feats[step] input_window[-1, load_col] pred preds_raw y_scaler.inverse_transform(np.array(preds_norm).reshape(-1, 1)) trues_raw y_scaler.inverse_transform(test_y[:48].reshape(-1, 1))这里每一轮只取模型输出的第一步而不是把 24 个预测值一次性加入窗口。因为实际业务中你只有第一个预测值能在下一个时刻变成新的历史后 23 个都是待验证的预判。这样滚动出来的误差曲线才和真实部署流程一致。跑完之后把 48 个误差点按小时拆开看会发现一个现象白天高峰段的误差通常大于深夜但如果深夜 MAPE 反而更高那说明预测值存在系统性的相位偏移。相位偏移经常来自时间特征没有加入星期信息或者历史窗口太短。这种判断比盯着一个总 MAPE 更有用。我也会在项目最开始固定随机种子np.random.seed(0) torch.manual_seed(0)不然同一份代码在不同机器上跑出来的曲线不一样后面排查问题很难回放。现在每轮实验我都会把跑批记录、最优权重、scaler 文件和分时段指标一起存下来不草率对一个 MAPE 下结论。这个习惯帮我少走了很多弯路希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑