ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于MyEMS和LSTM的电力负荷预测实战:从数据清洗到95%精度部署

基于MyEMS和LSTM的电力负荷预测实战:从数据清洗到95%精度部署 我做能源管理系统落地有五六年了最深的感触是能耗大屏做得再漂亮本质还是在把已经发生的事重新讲一遍。真正让业主愿意长期用这套系统的是你能不能提前告诉他明天上午十点厂房大概用多少电能不能提前半小时把冷机策略调好。这次聊的是我们在 MyEMS 这个开源能源管理平台上用 LSTM 神经网络把电力负荷预测精度做到 95% 左右回归任务里通常对应 R²≈0.95 或 MAPE≈5%的全过程包括数据怎么洗、特征怎么构造、模型怎么调、最后怎么部署回去和业务联动。1. 能源管理从“事后统计”走向“事前预测”1.1 负荷预测到底在解决什么业务问题很多刚接触能源管理的人会问电表数据都在采集了历史曲线也画得出来为什么还要专门做负荷预测这里面的差异不是“多一张曲线图”而是整个能源调度逻辑的转变。举一个工厂的例子。配电房里有两台变压器平时一台运行、一台备用。如果明天上午有一批大功率设备要同时启动负荷可能冲到变压器额定容量的 90% 以上。没有预测时电工只能凭经验判断“大概差不多”。有预测时系统会在前一天晚上给出 24 小时负荷曲线明确标出 10:00—11:30 会出现负荷尖峰调度就能提前决定要不要错峰启动、要不要提前投入第二台变压器甚至和电网侧的需量管理联动起来。这就是负荷预测在能源管理里的核心价值它不是替代电表而是给电表数据加上“时间方向”。企业侧可以用于需量控制、设备轮值、冷机/热泵的蓄能策略电网侧可以用于需求响应、配电容量规划。MyEMS 作为开源能源管理平台本身已经做了能耗采集、设备监控、分项计量和报表预测模块正好能补上“未来”这块拼图。1.2 MyEMS 在预测链路里的位置MyEMS 的架构通常分三层底层是仪表和设备的数据采集Modbus、M-Bus、BACnet 等中间是数据存储和服务端 API上层是 Web 可视化和告警。我们要做的负荷预测模块并不会去替代这三层而是在第二层和第三层之间加一个预测服务。我自己的做法是让预测服务独立于 MyEMS 主进程运行它从 MyEMS 的数据库里读取历史负荷数据完成特征构造和模型推理再把预测结果写回 MyEMS 的一张预测表中。这样做的原因是神经网络训练和推理会占用 CPU/GPU并且 Python 环境和 MyEMS 主服务的技术栈不一定一致。解耦之后即便模型服务重启也不影响采集和展示主链路。从数据流上看链路是这样的采集层电表每 15 分钟上报一次有功功率写入 MyEMS 的历史表特征层从历史表取近 N 天的负荷序列叠加温度、星期、节假日等外部特征模型层LSTM 模型读取滑动窗口数据输出未来 15 分钟到 24 小时的预测曲线业务层预测结果写回数据库在 MyEMS 前端图表展示或触发需量告警、设备联动。这套结构里MyEMS 承担的是“数据底座 展示出口”LSTM 承担的是“时序建模”两者各司其职也方便后期更换模型。2. LSTM 在负荷预测里到底强在哪2.1 从 RNN 梯度消失聊到细胞状态很多资料一上来就贴 LSTM 的公式但我想先聊聊它为什么会出现。RNN 在处理序列数据时逻辑上是在每个时间步共享同一套参数把上一时刻的隐状态传给下一时刻。这种方式对短序列没问题序列一长梯度在反向传播过程中反复相乘容易出现梯度消失或梯度爆炸结果是模型记不住太久之前的信息。LSTM 的解决思路是引入一条“细胞状态”的传送带。这个比喻非常贴切你可以把细胞状态看成一条贯穿整个序列的公路信息可以在上面几乎无损地流动而遗忘门、输入门、输出门则是公路上的收费站决定哪些信息要丢弃、哪些要写进去、哪些要输出给下一层。这样一来模型就能保留数小时甚至数天前的重要模式而不是像普通 RNN 那样“记性”只停留在最近几步。电力负荷数据恰恰是强序列依赖的典型今天上午 9 点的负荷不仅和最近半小时的趋势有关还和昨天同一时段、上周同一天工作日/周末的模式高度相关。LSTM 的门控机制让它有条件把“昨天上午 9 点”这个长期依赖信息保留下来。2.2 负荷数据的三个时间特征与 LSTM 的记忆窗口电力负荷数据有非常明显的周期特征我把它拆成三个时间尺度来看日内周期性一天 24 小时负荷曲线往往呈现“早高峰—午间波动—晚高峰—夜间低谷”的形态工厂、商场、写字楼各有不同周周期性工作日和周末的负荷曲线差异明显周一早上往往有开工冲击周五下午可能有提前收尾季节周期性夏季空调负荷和冬季采暖负荷会把整体基线抬高但这个周期跨度太长单靠 LSTM 难以直接捕捉一般需要用外部特征辅助。LSTM 擅长捕捉前两个尺度尤其是日内和周内模式。比如设定滑动窗口为 7 天、步长为 15 分钟的输入序列模型在每个时间步都能看到过去 7 天的同时段信息等于把“上周二上午 10 点是什么负荷水平”直接塞进了输入里让模型自己学出周期性权重。这里要特别说明LSTM 不是万能的。负荷预测里还有一些非线性影响因素比如气温骤降、节假日调整、生产线临时检修这些信息并不完全包含在历史负荷序列中。所以纯序列模型只能打底最终要拿到 95% 级别的精度一定要想办法把外部特征送入模型这点我会在特征工程部分展开。3. 在 MyEMS 里搭建 LSTM 预测链路3.1 数据清洗与训练集构建模型效果的上限由数据质量决定这个话我说过很多次。MyEMS 数据库里的原始负荷数据并不总是干净到可以直接训练最常见的几类问题如下采集中断设备离线、通信瞬断导致某段时间数据为空异常尖峰大设备启动瞬间、电表脉冲抖动出现明显偏离正常范围的负荷值时间戳不齐不同电表的采集时间点有偏移直接拼接会导致序列错位换表/改接线数据量纲或归属发生变化前后不连续。我的处理方式是分三步。第一步是时间戳对齐把所有数据按 15 分钟粒度重采样缺失值先看前后趋势如果缺失不超过 2 个点用线性插值补齐如果缺失超过 2 个点优先用前一天同时间段的值填充实在不行就丢弃这段。第二步是异常值过滤用滚动窗口的中位数和标准差做判断超过中位数 ±3 倍标准差的值标记为异常但并不直接删除而是根据业务判断是真实冲击还是采集噪声。第三步是归一化把负荷值缩放到 [0,1] 区间LSTM 对输入尺度敏感不归一化会导致训练不稳定。训练集构建时要注意数据泄漏问题不能用未来的信息去预测过去的点。所以划分训练集、验证集、测试集时必须以时间顺序切分绝不能随机打乱。我的做法是取前 80% 的时间段做训练接下来 10% 做验证集用于调参最后 10% 做测试集用于评估最终精度。3.2 特征工程负荷滞后项、温度、日历变量LSTM 能自动从序列中学习特征但“自动学习”不等于不需要特征工程。相反外部特征的构造质量直接决定了模型能否从 85% 走到 95%。我在这个项目里用了三类特征负荷滞后项当前时刻往前推 1 个点、2 个点、96 个点、96×7 个点的负荷值。96 是 24 小时 × 415 分钟一个点这个滞后项直接告诉模型“昨天同一时刻”和“上周同一时刻”的负荷水平温度与湿度对商业楼宇和工厂空调系统是负荷大户。温度特征需要做衰减处理因为建筑有热惯性当天最高气温的影响力往往滞后几个小时。我一般用过去 24 小时温度的指数加权平均而不直接用瞬时温度日历变量星期几独热编码、是否工作日、是否节假日、当前小时数。这一组变量帮助模型区分工作日和周末的负荷形态差异。特征构造有个容易踩的坑外部特征的时间对齐。预测未来 1 小时时温度可以用天气预报值但滞后项只能取到“当前时刻之前”的数据因为未来时刻的真实负荷还没发生。训练时如果不注意这一点把未来时段的真实值当特征喂进去测试时就没有这样的特征可用精度看起来很高一到线上就崩塌。3.3 模型结构与超参数选择LSTM 模型的结构不宜太复杂。我的基准配置是输入层滑动窗口长度 96×77 天数据每个时间步包含负荷值和外部特征第一层LSTM隐藏单元 64返回序列第二层LSTM隐藏单元 32不返回序列全连接层Dropout 0.2 后用 ReLU 激活输出层预测未来 N 个时间步的负荷值用线性激活。为什么用两层而不是更多层因为负荷预测本质上是强周期信号的拟合单层 LSTM 可能欠拟合但三层以上很容易过拟合而且训练时间成倍增加。64-32 这个配置是我在多个数据集上对比后的折中方案不是说一定最优但作为起点足够稳定。超参数里最敏感的三个滑动窗口长度、预测步长、学习率。我用一维搜索的方式调参每次只动一个变量。比如窗口长度从 241 天试到 33614 天发现 96×7 附近效果最好太短抓不到周周期太长引入过多噪声。学习率初始设为 0.001用 Adam 优化器当验证集损失连续 5 个 epoch 不下降时把学习率减半。3.4 训练、验证、测试的时间切分策略时间序列的训练切分和普通机器学习不一样不能随机打乱这点我再强调一次。我用的是“滚动训练”策略假设我们有 120 天的数据。第一次训练用前 70 天数据训练后 30 天做测试第二次训练把训练集向后移 15 天用 701585 天训练再预测后面的 30 天。这样滚动四次得到四组测试结果误差更稳健。评估指标我用两个。第一个是 MAPE平均绝对百分比误差它最直观可以直接说“预测误差在 5% 左右”第二个是 R²它反映模型对负荷波动的解释能力。95% 准确率在回归语境下通常就是 MAPE 在 5% 左右或者 R² 在 0.95 左右。单一指标容易骗人比如 MAPE 在夜间低负荷时段会因为分母太小而显得很高所以我同时关注分时段的误差表现。4. 把精度从 85% 调到 95% 的关键细节4.1 先跑一个简单基线认识数据上限我见过太多人上来就调 LSTM调了半个月还不如一个简单的“昨天同一时刻”预测准。这不是模型不行而是没有先建立基线。基线模型是预测领域的“地心参考系”没有它你不知道复杂模型的增量到底在哪里。我们的第一个基线是持久性预测就是直接用“过去 7 天同一时刻的负荷平均值”作为预测值。这个极端简单的模型在强周期数据上通常能达到 75%—85% 的精度。结果一跑我们发现数据本身规律性很强给后续 LSTM 留下空间。第二个基线是线性回归特征用滞后项、温度、日历变量不加任何神经网络。线性模型在 15 分钟粒度的负荷预测上往往能做到 85% 左右。如果线性回归连 85% 都到不了通常是特征工程有问题而不是模型不够深。我的习惯是先让线性模型跑出基线再上 LSTM如果 LSTM 对比线性回归提升不到 3%就要反思是模型结构问题还是数据质量问题而不是盲目加深网络。4.2 滑动窗口长度和预测步长的平衡窗口长度和预测步长是一对需要平衡的矛盾。窗口越长模型能看到的历史信息越多但计算量也越大而且过长的序列会把相关性弱的历史噪声也学进去。预测步长越长不确定性越大精度天然会下降。我在 MyEMS 项目里做了两个模型而不是一个万能模型。第一个模型预测未来 1 小时4 个时间步用于实时需量控制和设备联动窗口长度设为 96×33 天精度最高。第二个模型预测未来 24 小时用于日报和调度计划窗口长度设为 96×77 天虽然远期精度会下滑但整体曲线形态可控。经验是不要试图用一个模型同时吃下“短期高精度”和“远期趋势”两个目标。如果业务上两者都需要就拆成两个模型各自的输入输出设计都更简洁训练和调试也更快。4.3 节假日与极端天气的特征处理节假日是负荷预测最容易翻车的地方。春节、国庆这种长假期间工厂停产、写字楼空置负荷曲线跟平时完全不是一个形态。如果训练集里包含大量普通工作日模型会把节假日预测成普通工作日MAPE 直接飙到 20% 以上。我的处理方式是给节假日单独建一个掩码特征不只是“是否节假日”这个 0/1 变量还要区分节假日类型节假日第一天、节假日前一天、节假日后一天。这三天各有不同节假日前一天往往有收尾加班节假日后一天有开工冲击。这个特征对工厂和写字楼尤其重要。极端天气更麻烦。比如 35 度以上的高温天空调负荷急剧上升但罕见高温在历史数据里出现次数少模型学不到足够的样本。我在特征里引入了“温度偏离历史同期均值的差值”相当于把极端天气变成偏离度而不是绝对温度。这样一来模型在高温天来临时输入特征中的偏离度明显变大预测值会对温度异常更敏感。4.4 损失函数与评估指标的选择LSTM 训练默认用 MSE均方误差作为损失函数它把所有样本的误差平方后取平均对大误差样本更敏感。这在负荷预测里有好处也有坏处。好处是模型会尽量避免出现大的偏差尤其是高峰负荷预测坏处是夜间低负荷时段绝对误差很小但相对误差很大MSE 并不区分这些。我实际用的策略是在 MSE 之外增加一个分时段权重高峰时段的样本权重设为 1.5低谷时段设为 1.0。这样模型把更多容量分配到业务更关心的高峰时段。损失计算时只做权重乘法实现上非常小但对业务价值的提升很直接。评估指标不能只看一个。我在测试集上同时计算 MAPE 和 R²并且额外统计“高峰时段 MAPE”和“峰谷命中率”。峰谷命中率是指预测曲线和实际曲线在尖峰出现时间上的重合程度这对需量控制特别重要。有些模型整体 MAPE 好看但尖峰时间点错位业务上依然不可用。5. 把模型部署回 MyEMS并让它稳定跑起来5.1 模型导出与预测接口封装训练好的 PyTorch 模型不能直接丢给 MyEMS 用需要先导出。我一般把模型权重保存为 ONNX 格式这样可以用 ONNX Runtime 做推理不需要在部署环境里装完整的 PyTorch启动更快、内存占用更低。推理服务我用 FastAPI 写成一个独立的 Python 服务暴露两个接口POST /predict/short_term输入最近 3 天负荷序列和天气预报数据返回未来 1 小时逐 15 分钟的预测值POST /predict/daily输入最近 7 天数据和次日日历信息返回次日 96 个点的预测曲线。接口内部做的事情是一样的从请求参数构造特征矩阵调用 ONNX Runtime 执行模型推理后处理还原归一化得到实际负荷值返回 JSON。MyEMS 后端只需要用 HTTP 请求调用这个服务不需要关心模型实现细节。5.2 定时重训练与预测结果回写模型上线不是终点而是起点。负荷模式会随着季节变化、生产线调整、业态改变而漂移固定权重用一年精度一定会下降。我设置了每周一次的自动重训练任务用最近 8 周数据重新训练模型训练完成后自动跑一遍验证集如果验证集 MAPE 优于旧模型才替换线上权重否则保留旧模型并触发告警。预测结果的回写需要设计好数据表。我在 MyEMS 数据库里新建了一张预测表包含测点 ID、预测时间戳、预测值、模型版本号、生成时间。MyEMS 前端的图表组件只需要多关联一张表就能在原有历史曲线后面追加未来曲线。这里要注意时区问题MyEMS 的数据通常用本地时间存储预测服务也必须使用同一时区否则每天 0 点的划分会对不齐。5.3 极端天气和模型失效的兜底策略模型再准也会遇到没见过的情况。暴雨、台风、气温骤降超过 10 度都会让负荷规律发生突变。我在线上配置了兜底策略当天气预报的温度偏离模型训练集温度范围超过一定阈值时自动把预测结果标记为“低可信度”并在前端图表上用不同颜色显示。此时调度人员会参考预测但不会完全依赖。同时系统会自动切换到一个更保守的规则模型比如按历史同期的 90% 分位数预测宁可高估也不低估避免变压器过载。另外预测服务本身要监控。如果连续多次接口超时或返回异常MyEMS 侧要能感知到在告警中提示“预测服务不可用”并继续用上一次成功生成的预测结果保证业务不中断。6. 运行半年后的维护心得6.1 季节交替时重训节奏比模型结构更关键我们项目是三月份上线的第一版模型在春夏季节表现很好MAPE 稳定在 5% 左右。到了六月天气转热空调负荷抬头旧模型的 MAPE 开始往 7%—8% 爬。这时候我意识到不能只按固定周期重训还要看模型漂移信号。后来我把训练策略改成每周固定重训一次同时每天计算线上预测和实际值的滚动 MAPE。如果滚动 MAPE 连续三天上升超过 1 个百分点就触发一次临时重训。这样在换季期间模型能在两三天内追上负荷形态的变化。6.2 监控模型漂移的几个信号模型漂移不能等业务投诉了才发现。我总结三个日常监控信号一是误差分布变化。如果预测误差从零均值变成系统性偏高或偏低说明模型可能有偏置比如负荷整体上涨但模型还停留在过去水平。二是峰谷时点偏移。如果预测曲线和实际曲线形态相似但尖峰出现时间每天都差半小时可能是采集时间戳出了偏差或者日历特征没对齐。三是节假日模式的重新出现。如果每到周末预测误差就增大但训练集里一直包含周末数据就要怀疑是独热编码和季节特征的交互出了问题。这三个信号不需要复杂的运维系统写几个 SQL 查询每天跑一次输出到一个表就行。重要的是坚持记录否则漂移发生时手里没有任何可对比的数据。6.3 如果只让我给一条建议如果只给一条建议我会说别把时间花在堆模型结构上把精力放到数据质量和业务理解上。我第一次做负荷预测时总觉得 LSTM 不够强换成注意力机制、Transformer折腾两周效果还不如把天气数据洗干净加进来。后来想明白了负荷预测的难点不在“模型能不能学到复杂的非线性关系”而在“你给模型喂的信息里有没有覆盖足够的业务变化”。要覆盖变化就必须持续跟踪业务现场工厂是不是加了产线、商场的空调是不是换了主机、周末的营业时间是不是做了调整。这些变化会直接反映在负荷模式上但不会自动出现在训练数据里。模型的作用是在数据和业务理解都到位的前提下把规律拆出来。数据质量和业务理解到位了95% 的准确率是水到渠成的事。最后分享一个我们在项目里的小技巧预测模块上线后不要只看整体 MAPE一定要把预测曲线和实际曲线画在同一张图上每天翻一翻。很多时候误差不是算出来的是看出来的。某个时段系统性偏差肉眼扫一眼就能发现远比等到 MAPE 恶化再去查日志高效得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进