资讯详情

基于PyTorch的LSTM电力负荷预测实战:从数据构造到模型调优

📅 2026/10/8 11:26:15 | 华诺云谱 👁 阅读
基于PyTorch的LSTM电力负荷预测实战:从数据构造到模型调优
简介这份资源面向深度学习入门者与电力系统数据分析人员提供一套基于PyTorch实现LSTM电力负荷预测的完整源码与项目说明帮助读者掌握序列建模从数据预处理到结果可视化的全流程。压缩包共14个文件约2.1MB以5个py脚本、5个csv数据集为主另含pt模型权重、md说明文档及缓存文件分别对应训练、测试、数据处理与模型保存等环节。目前已有156人学习。项目按模块拆分数据清洗与归一化、LSTM网络搭建、损失函数与优化器选择、MAE/MSE/R²评估及预测曲线可视化说明文档对整体架构与代码实现逐层解读便于在此基础上调整隐藏层单元数、层数与学习率等超参数快速复现并优化负荷预测效果。1. 电力负荷预测为什么用 LSTM从「昨天几点用电」到「明天几点该发电」电力负荷预测这件事说白了就是让调度侧提前知道明天、下周甚至下个月每个时段大概要用多少电。传统做法靠历史同期均值、线性回归、ARIMA 这类统计模型遇到工作日/周末切换、节假日、极端气温就明显吃力。负荷曲线本身有强周期性日周期、周周期又有突发扰动降温、停产、检修本质是一个带长记忆的非线性时间序列问题。LSTM 的门控结构能选择性记住几十个时间步之前的信息又不会像普通 RNN 那样梯度消失所以在这类任务上成了工程里最常见的基线模型。这篇笔记就围绕「基于 PyTorch 实现一个简单的 LSTM 模型做电力负荷预测」这条线把数据怎么造、模型怎么搭、训练怎么调、坑在哪一步步讲清楚新手能照着跑通熟手能看到参数边界。2. 数据准备与滑动窗口构造把一张负荷表变成监督学习样本2.1 电力负荷数据的典型形态与清洗要点真实负荷数据一般来自 SCADA 或电表采集常见格式是「时间戳 有功功率」采样间隔 15 分钟、30 分钟或 1 小时。做 LSTM 之前先要把它整理成等间隔序列。常见坑是缺值和异常值采集断点会留下空行传感器故障会留下 0 或明显偏离的量级。我一般按下面顺序处理统一时间索引用pandas.date_range重采样到固定频率缺失点先标记为 NaN。对短缺口连续 13 个点用线性插值对长缺口不要硬插直接截断成两段样本。异常值用滑动中位数加 MAD 判定超过阈值就替换成 NaN 再插值。最后做归一化MinMax 到 [0,1] 或按均值方差标准化训练集统计量要保存下来给推理用。import pandas as pd import numpy as np # 读取原始负荷表假设两列timestamp, load df pd.read_csv(load.csv, parse_dates[timestamp]) df df.set_index(timestamp).sort_index() # 重采样到 1 小时缺失标记 NaN df df.resample(1h).mean() # 短缺口线性插值长缺口先不处理 df[load] df[load].interpolate(methodlinear, limit3) # MAD 异常检测 median df[load].rolling(24, min_periods1).median() mad (df[load] - median).abs().rolling(24, min_periods1).median() mask (df[load] - median).abs() 6 * mad df.loc[mask, load] np.nan df[load] df[load].interpolate(methodlinear, limit3) # 归一化保存统计量 train_mean df[load].mean() train_std df[load].std() df[load_norm] (df[load] - train_mean) / train_std这段代码的逻辑是先把时间轴对齐再用滚动中位数和 MAD 做鲁棒异常检测避免个别尖峰把归一化区间拉偏。limit3控制插值长度超过就留 NaN后面构造窗口时跳过。归一化用训练集统计量是为了防止未来信息泄漏到训练里这一点在时间序列里比在图像任务里更容易翻车。2.2 滑动窗口用过去 N 小时预测未来 M 小时LSTM 的输入是序列所以要把一维负荷序列切成(样本数, 时间步, 特征数)的三维张量。常见做法是滑动窗口用过去seq_len个点预测未来pred_len个点。seq_len取 24 能覆盖一天日周期取 168 能覆盖一周周周期pred_len取 1 是单步预测取 24 是日前预测。工程里日前预测更常用因为调度需要提前一天排计划。import torch from torch.utils.data import Dataset, DataLoader class LoadWindowDataset(Dataset): def __init__(self, series, seq_len168, pred_len24): self.series torch.tensor(series, dtypetorch.float32) self.seq_len seq_len self.pred_len pred_len def __len__(self): return len(self.series) - self.seq_len - self.pred_len 1 def __getitem__(self, idx): x self.series[idx : idx self.seq_len] y self.series[idx self.seq_len : idx self.seq_len self.pred_len] return x.unsqueeze(-1), y # x: (seq_len, 1), y: (pred_len,) # 按时间顺序切分不要随机打乱 n len(df) train_end int(n * 0.7) val_end int(n * 0.85) train_ds LoadWindowDataset(df[load_norm].values[:train_end]) val_ds LoadWindowDataset(df[load_norm].values[train_end:val_end]) test_ds LoadWindowDataset(df[load_norm].values[val_end:]) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) val_loader DataLoader(val_ds, batch_size64, shuffleFalse) test_loader DataLoader(test_ds, batch_size64, shuffleFalse)这里的关键参数是seq_len和pred_len。seq_len168意味着模型每次看一周的历史显存和计算量都会上去如果数据只有几个月建议先用 24 或 48 跑通。切分必须按时间顺序不能随机打乱否则验证集里混入未来信息指标会虚高。shuffleTrue只用在训练集验证和测试保持顺序。提示如果负荷数据有明显周周期可以在特征里额外加入「小时」「星期几」的 one-hot 或 sin/cos 编码LSTM 对纯数值序列的周期建模能力有限加时间特征通常能降 5%10% 的误差。3. 用 PyTorch 搭一个能跑的 LSTM层数、隐藏维度和输出头怎么定3.1 模型结构从 nn.LSTM 到全连接输出PyTorch 里nn.LSTM的输入形状是(batch, seq_len, input_size)输出是(batch, seq_len, hidden_size)加上最后的(h_n, c_n)。做预测时通常取最后一个时间步的隐藏状态或者对所有时间步做注意力/池化。简单起见这里取最后一个时间步接一个全连接层映射到pred_len。import torch.nn as nn class LSTMForecaster(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, pred_len24, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0.0, ) self.fc nn.Linear(hidden_size, pred_len) def forward(self, x): # x: (batch, seq_len, input_size) out, (h_n, c_n) self.lstm(x) last out[:, -1, :] # 取最后一个时间步 y self.fc(last) # (batch, pred_len) return yhidden_size是模型容量最直接的旋钮64 到 256 是常见区间num_layers超过 2 层后收益递减而且容易过拟合建议从 1 层或 2 层起步。dropout只在多层 LSTM 内部生效单层时设 0。batch_firstTrue让输入维度顺序符合直觉不然要手动 transpose容易搞混。3.2 训练循环损失、优化器和早停负荷预测是回归任务损失用 MSE 或 Huber。Huber 对异常值更稳如果数据里还有没清干净的尖峰优先用 Huber。优化器 Adam 学习率 1e-3 是安全起点配合ReduceLROnPlateau在验证损失不降时减半。import torch.optim as optim device torch.device(cuda if torch.cuda.is_available() else cpu) model LSTMForecaster(hidden_size64, num_layers2, pred_len24).to(device) criterion nn.HuberLoss(delta1.0) optimizer optim.Adam(model.parameters(), lr1e-3) scheduler optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.5, patience5) best_val float(inf) patience_counter 0 early_stop_patience 15 for epoch in range(100): model.train() train_loss 0.0 for x, y in train_loader: x, y x.to(device), y.to(device) optimizer.zero_grad() pred model(x) loss criterion(pred, y) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() train_loss loss.item() * x.size(0) model.eval() val_loss 0.0 with torch.no_grad(): for x, y in val_loader: x, y x.to(device), y.to(device) pred model(x) val_loss criterion(pred, y).item() * x.size(0) train_loss / len(train_ds) val_loss / len(val_ds) scheduler.step(val_loss) if val_loss best_val: best_val val_loss torch.save(model.state_dict(), best_lstm.pt) patience_counter 0 else: patience_counter 1 if patience_counter early_stop_patience: print(fEarly stop at epoch {epoch}) break if epoch % 10 0: print(fEpoch {epoch}: train {train_loss:.4f}, val {val_loss:.4f})梯度裁剪clip_grad_norm_在 LSTM 里几乎是必选项序列一长梯度容易爆。早停耐心值 15 是经验值数据少时可以降到 8。保存验证损失最低的权重而不是最后一个 epoch 的权重这是避免过拟合的基本操作。3.3 评估指标别只看 MSE归一化后的 MSE 不直观工程里更常看 MAPE 和 RMSE还原到原始量纲。MAPE 在负荷接近 0 时会爆炸所以夜间低谷时段要单独看。我一般同时输出三个指标整体 RMSE、日间 MAPE、峰值时段误差。def evaluate(model, loader, mean, std): model.eval() preds, trues [], [] with torch.no_grad(): for x, y in loader: x x.to(device) pred model(x).cpu().numpy() preds.append(pred) trues.append(y.numpy()) preds np.concatenate(preds) * std mean trues np.concatenate(trues) * std mean rmse np.sqrt(((preds - trues) ** 2).mean()) mape np.mean(np.abs((preds - trues) / (trues 1e-6))) * 100 return rmse, mape rmse, mape evaluate(model, test_loader, train_mean, train_std) print(fTest RMSE: {rmse:.2f}, MAPE: {mape:.2f}%)还原时用训练集的mean和std不要用测试集的否则指标不可比。MAPE 分母加1e-6是防止除零但更稳妥的做法是过滤掉真实值低于某个阈值的点再算。4. 避坑与排查LSTM 负荷预测里最容易翻车的 5 个地方4.1 现象验证损失比训练损失低很多原因验证集切分时混入了训练集附近的时间段或者归一化用了全量数据统计量造成信息泄漏。时间序列里相邻样本高度相关随机切分会让验证集「见过」训练集的信息。解决严格按时间顺序切分归一化统计量只用训练段计算。如果数据量足够做滚动预测验证walk-forward每次用过去预测未来一段更接近真实部署。4.2 现象预测曲线整体滞后一个时段原因模型学到了「用上一个点预测下一个点」的恒等映射尤其是seq_len太短、pred_len1时。负荷曲线平滑滞后一小时的预测 MSE 也很低但实际调度价值不大。解决增大pred_len做多步预测或者在损失里对峰值时段加权。也可以把输入从「过去负荷」扩展为「过去负荷 时间特征」逼模型学周期而不是学复制。4.3 现象训练 loss 震荡不收敛原因学习率太大、batch size 太小、或者序列太长导致梯度爆炸。LSTM 对学习率比 CNN 敏感。解决学习率从 1e-3 降到 1e-4 试加梯度裁剪batch size 调到 64 或 128。如果还震荡检查数据里有没有没清掉的异常值Huber 的delta可以调小到 0.5。4.4 现象测试集 MAPE 很低但峰值误差很大原因负荷峰值样本占比小MSE 被大量平缓时段主导模型倾向于预测均值。MAPE 对峰值也不敏感因为峰值绝对值大相对误差反而小。解决单独看峰值时段的 RMSE或者在损失里对高负荷样本加权。也可以做分位数预测输出 P90 而不是均值调度侧更关心上限。4.5 现象换一台机器推理结果不一致原因推理时忘了切model.eval()dropout 还在生效或者归一化统计量没保存用了新数据的统计量。解决推理前固定model.eval()和torch.no_grad()把mean、std、seq_len、pred_len一起存进 checkpoint 或配置文件加载时严格对齐。5. 进阶技巧用 teacher forcing 和滚动预测把误差压下去多步预测有一个经典问题训练时用真实值作为下一步输入teacher forcing推理时只能用模型自己的预测误差会累积。负荷预测里pred_len24时这个问题在下午到晚间时段特别明显。我一般用两种手段缓解。第一种是 scheduled sampling训练前期用真实值后期逐步替换成模型预测让模型适应自己的误差。实现上就是在每个时间步决定用y_true还是y_pred概率从 0 线性增到 0.5。第二种是滚动预测模型只预测下一步然后把预测值拼回输入序列滚动 24 次得到全天曲线。这样模型结构简单但推理慢且误差同样会累积。实际项目里我会先跑滚动预测作为基线再用直接多步预测对比哪个峰值误差小用哪个。def rolling_forecast(model, init_seq, steps24): model.eval() seq init_seq.clone().to(device) # (1, seq_len, 1) preds [] with torch.no_grad(): for _ in range(steps): pred model(seq) # (1, pred_len) next_val pred[:, 0].view(1, 1, 1) preds.append(next_val.item()) seq torch.cat([seq[:, 1:, :], next_val], dim1) return preds这段代码每次只取预测的第一个点拼回输入末尾滑动窗口前移一步。init_seq是测试集某一段的真实历史steps24表示滚动预测一天。注意model的输出维度要和pred_len一致如果模型训练时pred_len24这里只取第一个点用其余丢弃。验证方法上我习惯把测试集按「工作日/周末」「夏/冬」分组分别算 RMSE。如果某个分组误差明显偏高说明模型没学到该场景的模式需要补特征或分场景建模。最后说一个我自己的习惯每次调完参数先把seq_len、hidden_size、num_layers、学习率、batch size 记在一张表里跑完对比再决定下一步改哪个。LSTM 负荷预测没有一套通吃参数靠的是对数据的理解和反复试。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑