基于LSTM的电力负荷时间序列预测:从数据清洗到多步预测完整实践

发布时间:2026/9/23 8:13:43
基于LSTM的电力负荷时间序列预测:从数据清洗到多步预测完整实践 简介这是一份基于深度学习算法实现电力负荷时间序列未来预测的 Python 源码项目围绕负荷历史数据完成特征构造、模型训练与结果评估覆盖 LSTM、GRU、Transformer、ARIMA、随机森林、决策树、KNN 等多种算法适合计科、人工智能、大数据等专业学生作为毕业设计、课程设计或深度学习入门实践。压缩包共 20 个文件以 16 个 Python 脚本为主分别实现不同预测模型的数据处理与训练逻辑另含 2 个说明文档和 2 个文本文件用于介绍项目背景、运行配置与使用指引整体仅 43KB结构精简、便于阅读和二次开发。目前已有 265 人学习下载。代码注释详细关键步骤均有解释既可以帮助初学者理解时序预测的建模流程与算法差异也能为进阶者提供对比实验和模型改进基础是电力负荷预测方向值得参考的完整示例。1. 这套预测源码在解决什么问题把明天的负荷曲线提前算出来电力调度值班最怕的不是电网设备故障而是明天早上九点的负荷峰值和你昨晚报的数差了十万八千里。传统时间序列方法在平稳天气下表现尚可一旦遇到连续阴雨、节假日调休误差就迅速放大。近年电力负荷预测的落地重心已经明显转向深度学习模型——LSTM、GRU这类循环结构天然适合处理带前后依赖的序列数据。我拿到标题里这套「基于深度学习算法实现电力负荷的时间序列未来预测python源码」时第一反应是这不只给了一个模型而是给了整条从原始负荷序列到未来节点的预测链路。适合三类人电力行业的数据分析岗、做时间序列预测需要参考案例的算法工程师、以及毕业论文选了LSTM时序方向的学生。用好它比想象中容易前提是你把数据处理的细节想明白。2. 预测问题怎么定义从一条时间序列到一组监督学习样本2.1 单变量还是多变量先想清楚模型要学什么电力负荷预测在建模层面首先分两条路单变量时间序列预测只用历史负荷值本身把负荷当作一个自回归过程多变量则额外引入温度、湿度、风速、日期类型等外生特征。标题里的核心对象是电力负荷最常见的落地形态就是单变量为主、时间特征为辅。我一般会先用单变量跑通基线再接小时、星期、节假日等时间特征做多变量增强这样能明确看到每一路输入带来的增益在哪。构建监督学习样本之前先确认数据结构。源码包里的示例数据通常是两列timestamp和load时间粒度可能是15分钟、30分钟或1小时一个点。后续所有滑动窗口的窗口长度、步长都建立在“时间粒度”这个基础上它决定了一个窗口里到底包含了多少个时刻的物理意义。2.2 滑动窗口用过去N个点预测未来M个点模型没法直接吃一串原始时间戳它需要的是一组“特征-标签”对。做法是把时间序列切成固定长度的窗口取第t-L1到t这 L 个点的负荷作为特征预测t1到tH这 H 个点的负荷作为标签。L 是 lookbackH 是预测步长。import numpy as np import pandas as pd def create_sliding_windows(data, lookback48, horizon1, stride1): 将一维或多维时间序列转换为监督学习样本。 参数 data: 形状为 (n_samples, n_features) 的数组n_features1 lookback: 用过去多少个时间步作为特征 horizon: 预测未来多少个时间步默认1表示单步预测 stride: 窗口滑动步长默认1表示逐点滑动数据量大时可调大为2或4 返回 X: 形状为 (n_windows, lookback, n_features) y: 形状为 (n_windows, horizon) X, y [], [] for i in range(len(data) - lookback - horizon 1): X.append(data[i : i lookback]) y.append(data[i lookback : i lookback horizon, 0]) return np.array(X), np.array(y)窗口构造这段逻辑是整个预处理里最重要的地方。X被组织成(样本数, lookback, 特征数)三维结构这个形状是为后面送入 LSTM 准备的batch_firstTrue时模型要求输入就是这种排布。horizon1表示每步只预测下一个时点的负荷先做单步预测拿到稳定基线再扩展多步。stride参数容易被忽略如果原始数据一分钟一个点逐点滑动会产生极大的样本冗余训练速度变慢且相邻样本高度相似把stride调到 5 甚至 15能显著减少这种重叠模型泛化也更好。提示窗口长度 L 的选取要参考负荷的“日周期性”。如果数据是15分钟粒度一天就是96个点那lookback96或1921~2天比较合理如果是小时粒度一天24个点lookback24起步。2.3 数据划分的铁律时间序列不能随机打乱这是新手做时序预测时最容易翻车的地方。把数据集train_test_split(shuffleTrue)一拆验证集指标好看得惊人一上真实场景就崩。原因在于随机打乱把未来信息混进了训练集模型等于提前看见了答案。train_ratio, val_ratio 0.7, 0.15 n len(X) train_end int(n * train_ratio) val_end int(n * (train_ratio val_ratio)) X_train, y_train X[:train_end], y[:train_end] X_val, y_val X[train_end:val_end], y[train_end:val_end] X_test, y_test X[val_end:], y[val_end:]划分的核心原则是严格按照时间先后顺序切分训练集在最前验证集在中间测试集在最后。这样测试集的预测过程才真正模拟了“用过去预测未来”的线上场景。注意验证集不是用来调参后反复试的它承担的是早停和超参数选择的职责测试集只在最终评估时碰一次这个纪律要守住否则测试集也会被你不知不觉调成验证集的一部分。3. 数据预处理预测上限在模型之外就被决定了3.1 缺失值和异常值先剔尖峰再补空洞电力负荷数据从采集终端到数据库会经历通信中断、表计故障、人工补录等环节缺失值和异常尖峰几乎不可避免。处理顺序有讲究必须先识别并剔除异常值再做缺失填充。如果反向操作异常尖峰可能被插值算法当成正常波动融入周围窗口污染一整段序列。def clean_load_series(df, value_colload, zscore_thresh4.0): 负荷序列清洗先剔除异常尖峰再线性插值补缺失。 参数 df: 必须包含时间列和负荷值的DataFrame zscore_thresh: z-score阈值超过该值视为异常点 df df.copy() df[load] pd.to_numeric(df[load], errorscoerce) # 第一步z-score识别并剔掉异常跳变点 mean, std df[load].mean(), df[load].std() df.loc[(df[load] - mean).abs() zscore_thresh * std, load] np.nan # 第二步统一按缺失值处理用线性插值补全 # 负荷曲线整体平滑线性插值已足够不要用均值填充会让曲线变平 df[load] df[load].interpolate(methodlinear, limit_directionboth) return dfz-score 的阈值要看数据本身的噪声水平。我见过负荷数据标准差本身被几个大尖峰拉大导致真实异常反而落在阈值内部的案例所以更稳的做法是先用中位数绝对偏差MAD做一次稳健估计。插值方面负荷序列短期变化有惯性线性插值足够但要注意limit_directionboth这个参数它保证序列最前和最后位置的缺失也能被填上。如果缺失发生在数据末尾插值后可能仍然不规则这种情况最好是直接截掉末尾的无效段。3.2 归一化scaler 只能 fit 训练集不能看全局数据LSTM 内部使用 tanh 和 sigmoid 作为激活函数输入尺度太大或太小都会让梯度更新变慢所以归一化几乎是必须的。电力负荷的幅值从几百到几千兆瓦不等数量级差异对训练稳定性影响很大。from sklearn.preprocessing import MinMaxScaler # 用训练集的负荷列 fit scaler scaler MinMaxScaler(feature_range(0, 1)) train_values df.iloc[:train_end][[load]].values scaler.fit(train_values) # 训练/验证/测试集全部用同一个 scaler 变换 df[load_scaled] scaler.transform(df[[load]].values)scaler.fit的对象只有训练集这一段这是整个预处理中最容易忽略的泄漏点。如果对全量数据做 fit那测试集的数值范围已经被模型“看见”了预测未来时你根本拿不到未来的统计信息来做归一化效果就会虚高。验证集和测试集只能使用训练集训练好的同一个 scaler 做transform推理阶段也一样这个习惯要固化成肌肉记忆。3.3 时间特征让模型感知“今天是星期几、几点钟”单纯把负荷数值喂进 LSTM模型只能学到粗糙的惯性延续很难自己悟出负荷的周期性规律。负荷曲线的形状跟时间强相关工作日早高峰、晚高峰周末整体走低节假日完全变形。常见做法是构造三类时间特征hour、dayofweek、is_holiday。def build_time_features(df): 从时间戳中提取周期性时间特征用sin/cos编码保留连续变化关系。 ts pd.to_datetime(df[timestamp]) df[hour] ts.dt.hour ts.dt.minute / 60.0 df[dayofweek] ts.dt.dayofweek # 小时的0点和23点虽然数值相差很大但物理上相邻用sin/cos编码更合理 df[hour_sin] np.sin(2 * np.pi * df[hour] / 24.0) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24.0) df[dow_sin] np.sin(2 * np.pi * df[dayofweek] / 7.0) df[dow_cos] np.cos(2 * np.pi * df[dayofweek] / 7.0) # 节假日是强影响因子直接用0/1标签 # is_holiday 需要结合具体地区的节假日表生成这里只留接口 df[is_holiday] 0 return df时间特征里hour用 sin/cos 编码而不是直接当一个整数喂进去是一个很关键的细节。整数编码会暗示模型“23点和0点差距很大”但物理上它们是连续循环的。sin/cos 编码把时间映射到单位圆上0点和23点距离很近模型更容易学会周期性。把这些特征拼到负荷值后面特征维度就从1扩到5~6维create_sliding_windows里的n_features要同步调整。4. 模型与训练LSTM 电力负荷预测的核心代码走读4.1 定义 LSTM 回归模型输入形状与输出形状LSTM 在这类任务里担任的角色是捕捉序列内部的长期依赖。负荷数据里昨天的同一时刻、上周的同一工作日的负荷往往比前一刻的值更有参考价值这正是 LSTM 的门控结构擅长的路径。模型本身不复杂重点在于输入输出的形状别搞错。import torch import torch.nn as nn class LSTMPowerForecast(nn.Module): 单变量/多变量电力负荷预测的LSTM模型。 输入形状: (batch_size, lookback, feature_dim) 输出形状: (batch_size, horizon) def __init__(self, feature_dim1, hidden_dim64, num_layers2, horizon1, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizefeature_dim, hidden_sizehidden_dim, num_layersnum_layers, batch_firstTrue, dropoutdropout ) self.fc nn.Sequential( nn.Linear(hidden_dim, hidden_dim // 2), nn.ReLU(), nn.Linear(hidden_dim // 2, horizon) ) def forward(self, x): # out: (batch_size, lookback, hidden_dim) out, _ self.lstm(x) # 取最后一个时间步的隐藏状态形状变为 (batch_size, hidden_dim) last_hidden out[:, -1, :] return self.fc(last_hidden)batch_firstTrue让输入形状是从(seq_len, batch, feature)变成(batch, seq_len, feature)代码读起来直观很多。out[:, -1, :]取序列最后一个时间步的输出这代表模型把所有历史信息压缩到最后一个隐状态再做映射得到未来 H 个负荷值。hidden_dim64、num_layers2是从小数据集起步的稳妥搭配如果你的数据量上万条可以试着把 hidden_dim 提到128层数加到3但要同步增加 dropout 防止过拟合。fc里的两层全连接是为了给 LSTM 输出加一点非线性变换能力直接把隐状态映射到horizon维也行但效果通常会差一些。4.2 训练循环损失函数、优化器与早停def train_model(model, train_loader, val_loader, epochs50, lr1e-3, devicecuda): model.to(device) criterion nn.MSELoss() # 回归任务首选MSE梯度平滑 optimizer torch.optim.Adam(model.parameters(), lrlr, weight_decay1e-5) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, factor0.5, patience5, verboseTrue ) best_val_loss float(inf) best_state None for epoch in range(epochs): model.train() train_loss 0.0 for x_batch, y_batch in train_loader: x_batch x_batch.to(device) y_batch y_batch.to(device) optimizer.zero_grad() pred model(x_batch) loss criterion(pred, y_batch) loss.backward() optimizer.step() train_loss loss.item() * x_batch.size(0) train_loss / len(train_loader.dataset) # 验证集上评估保存最优权重 model.eval() val_loss 0.0 with torch.no_grad(): for x_batch, y_batch in val_loader: x_batch x_batch.to(device) y_batch y_batch.to(device) pred model(x_batch) val_loss criterion(pred, y_batch).item() * x_batch.size(0) val_loss / len(val_loader.dataset) scheduler.step(val_loss) if val_loss best_val_loss: best_val_loss val_loss best_state {k: v.clone() for k, v in model.state_dict().items()} if epoch % 10 0: print(fepoch {epoch}, train_loss{train_loss:.5f}, val_loss{val_loss:.5f}) model.load_state_dict(best_state) return model训练循环里几个参数值得多说两句。Adae换成Adam是这类任务的默认选择weight_decay1e-5做一点L2正则ReduceLROnPlateau会在验证损失连续5个epoch不下降时把学习率减半比硬编码固定学习率稳很多。每次迭代都用验证集损失最小的权重作为最终模型这就是早停的变体实现——不用等训练完全结束保存最优状态比训练到最后一步更可靠。from torch.utils.data import TensorDataset, DataLoader # 数据形状: X_train (n_samples, lookback, feature_dim) train_dataset TensorDataset( torch.FloatTensor(X_train), torch.FloatTensor(y_train) ) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue, drop_lastTrue)DataLoader 的shuffleTrue这里可以开因为特征和标签在时间上已经配对完成窗口内部的时间顺序没有被打乱样本之间洗牌不影响时序语义反而有助于训练稳定。batch_size64是常见起步值显存不够就调到32数据量小就调小这个参数跟数据规模绑定没有绝对最优。4.3 预测与反归一化从标准化数值回到真实负荷训练完成之后预测的关键是把缩放后的数值还原成真实功率值。源码里最容易缺的一环就是这个反归一化忘掉的话预测结果会一直停留在0到1之间看起来形状对但数值完全没有意义。import joblib # 保存模型和scaler推理时两个都要加载 torch.save(model.state_dict(), lstm_power.pth) joblib.dump(scaler, scaler.save) # 预测函数 def predict_next(model, scaler, latest_window, devicecpu): latest_window: 最近 lookback 个时间步的负荷值已经按训练时同样的方式归一化 model.eval() x torch.FloatTensor(latest_window).unsqueeze(0).to(device) # (1, lookback, feature_dim) with torch.no_grad(): pred_scaled model(x).cpu().numpy().reshape(-1, 1) return scaler.inverse_transform(pred_scaled)scaler.inverse_transform可以直接把形状为(n, 1)的归一化值还原回真实量纲。这里还隐含一个工程问题如果用的是多变量特征latest_window构造时要保证各特征列的顺序和训练时完全一致否则模型吃进去的是一个特征顺序被打乱的样本预测结果自然不对。我建议把feature_columns列表固定下来以列表顺序为准拼接输入不要靠记忆打特征。把这些部分串起来这套源码的整体流程就清楚了数据清洗 → 时间特征构造 → 归一化 → 滑动窗口 → 数据集划分 → LSTM训练 → 反归一化输出。每一步都有对应的Python代码标题里“有详细注释”的价值也正在这里。5. 电力负荷 LSTM 预测最容易踩的5个坑现象、原因与解决5.1 验证集效果很好换上真实新数据就拉胯现象训练时验证集 MAPE 在2%以内一放到没有参与训练的最新数据上误差直接跳到10%以上。 原因最常见的是在归一化或数据清洗阶段用了全量数据统计量。scaler.fit如果在切分训练集之前执行验证集和测试集的数值范围就已经泄露出去了。 解决把scaler.fit严格限定在训练集范围内验证和测试只用transform。检查代码里所有fit操作只允许对train前缀的数据执行。5.2 训练过程损失值突然变成 NaN现象loss 在某个 epoch 之后输出nan训练直接崩溃。 原因一是梯度爆炸LSTM在长序列上容易累积梯度幅值二是学习率太大导致loss越过数值上界。 解决先给模型加torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)这行代码放在optimizer.step()之前。同时把初始学习率从1e-3降到5e-4重试。如果用了ReduceLROnPlateau要确认它的触发逻辑正常不会在损失异常时继续放大学习率。5.3 预测曲线几乎是一条水平直线现象预测值整体贴合历史均值峰谷特征全部消失像一条被拍扁的曲线。 原因模型学到的实际是“输出接近训练集均值”这条捷径。常见诱因是归一化时存在极端离群值把数据压缩到很窄的区间或者LSTM隐层维度太小、表达能力不足。 解决检查归一化前是否剔除了异常尖峰把hidden_dim从 32 提到 64 或 128如果数据量够把num_layers从1提到2。另外确认窗口构造时horizon是否过大——预测步长太长时模型会退化为输出均值先回到horizon1把单步预测做准。5.4 用测试集反复调参调完一套发现全白费现象每次改动超参数后都用测试集评估前后对比发现最优参数在测试集上过拟合了。 原因测试集被当成了验证集使用模型和超参数都对测试集产生了间接记忆。 解决训练过程中只用验证集评估选参测试集只在最终一次评估时使用。把测试集数据从开发流程中隔离出去甚至可以只在发布前跑一次。这点对精度要求高的负荷预测场景尤其重要因为它直接关系到模型上线后的真实表现。5.5 多步预测误差随步长急剧累积现象预测未来1小时误差3%未来6小时误差跳到9%未来24小时几乎不可用。 原因递归式多步预测把每一步的预测值当作下一步的输入误差像滚雪球一样叠加。这是时间序列多步预测的固有问题。 解决在源码基础上改用直接式多步预测即一次输出horizon维的标签训练时把horizon从1改成目标步长模型自己学习多步映射关系。或者直接换成 Seq2Seq 结构让解码器逐步产出未来序列。这个方向在下一章展开。6. 从单步到多步进阶用法与验证习惯多步预测的现实意义远大于单步——调度员关心的是未来一天甚至一周的负荷曲线而不是下一个15分钟的点。最常见的进阶做法是两种一是把horizon直接设为目标步长让模型一次输出整个未来窗口这种直接式预测每步之间互不影响不会累积误差但训练难度稍高序列越长越难收敛二是保持单步模型不变用预测出的值不断回填到窗口尾部做滚动预测代码改动最小但误差累积问题无解。我建议先实现直接式把horizon24放进现有模型里跑一轮输出形状从(batch, 1)变成(batch, 24)其余代码几乎不用动这是性价比最高的升级。多步预测的验证方式也要跟着调整。单步预测看每个时点的平均误差就行多步预测必须按步长分别统计误差你会看到类似“第1小时 MAPE 3%、第6小时 7%、第12小时 9%”的分布曲线。这条曲线比一个总体的平均值信息量大得多它告诉你模型的有效预测时长在哪里结束也直接决定业务上能用这个结果做什么——比如超过6小时之外的预测值只能做趋势参考不能进调度计划。我个人的验收习惯是三件套第一画一张测试集后两周的预测对比曲线肉眼确认峰谷对齐情况第二按工作日、周末、节假日分组统计 MAPE单独观察模型在特殊日期的表现第三把训练集最后200个点强行预测一遍记录从第几步开始偏差明显放大。这三个动作做下来模型的真实水平就基本摸清了。这套源码的价值在于它把深度学习的建模链路完整打通了从数据处理到模型训练都有详细注释。拿到手之后不要急着换模型结构先替换成你自己的电力负荷数据跑通全流程拿到基线误差再逐步加时间特征、加深网络、扩展多步预测。数据清理和特征工程带来的提升往往比换一个更强的网络结构更立竿见影。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询