
简介基于LSTM模型的电价预测源代码是一套面向电力市场分析、能源交易决策及量化研究场景的深度学习时序预测项目适合高校学生、数据科学初学者与相关领域工程师参考使用。压缩包共33个文件包括11个Python脚本、8个编译缓存、4个XML工程配置、4个CSV样本数据、1个训练好的模型权重、2张可视化图片及说明文档整体仅540KB代码组织清晰可直接运行与二次开发。项目覆盖了从数据清洗、归一化、训练集/测试集划分到LSTM网络搭建、参数迭代、评估与预测结果输出的完整链路并自带示例数据与模型参数便于对照复现代码模块化程度较高具备良好迁移性除电价预测外也可迁移至股票价格等相似时间序列任务。该资源已有202人学习体量虽小但信息密度高既能帮助理解LSTM内部机制又能作为快速上手的实战模板。 做电价预测这个方向有一段时间了最初我也没有直接上LSTM而是先试了ARIMA、XGBoost这些常见套路。结果怎么说呢能出结果但峰谷位置的误差始终压不下来。后来把重心切到LSTM才发现电价这种数据天生就适合交给带记忆机制的循环网络去处理——它不只是学一个当前特征到价格的映射更关键的是它能记住过去一段时间的价格走势然后基于走势外推。这篇文章就围绕基于LSTM模型的电价预测源代码展开把整个项目从数据清洗、样本构造、模型搭建到训练评估的完整链路讲清楚最后给出可以照着改的源码结构。想入时间序列预测这个坑、手里正好有历史电价数据但不知道怎么下手的同学可以认真看一下。1. 电价数据有什么特殊之处凭什么它适合用LSTM建模在动手写代码之前得先想明白一个问题为什么电价预测这个任务LSTM会比普通回归模型更合适。这不是赶时髦而是从数据本身的性质推导出来的结论。电价序列有几个非常明显的特征。一是强周期性一天之内通常是凌晨低谷、午前小高峰、晚间主高峰这样的形态工作日和周末的曲线又有差异不同季节的用电规模也不一样。二是高度非线性极端天气、发电机组临时检修、负荷突变都会让价格瞬间跳变。三是强依赖历史状态明天的早高峰价格往往和今天、前几天同时间段的价格存在强相关这种依赖不是某个固定公式能表达的需要模型自己去学。普通的前馈神经网络在处理这类数据时的短板很明显它对每个时间点的输入是独立看待的完全没有记忆的概念。而LSTM内部有输入门、遗忘门、输出门三个门控结构通过门控机制决定哪些历史信息要保留、哪些要丢弃。说得直白一点它就像一个人读一条时间序列的时候边读边记笔记碰到重要的信息就写进本子碰到不重要的就划掉最后靠这本子里的浓缩记忆来预测下一个时刻的数值。当然我也对比过GRU和Transformer。GRU结构更轻训练速度快一些在样本量不是特别大的时候也是个不错的选择但我最终保留LSTM版本的原因有两个一是LSTM在电价这种中等长度序列上的表现足够稳定二是LSTM的tuning经验在社区里更丰富出了问题能搜到大量现成案例。至于Transformer它在长序列全局依赖上确实强但电价预测这种任务用过去24小时或者168小时的数据去预测未来1小时序列长度并不算长Transformer的全局注意力优势发挥不出来反而容易因为训练数据量不够而过拟合。所以选LSTM不是因为它最先进而是因为它在效果可接受、调试成本低、社区资料多这三个维度上最均衡。这个道理适用于绝大多数实际项目选模型永远不要只看排行榜要看数据和任务的真实需求。2. 数据清洗与样本构造九成的预测误差其实出在这一步很多人在网上跑通现成代码之后换到自己数据集上效果立刻崩掉问题多半不在模型在数据处理。原始电价数据的质量问题比想象中严重最常见的有三类缺失值、异常尖峰、时间不连续。2.1 缺失值与异常值的处理策略电价数据在采集过程中经常会出现整点数据丢失或者某个时段因为系统故障记了一堆0。我的处理原则是连续缺失不超过两个点就用线性插值超过两个点就对齐相邻日期的同一时刻进行填充。这是因为电价有极强的日内周期性用前一天同一时刻的价格做参考比单纯靠相邻点插值可靠得多。异常尖峰要分情况讨论。如果尖峰出现在极端天气日或者重大节假日那它本身是有效信息不能随便去掉。如果尖峰是因为采集设备故障导致的比如价格突然跳到几万块一兆瓦时然后又落回正常范围这时候需要设置一个合理的阈值比如超过正常区间3倍以上且持续时间只有一个采样点就判定为异常值并做平滑处理。2.2 时间特征构造模型不认识日历但我们得喂给它LSTM本身只感知到连续的数值序列它并不知道今天到底是周一还是周六也没办法自动理解凌晨2点和下午2点有什么区别。所以我们必须把时间信息转成特征喂进去。我通常会构造以下几列特征小时的sin/cos编码避免23点和0点之间的数值跳变星期几的独热编码让模型区分工作日与周末节假日标记非工作日特征直接给0/1历史价格滞后项如滞后1小时、24小时、168小时的价格滑动均值与滑动标准差反映近期价格水平和波动程度这里有一个容易被忽略的细节做时间滑窗的时候一定要避免未来数据泄漏。就是说预测t时刻的价格时只能用t时刻之前的数据构造特征绝对不能把t时刻之后的信息提前混进来否则训练时指标漂亮一上真实环境就原形毕露。2.3 归一化与滑动窗口样本生成LSTM对输入数值的尺度很敏感。电价的范围可能在几十到几千之间浮动这种量级差异会让训练过程极不稳定。我的做法是使用MinMaxScaler把价格缩放到0到1之间注意统计缩放参数只能用训练集去拟合验证集和测试集都要复用训练集的scaler来转换否则又犯了数据泄漏的毛病。滑动窗口的样本生成是整个预处理里最关键的一步。假设我们设置lookback窗口为24小时意思就是拿过去24个小时的价格和特征数据去预测下一个小时的价格。用代码表示就是def create_sequences(features, target, lookback24): X, y [], [] for i in range(len(features) - lookback): X.append(features[i:i lookback]) y.append(target[i lookback]) return np.array(X), np.array(y)窗口长度这个超参数后面还会详细聊它直接影响模型能看到多远的过去。对于电价预测我实测下来24小时和168小时是两个常见的窗口档位前者适合做次日小时级预测后者因为涵盖完整一周的周期适合做含周规律的预测任务。2.4 数据集划分顺序切分不要乱序这一点必须单独拿出来强调。时间序列数据的划分和普通机器学习不一样不能用随机打乱的方式切分训练集和测试集因为样本之间存在时序依赖。正确做法是按时间顺序比如前70%的数据作为训练集接着15%作为验证集最后15%作为测试集。测试集必须是最新一段时间的真实数据这样评估出来的结果才有说服力。之前见过有人拿随机抽样切分方式训练LSTM测试集里的样本时间点比训练集还早结果模型偷看了未来的信息验证指标异常地好上线之后完全不是那么回事。这种错误不细想根本发现不了但一旦部署代价非常大。3. 网络怎么搭LSTM层数、隐层维度与关键超参数的实测权衡模型结构这部分业界没有统一标准网上各种配置都有。我分享一下在电价预测场景下实测下来比较稳定的一组配置以及每个参数的调优逻辑。3.1 基线网络结构我用的是Keras实现整体结构非常简洁from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau model Sequential([ LSTM(units64, return_sequencesTrue, input_shape(lookback, n_features)), Dropout(0.2), LSTM(units32, return_sequencesFalse), Dropout(0.2), Dense(units16, activationrelu), Dense(units1) ]) model.compile(optimizeradam, lossmse, metrics[mae])这个结构里有几个点值得展开说。3.2 单层还是双层LSTM我试过一层、两层、三层LSTM堆叠。一层LSTM在数据量只有几千条的时候训练速度快但拟合能力有限对电价这种复杂非线性模式的刻画出不太够用。三层LSTM表达能力上去了但训练时间明显拉长而且容易过拟合在小规模数据集上尤其明显。最后稳定在两层第一层return_sequences设为True是为了把完整的隐藏状态序列传给第二层第二层return_sequences设为False只输出最后一个时间步的隐藏状态再接全连接层做预测。3.3 隐层维度64还是128隐层维度说的是LSTM内部状态向量的宽度。64对比128在测试集上的差异并没有想象中大提升大概在2%到3%的MAPE之间但训练时间却增加了近一倍。所以数据量没有大到一定程度的时候64是一个性价比很高的起点。如果后续数据量扩充了再往上加到128也不迟。3.4 lookback窗口、batch size与学习率lookback窗口的选择可以直接实验对比我用过一个笨办法分别用12、24、48、168跑几组实验对比验证集loss。实测下来24小时窗口在小时级预测任务上的表现最均衡12小时因为缺少跨天信息导致夜间预测偏差大168小时窗口数据维度高、训练慢但MAPE并没有明显下降。batch size我一般设为64或128。过小会导致梯度更新方向抖动剧烈过大则内存占用高而且容易收敛到平坦但泛化差的区域。学习率初始值用0.001配合指数衰减或者ReduceLROnPlateau训练后期自动降学习率来精细收敛。实测中ReduceLROnPlateau的触发效果比固定指数衰减更可控因为它只有在loss停滞时才会降低学习率避免过早衰减导致收敛不充分。4. 训练过程翻车记录Loss震荡、过拟合和早停策略实操代码写出来跑通只是第一步真正让人挠头的是训练过程中出现的各种异常。我把实际踩过的坑和对应的处理方法整理一下这些经验在教科书上很难找到。4.1 Loss震荡不收敛先查数据再查学习率我遇到过最迷惑的情况是训练集loss在一开始就不降反升甚至出现NaN。排查链路是这样的先检查归一化是否出了问题比如某列特征没有做缩放数值直接爆掉接着检查序列构造是否存在泄漏比如滑窗内混入了目标值自身。排除这些之后如果正常训练但仍然震荡那就需要降低学习率从0.001降到0.0003试试。还有一次很隐蔽的问题我在构造特征时把目标值的小时标记误当成了特征列训练时模型走了捷径验证集效果一塌糊涂。这个坑提醒我特征工程做完之后一定要进行一次未来信息自检一行一行地过特征逻辑别贪多。4.2 过拟合的直观信号与应对手段过拟合最直接的信号是训练loss不断下降但验证loss在某个epoch之后开始回升。这时我一般先看训练集和验证集的差距有多大如果训练集MAE已经压到极低而验证集远高于它基本可以确定过拟合了。应对手段按优先级排序第一增加Dropout比率我会从0.2提到0.3或者0.4第二缩小LSTM隐层维度从64降到32第三增加训练数据量或者用更短的lookback窗口来减少模型容量第四加入L2正则化。注意Dropout和L2不是加得越多越好过大之后模型会欠拟合表现为训练集和验证集的误差同时上升。4.3 早停策略不要执着于固定的epoch数训练LSTM的时候设置一个固定的epochs数比如100或者500这其实是一个非常粗糙的做法。我现在的习惯是配合EarlyStopping监控验证集loss当它在连续15到20个epoch内没有下降时就提前终止训练并且把验证集loss最低的那个epoch对应的权重保存下来。early_stop EarlyStopping(monitorval_loss, patience15, restore_best_weightsTrue) lr_scheduler ReduceLROnPlateau(monitorval_loss, factor0.5, patience5)EarlyStopping的好处不只是省时间它本质上就是在模型泛化能力最强的那一刻截断训练。如果不设早停训练到后期虽然训练loss还在微降验证loss已经开始恶化保存下来的模型反而不是最优的。4.4 梯度裁剪处理Loss突然爆掉的小技巧LSTM在训练过程中偶尔会遇到loss突然飙到一个离群值的情况这与梯度爆炸有关。在Keras里可以通过clipnorm参数来控制梯度范数的上限比如设置clipnorm1.0。这个参数平时用不上但如果你的loss曲线偶尔出现尖峰加上它往往能让训练过程稳定不少。5. 结果评估别只看MAE峰谷电价捕捉能力才是关键模型训练完之后评估环节最容易出现的问题是只看一个平均误差指标就下结论。我之前也犯过这样的错误后来被实际场景教育了某些时段预测的误差被平均指标稀释了但那些时段恰恰是电力交易中最需要精准预测的时刻。5.1 指标不是越多越好但要选对电价预测常见的指标有MAERMSEMAPE。我一般会同时打印这三个值因为它们的评价视角不同MAE反映平均绝对偏差RMSE对大幅偏差更敏感MAPE反映相对误差的大小。还有一点需要注意的是当电价出现接近0的低谷值时MAPE会被分母拉得很离谱所以如果数据里存在大量低价时段MAPE的值要大打折扣看待。5.2 按时段拆分误差比总指标更有洞察力我在评估阶段会把测试集的预测结果按小时维度拆分分别计算早上7点到10点、晚上18点到21点这两个用电高峰段的误差指标再计算其他时段的误差指标。为什么要单独拆峰谷因为这两个时间段的价格绝对值高、波动大预测偏差直接影响交易策略的盈亏。很多时候总平均MAE看起来还行拆开一看发现高峰时段的MAE是其他时段的两倍不止这种信息只看总指标是永远发现不了的。具体实现也很简单就是把预测结果和真实值按时间标签存储然后用pandas按小时分组求MAE。最后画出一张24小时的误差分布图哪个时段预测弱一眼就能看出来。5.3 反归一化一个极其容易翻车的细节测试集预测出来的结果是通过scaler归一化过的评估之前必须反归一化回真实电价尺度。很多人在这里图省事直接把归一化后的误差当成最终指标结果发现MAE只有0.05就觉得模型很完美实际换算成真实电价完全不是一回事。正确做法是先用scaler.inverse_transform把预测值和真实值都还原再计算误差指标。反归一化之后还有一个小细节如果预测值出现了负值这在电价场景下实际上是不合理的需要做截断处理把负值置为0或者一个很小的正数。虽然LSTM输出层用的是线性激活不排除个别点会拟合出负值但这种点要单独处理否则下游交易策略会把负价格当成真实信号。5.4 可视化对比预测曲线和真实曲线叠加评估的时候我习惯把测试集最后7天的真实电价和预测电价画在同一个图上。这个图能直观告诉我的信息比任何指标都多如果预测曲线整体滞后于真实曲线说明模型对短时波动的响应偏慢可能需要调整lookback窗口如果峰谷趋势跟上了但幅度偏小说明模型存在过度平滑的问题可能是Dropout率太高或者模型容量不足。6. 源码工程化从单个脚本到可维护预测工具的整理思路很多开源项目里的LSTM预测代码是一个大而全的脚本从上到下依次是导入库、读数据、处理、训练、评估。这种结构在一个固定数据集的演示场景下没有问题但一旦要接新数据、换参数、部署到生产环境就会非常痛苦。所以我建议把项目按功能模块拆分一个清晰的目录结构本身就是项目质量的一部分。6.1 推荐的项目目录结构electricity_price_lstm/ ├── data/ │ ├── raw/ # 原始数据 │ ├── processed/ # 清洗后的数据 │ └── scaler.pkl # 归一化参数 ├── src/ │ ├── data_loader.py # 数据读取与清洗 │ ├── features.py # 特征工程与序列生成 │ ├── model.py # LSTM模型定义 │ ├── train.py # 训练脚本 │ ├── evaluate.py # 评估脚本 │ └── utils.py # 公共工具函数 ├── configs/ │ └── config.yaml # 超参数配置 ├── models/ │ └── lstm_model.h5 # 训练好的模型权重 └── notebooks/ └── eda.ipynb # 探索性数据分析这个结构里最重要的就是config.yaml把所有超参数集中管理。换数据集或者调参的时候只需要改配置文件不需要动代码。对于做实验来说这个改动省下来的时间非常可观。6.2 模型保存、加载与推理封装训练完成后把权重和配置一起保存这样别人拿到的不是一个黑盒模型文件而是一整套可复现的实验配置。model.save(models/lstm_model.h5) # 推理时加载 from tensorflow.keras.models import load_model model load_model(models/lstm_model.h5)在部署的时候我会在推理模块里做一个很小的封装函数输入的是原始特征DataFrame和lookback窗口内部完成标准化和序列拼接输出的是反归一化之后的真实价格。用户不需要关心内部细节这样的接口设计在实际使用中体验好很多。6.3 后续扩展从单步预测到多步预测这篇文章里讨论的是单步预测也就是用过去24小时预测未来1小时。实际业务里我们往往需要预测未来24小时甚至72小时的价格曲线。扩展方向有两种一种是递归预测把预测出来的值作为输入的一部分继续预测下一个时刻这种方式实现简单但误差会随着预测步长累积另一种是seq2seq结构用编码器-解码器架构直接输出未来24个小时的价格序列误差累积问题会好一些但模型结构更复杂训练时间更长。我个人的建议是先做好单步预测把整个数据链路打磨稳定再扩展多步这个顺序能少踩很多坑。6.4 代码质量与可复现性的几个经验写这类项目代码时有几点是我现在非常在意的所有随机操作设置固定的随机种子保证实验可复现每个处理函数写清楚输入输出格式方便自己隔一段时间回来看也能快速上手重要步骤加注释说明为什么这样做而不是只写做了什么。最后再分享一个实操中容易忽视的细节当你要发布这套源代码的时候记得把数据脱敏尤其是数据里可能包含特定区域、特定交易机构的信息尽量只保留时间序列本身。这样既安全又方便别人用任意一份电价数据替换测试真正发挥开源源码的价值。本文还有配套的精品资源点击获取