
简介一套面向电网调度与能源管理的电力负荷预测系统基于时间序列数据实现每小时电力负荷数值及未来变化趋势的预测。面向电力行业数据分析人员、机器学习学习者及电网运维工程师涵盖了ARIMA、决策树、KNN、LSTM、GRU、随机森林与Transformer等主流时序建模方法便于对比不同算法在负荷预测任务中的适用性与精度。压缩包共11个文件以8个Python脚本为核心分别对应各模型的训练与预测实现附带Markdown说明文档、txt使用说明和docx附赠资料辅助理解模型原理并快速复现实验。包体仅54KB轻量易部署目前已有63人浏览学习。通过这套资料读者可掌握从经典统计到深度学习的完整方法链结合示例代码与说明文件完成特征分析、模型调参与结果评估为电网调度优化与能源管理提供可落地的预测参考。1. 时间序列电力负荷预测为什么每个模型单独跑得通一上系统就翻车电力负荷预测是电网调度和能源管理的“地基”之一。配电网要提前安排机组启停、储能充放和需求响应靠的不是“昨天大概用了多少电”而是对每小时负荷的定量预测。这个基于时间序列数据、以小时为粒度构建的电力负荷预测系统把 ARIMA、决策树、KNN、随机森林、LSTM、GRU、Transformer 七类模型放进同一套框架里做对比和集成目标就是逼近“未来 24 小时每个整点的负荷值”。适合的读者很直接正在做能源管理平台、储能调度策略、电力交易辅助决策的工程师或者刚接触时序预测、想找一份能落地的多模型基准代码的开发者。这类系统的坑往往不在模型本身而在数据清洗和评估方式。电力负荷天然具备强周期性和节假日效应如果直接用默认参数跑 LSTM 或 Transformer预测曲线经常会整体滞后一拍看起来 R² 不错画出来却比真实值晚了整整一小时。本文会先把这类系统的数据链路和特征逻辑讲透再给出可复现的建模流程与参数配置最后用避坑记录把最容易出问题的地方逐个拆开。2. 负荷时间序列的数据特性与特征工程先把“每小时”变成模型能学的东西2.1 电力负荷的三个周期日周期、周周期和季节性电力负荷数据不是平稳序列但它的非平稳是有规律的。以每小时为粒度的数据里最明显的模式是日周期早上六点开始爬升午间出现第一个高峰晚上七点到十点达到晚高峰凌晨跌入谷底。第二个模式是周周期工作日的负荷整体高于周末周一早高峰和周五晚高峰又有各自的形状。第三个是季节性夏季制冷负荷和冬季采暖负荷会把整体基线抬高。这三种周期性叠加在一起意味着模型不能只吃“过去 24 小时”的原始数值。常见做法是显式构造时间特征把周期信息编码成模型能理解的输入。小时、星期、月份直接作为数值特征往往不够用因为 23 点和 0 点在数值上差得很远但在周期意义上只差一小时。用正弦和余弦编码能把这种“循环距离”表达出来。对于小时特征编码公式是 sin(2π·hour/24) 和 cos(2π·hour/24)星期特征同理周期换成 7。滞后特征是另一个关键输入。电力负荷有很强的惯性t 时刻的负荷和 t-1、t-24、t-168即 7 天前同一时刻的负荷高度相关。把这些滞后值作为特征加入模型等于把“惯性”和“同期对比”直接喂给算法。滚动均值也有用比如过去 3 小时的滑动平均能反映短时趋势过去 24 小时的滑动平均能反映日基线水平。提示滞后窗口不要盲目开大。滞后特征越多训练样本的有效长度越短而且 LSTM 和 Transformer 这类模型本身就能从序列中学习依赖关系特征工程过度反而稀释了模型的注意力。2.2 缺失值与异常值的处理顺序不能反电力负荷数据来自 SCADA 系统或智能电表缺失和异常几乎不可避免。缺失通常表现为某几个整点没有数据或者一整段传输中断。异常则表现为负荷瞬间跳到离谱的值比如 0 或者正常值的五倍以上。处理顺序很关键先做缺失值插补再做异常值检测。如果先剔除异常值会在序列里制造新的空洞还得再插补一遍流程上多一道麻烦。缺失值插补用线性插值即可因为小时粒度的负荷曲线在短时间窗口内基本平滑分段线性插值足够不需要上样条或卡尔曼滤波。异常值检测采用“滚动窗口 标准差阈值”的办法对每个点计算其前后 6 小时窗口内的均值和标准差如果该点偏离均值超过 3 倍标准差就判定为异常并替换为窗口均值。这种方法的优点是自适应凌晨的低谷和晚高峰的尖峰各自有各自的基线不会出现用全局均值误杀高峰值的现象。import pandas as pd import numpy as np def clean_load_series(df, value_colload, window6, z_thresh3.0): data df.copy() data[value_col] data[value_col].interpolate(methodlinear, limit12) rolling_mean data[value_col].rolling(windowwindow * 2 1, centerTrue, min_periods1).mean() rolling_std data[value_col].rolling(windowwindow * 2 1, centerTrue, min_periods1).std() diff (data[value_col] - rolling_mean).abs() anomaly diff z_thresh * rolling_std data.loc[anomaly, value_col] rolling_mean[anomaly] return data这段代码先做线性插补limit12 意味着连续缺失超过 12 小时就不再插补需要人工介入。滚动窗口用 centerTrue 保证每个点的均值计算包含前后各 6 小时的信息这种对称窗口对突变点的定位更准确。z_thresh3.0 是经验值电力数据相对平稳3 倍标准差足够抓出真正的坏点如果数据噪声大可以放宽到 3.5。2.3 训练集、验证集、测试集必须按时间切分电力负荷预测不能随机打乱数据划分训练集和测试集。随机划分会把未来的数据泄露到训练集里模型等于偷看了答案。正确做法是按时间顺序切分假设数据覆盖一整年取前 10 个月做训练第 11 个月做验证最后一个月做测试。验证集用于模型选择和早停测试集只跑一次用于最终评估。这里有一个容易忽略的细节验证集和测试集不能紧挨着训练集中间应该留出几个星期的缓冲。原因是滞后特征会引用前序时间点的数据如果测试集紧贴训练集测试集前几个样本的滞后特征实际上来自训练集末尾虽然不构成标签泄露但会让评估结果偏乐观。留出缓冲期后每个样本的特征和标签在时间上完全自洽。小时级数据按时间切分还有一个好处就是可以方便地做滚动预测评估。比如在验证集上用训练好的模型预测接下来 24 小时然后把真实值并入历史窗口再预测接下来的 24 小时这样模拟真实调度场景中的“每天滚动预测一次”操作评估指标更贴近实际使用时的表现。3. 七类模型的选型逻辑与建模流程从 ARIMA 到 Transformer 的完整对照3.1 ARIMA 和 KNN先跑通基准再谈深度学习ARIMA 适合做基线。虽然电力负荷有强周期性但 ARIMA 本身不擅长捕捉多周期所以常用做法是先用 STL 或差分把季节性拆掉再对残差序列跑 ARIMA。另一条路是直接用 SARIMA设置季节性周期为 24但阶数稍微高一点就很容易爆内存或收敛失败。ARIMA 在这个系统里最大的价值是提供一个“传统方法下限”如果 LSTM 和 Transformer 连 ARIMA 都跑不过那大概率是数据预处理或特征构造出了错。from statsmodels.tsa.arima.model import ARIMA model ARIMA(train[load], order(2, 1, 2)) fit model.fit() forecast fit.forecast(steps24)order 中的三个值分别是自回归阶数、差分阶数和移动平均阶数。(2, 1, 2) 是小时数据的经验起点差分一次把趋势去掉。如果 AIC 仍然偏高可以尝试 (3,1,2) 或 (2,1,3)不要机器调参太多ARIMA 在这种场景里只是参照系。KNN 做时序预测的思路是找历史相似片段。把过去 24 小时的负荷序列作为一个“模板”在历史数据里搜索最相似的 k 个片段然后把这 k 个片段之后的那一小时负荷做加权平均作为下一小时预测。这个方法的优点是没有任何训练过程适合快速验证特征是否有效。但它的缺点也很明显对趋势外推无能为力如果未来一小时负荷持续爬升KNN 找相似片段时天然偏向“历史中相似但后续涨幅更小”的片段预测值会系统性偏低。3.2 决策树与随机森林特征重要性的免费体检决策树在时序预测里单独用容易过拟合但随机森林值得认真调一次。原因在于随机森林能输出特征重要性帮你确认“滞后 24 小时”和“滞后 168 小时”到底哪个信息量更大。这个结论可以直接指导后面 LSTM 和 Transformer 的输入窗口设计。随机森林做单步预测的输入是一维特征向量当前时间的小时编码、星期编码、节假日标记、滞后 1/2/24/168 小时负荷、过去 3 小时均值、过去 24 小时均值。输出是下一小时负荷。训练完成后查看 feature_importances_ 属性通常会发现滞后 24 小时和滞后 1 小时排在前两位这个信息比模型本身的预测精度还有价值。from sklearn.ensemble import RandomForestRegressor rf RandomForestRegressor( n_estimators300, max_depth12, min_samples_leaf3, n_jobs-1, random_state42 ) rf.fit(X_train_rf, y_train_rf) print(sorted(zip(feature_names, rf.feature_importances_), keylambda x: -x[1])[:5])n_estimators 设 300 是平衡时间和精度的常规值超过 500 收益很小。max_depth 限制在 12 防止过拟合到训练集里的某个极端日。这里有个细节随机森林的预测值是离散化空间里的均值天然会把峰值削平所以在晚高峰的预测上随机森林通常会偏低一些这不一定是 bug而是模型本身的性质。这一点在横向对比 LSTM 和 Transformer 时要注意不能因为随机森林削峰就全盘否定它。3.3 LSTM 与 GRU序列窗口怎么定损失函数怎么选LSTM 是这类系统的主力模型。它接受的是三维张量形状为样本数时间步长特征数。时间步长决定模型每次看多长的历史特征数由特征工程决定。经验值是把窗口设为 168 小时也就是整整一周。这个窗口足够覆盖日周期和周周期又不会长到让训练变得极慢。特征选择上小时编码、星期编码和滞后值保留额外加入一个“是否为工作日”的布尔标记让模型明确区分周末和工作日。import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, GRU, Dense, Dropout def build_lstm_model(input_shape): model Sequential([ LSTM(64, return_sequencesTrue, input_shapeinput_shape), Dropout(0.2), LSTM(32, return_sequencesFalse), Dropout(0.2), Dense(16, activationrelu), Dense(1) ]) model.compile(optimizeradam, lossmse, metrics[mae]) return model model build_lstm_model((168, num_features))第一层 LSTM 设置 return_sequencesTrue 是为了把完整的隐藏状态序列传给第二层第二层 return_sequencesFalse 只输出最后一个时刻的隐藏状态然后接全连接层输出预测值。Dropout 加在层间防止过拟合0.2 是常用起点。loss 用 mse 会让模型更关注误差大的点也就是负荷高峰时段如果希望所有时段均匀拟合可以换成 mae。GRU 是 LSTM 的简化版参数更少训练更快在小数据集上通常比 LSTM 稳定。它的结构只有两个门没有单独的细胞状态但在这个任务上精度差距几乎可以忽略。我的习惯是把 LSTM 和 GRU 都跑一遍用验证集上的误差决定最终选哪个而不是主观偏好。训练时的关键参数是 epoch 和早停。用 EarlyStopping 监控验证集损失patience 设为 10也就是验证损失连续 10 轮不下降就停止训练。这样既避免过拟合也省去手动挑 epoch 的麻烦。batch_size 用 128 还是 256 取决于数据量一年小时数据约 8760 个样本排除滞后窗口后略少batch_size 设为 64 或 128 更稳妥。提示LSTM 输入要做标准化负荷数值动辄几百兆瓦直接喂进 tanh 激活函数会让梯度饱和。用 sklearn 的 StandardScaler 按训练集的均值和方差做标准化注意测试集也要用同一个 scaler不能在测试集上重新计算均值和方差。3.4 Transformer用注意力替代循环但别忽略位置编码Transformer 用在电力负荷预测上核心优势是能同时关注远处和近处的依赖关系。LSTM 的信息传递是逐步的窗口 168 小时意味着信息要经过 168 步传递早期信息会衰减。Transformer 的注意力机制可以一步到位直接计算每个历史时刻对预测时刻的相关性。但 Transformer 有一个前置要求输入序列的每个位置需要有位置编码。Transformer 本身没有顺序概念如果不加位置编码“第 1 小时”和“第 50 小时”在初始输入上完全一样注意力机制无法区分它们的时间先后。常见做法是使用正弦位置编码公式为 PE(pos, 2i) sin(pos / 10000^(2i/d_model))d_model 是特征维度。对于小时负荷预测可以使用更简单的做法把时间戳的小时编码和星期编码拼在特征里替代位置编码效果同样可用而且实现更省事。Transformer 在训练时需要更多的数据。一年的小时数据大约 8000 多个样本对于 Transformer 来说偏少。用滑动窗口构造训练样本可以缓解这个问题窗口长度为 168每次滑动 1 小时生成一个样本一年数据能生成约 8000 个样本这在 Transformer 中属于小规模需要调小模型尺寸。d_model 设为 32 或 64head 数设为 4层数设为 2避免模型过大导致欠拟合。import tensorflow as tf from tensorflow.keras import layers def transformer_encoder_block(inputs, head_size8, num_heads4, ff_dim64, dropout0.1): attention layers.MultiHeadAttention( num_headsnum_heads, key_dimhead_size )(inputs, inputs) attention layers.Dropout(dropout)(attention) attention layers.LayerNormalization(epsilon1e-6)(inputs attention) ffn layers.Dense(ff_dim, activationrelu)(attention) ffn layers.Dense(inputs.shape[-1])(ffn) ffn layers.Dropout(dropout)(ffn) return layers.LayerNormalization(epsilon1e-6)(attention ffn) inputs tf.keras.Input(shape(168, num_features)) x layers.Dense(32, activationrelu)(inputs) x transformer_encoder_block(x, head_size32, num_heads4, ff_dim64) x layers.GlobalAveragePooling1D()(x) x layers.Dense(16, activationrelu)(x) outputs layers.Dense(1)(x) transformer_model tf.keras.Model(inputsinputs, outputsoutputs)Transformer 在负荷预测上也有一個经典翻车现象预测曲线比真实值滞后一拍。原因在于自回归式的训练方式让模型学会了“抄”上一时刻的值因为上一时刻和当前时刻强相关Loss 会迅速下降但最终预测曲线整体右移。缓解手段是训练时使用 teacher forcing同时在损失函数中加入一阶差分项即不仅惩罚预测值的误差也惩罚预测变化率的误差。差分损失让模型必须学会“变化的趋势”而不是简单复制上一步的数值。4. 多模型融合与评估单一模型的极限在哪怎么组合才有效4.1 误差分析先于模型选择分时段、分星期看指标很多人在模型对比上只看一个 MAPE平均绝对百分比误差这不够。电力负荷的误差结构是不均匀的晚高峰的绝对误差天然比凌晨高但相对误差反而更低。要真正判断哪个模型更适合这个场景必须分时段看误差。把测试集的预测结果按小时分组分别计算每个小时的 MAE会得到 24 个值。通常你会发现凌晨 2 点到 5 点的 MAPE 可能很高因为基数低分子一点点就导致百分比很大而晚高峰 19 点到 21 点的 MAE 最大但 MAPE 反而低。这带来一个取舍如果你的目标是电网调度中的容量预留应该重点关注 MAE 最大的时段如果你的目标是需求响应中的用户侧通知MAPE 更重要。两个指标不能只盯一个。按星期分组同样有信息量。周一的预测误差通常比其他工作日大因为周一早高峰的负荷受到前一个周末活动的影响较大。Transformer 在周一的表现可能比 LSTM 好而在普通工作日两者差不多。这时候单一模型的最优选择就不一定适用于整个一周可以考虑“分模型融合”的策略周一到周五用 Transformer周末用 LSTM或者反过来以验证集误差为准。4.2 加权融合与堆叠比单独调参更划算的收益多模型融合的收益在电力负荷预测里非常稳定。原因是不同模型的误差来源不同ARIMA 的误差来自无法建模节假日突变LSTM 的误差来自长时依赖衰减Transformer 的误差来自训练数据量不足。把这些误差进行对冲比在一个模型上死磕超参数更划算。最简单的融合方式是加权平均。权重不是在测试集上试出来的而是在验证集上用线性回归拟合各模型预测值与真实值的关系。from sklearn.linear_model import LinearRegression stack_X np.column_stack([ pred_arima, pred_rf, pred_lstm, pred_gru, pred_transformer ]) stack_y y_valid.values meta_model LinearRegression() meta_model.fit(stack_X, stack_y) weights meta_model.coef_这段代码的输出就是每个模型的融合权重。需要注意的是权重可能有负数代表某个模型在这个时段的表现系统性较差它对最终预测起到了“纠偏”作用。如果某个模型的权重是负数不要急着剔除它先看它对应的预测是否高度相关于另一个模型——多重共线性会让线性回归的系数失稳。遇到这种情况可以改用岭回归。堆叠模型的第二层也可以用随机森林或 LSTM但线性回归往往就够了。原因是第一层各模型的预测值已经高度线性相关于真实值第二层的主要工作是做校正非线性模型在这种场景容易过度拟合验证集得不偿失。4.3 调度场景的核心指标峰值误差和连续误差电网调度最关心的不是平均误差而是两个具体指标峰值时段误差和连续预测误差。峰值时段误差用“晚高峰最大绝对误差”衡量因为调度员最怕的是一天内最大负荷的预测值偏差过大这直接关系到机组容量是否足够。连续预测误差则用接下来 24 小时预测误差的累积值衡量如果误差始终偏正意味着系统持续高估负荷可能导致过度发电。这两种误差可以用一个简单的方法暴露出来在测试集上做滚动 24 小时预测画出每天的“预测误差累积曲线”。如果这条曲线总体向上走或向下走说明模型存在系统性偏差不是随机噪声。系统性偏差通常可以通过在最终预测上叠加一个常数校正项来消除这个校正项的值取验证集误差的均值。这种后处理手段效果直接而且不改变模型本身的结构。注意不要为了追求 MAPE 最低而做超参数穷举。电力负荷预测里模型稳定性比精度重要得多。一个在验证集上 MAPE 略高但标准差更低的模型在实际调度中比一个精调过的低误差模型更可靠。你可以用多次重复实验不同随机种子来确认模型的稳定性而不是只跑一次就拿结论。5. 系统落地的 5 个高频必踩坑现象、原因与解决办法5.1 时间索引没排序滞后特征全部错乱现象模型跑完回测前几个样本的预测值离谱到完全不可用。原因CSV 文件读入后行顺序是乱的按小时升序排列后才发现原始数据在导出时有乱序。滞后特征用的是 shift()shift 基于行号移动行号错乱时滞后值取到的是逻辑上完全无关的负荷。解决读入数据后先按时间列排序再 reset_index(dropTrue)然后才做滞后特征、滚动均值。这条是最容易踩的坑也是后果最严重的坑。5.2 划分训练集和测试集时用了随机切分现象LSTM 的测试集 MAPE 比训练集还低明显不科学。原因随机切分后测试集里包含了与训练集时间相邻的样本滞后特征直接引用到训练集标签造成泄露。解决改用时间顺序切分并且切分点之间保留缓冲期。验证方式是打印训练集最大时间和测试集最小时间确认两者不重叠且有间隔。5.3 多步预测把预测值当成真实值滚进去现象做未来 24 小时预测时用上一步的预测值作为下一步的输入结果越往后偏差越大到第 20 小时已经完全偏离真实负荷。原因递归多步预测会累积误差LSTM 和 Transformer 尤其明显因为它们的输出分布会随着递归逐步漂移。解决如果模型只支持单步预测就采用“滚动预测”策略——每次预测下一小时然后加入真实观察值再预测再下一小时。如果必须一次性输出 24 小时要使用 seq2seq 结构在解码器端用 teacher forcing 来训练。5.4 标准化参数用错了对象现象测试集误差远大于验证集误差。原因对测试集独立做了 StandardScaler而不是复用训练集的均值和方差。标准化的本质是用训练集的分布来归一化新数据如果测试集用了自己的统计量数据分布被重新扭曲了。解决把 scaler 保存为 pkl 文件训练完成后在推理阶段用同一个 scaler 做 transform禁止对测试集重新 fit。5.5 节假日特征没有单独处理现象全年 MAPE 看着正常但春节、国庆期间的预测结果完全失控。原因模型从历史数据里学到的周周期是以“自然星期”为单位的节假日打破了这一规律周二放假的日期在特征上与普通周二相同但负荷模式完全是另一回事。解决给节假日单独做二进制特征而且尽量用“放假前一天”“放假最后一天”这类相对位置特征单独用 0/1 标记还不够因为节假日前后各一天的负荷形态都有变化。6. 多步预测与模型部署技巧用滚动预测换取调度可用性实际电力调度系统要的不是“下一点预测”而是“未来 24 小时逐小时预测”。从单步模型到多步预测有一个常见的思路是独立多模型法训练 24 个模型第 k 个模型专门预测未来第 k 小时的负荷。每个模型只做一步预测输入是同样的历史窗口没有误差累积也不用担心递归衰退。缺点是需要维护 24 个模型的文件和代码工程上稍显繁重但换来的是稳定性和可解释性。如果团队人力紧张退一步用 seq2seq 或 Transformer 的解码器输出 24 个值也可以但必须接受一定的长期误差。我通常会在部署阶段做两件事。第一件是把预测结果加上“置信区间”不是统计学意义上的严格置信区间而是基于历史误差分位数的一个经验区间。比如计算验证集上每个小时的误差的 90% 分位数部署时预测值加减这个值得到调度可用的上下界。第二件是设置“模型回退机制”如果某天的预测值和前一天同一时刻的实测值偏差超过阈值系统自动切换用简单的移动平均模型输出而不是硬着头皮用复杂模型的异常预测结果。这一条救过我好几次模型不可能对每个突发的天气变化都反应得过来。数据更新频率也值得注意。电网负荷的模式会随季节缓慢漂移建议每周增量训练一次或者每个月做一次全量微调。小时级数据的训练成本不高LSTM 训练一轮大约几分钟完全能支撑常规更新节奏。每周重新训练模型的另一个好处是能捕捉到近期用电行为的变化比如某个区域新建了数据中心负荷曲线会明显改变不做增量训练的话这类变化要滞后几周才能反映到模型里。最后说一个小习惯。每次重新训练后我会把模型在新数据上的前 7 天预测误差和上一版模型对比如果误差没有下降就回滚到旧模型。模型回滚这件事听起来不高级但实战中真的让人少掉很多头发。时间序列模型不像分类模型那样有清晰的准确率红线它的退化是渐进的不对比你根本察觉不到。希望这些踩坑记录和落地思路能帮到你尤其是第一次把多模型负荷预测系统从 Jupyter Notebook 搬到调度平台上的时候少踩一个坑就能早一步交付。本文还有配套的精品资源点击获取