先说结论:这套方案我在园区能管项目里真刀真枪跑过,用 MyEMS 采集数据、LSTM 做负荷预测,最终 MAPE(平均绝对百分比误差)控制在 5% 以内,也就是业务上常说的“95% 准确率”。它不是靠调一个神奇的网络结构调出来的,而是从数据采集、特征工程、模型训练到部署运维一整条链路打磨出来的结果。这篇文章把我的完整做法、参数细节、踩过的坑一次讲清楚。
这篇内容适合谁?一类是做能源管理平台、想给客户加“预测能力”的工程师,另一类是刚入门时间序列预测、想找一个真实业务落地案例的算法同学。你不需要已经有 MyEMS 的部署经验,只要懂基本的 Python 和机器学习概念,跟着思路走就能复现个大半。
1. 一个 95% 准确率的前提:先搞清负荷预测在能源管理里到底解决什么问题
1.1 MyEMS 是什么,为什么选它做数据底座
MyEMS 是一套开源能源管理平台,核心能力是采集电表、水表、气表等计量设备的读数,做数据归档、能耗分析、成本分摊和报表展示。它自带设备管理、数据采集、数据存储和可视化页面,底层数据落在数据库里,有清晰的表结构。对我来说它最有价值的一点是:不用从零造一套采集和存储系统,可以直接把精力放在“预测”这件事上。
在做负荷预测之前,平台里已经有持续运行三个月以上的历史数据——这是我能把模型训练出来的物质基础。很多算法项目死在“没有数据”,MyEMS 的角色就是把这一步补上。你可以把它理解成:MyEMS 负责“记账”,我的 LSTM 模型负责“根据账本预测未来花销”,两者是上下游关系。
1.2 负荷预测具体预测什么,颗粒度怎么定
工业场景里,负荷预测通常分三种颗粒度:超短期(未来 5 分钟到 1 小时)、短期(未来 1 天到 7 天,通常按 15 分钟或 1 小时一个点)、中长期(月度、年度)。这次项目做的是短期预测里的“日前预测”:每天凌晨用过去 7 天的数据,预测未来 24 小时逐时的用电负荷。
选择这个颗粒度的原因是它直接对应能源管理里的几个实际需求:一是参与需量申报,预测不准会被罚款;二是给储能系统做充放电策略;三是帮运维人员提前发现异常负荷。我们首先落地的是需量管理场景,因为园区变压器容量有限,如果预测到明天某个时段负荷会超过阈值,可以提前安排部分产线错峰生产。
1.3 “95% 准确率”到底是什么口径
这个必须说清楚,因为“准确率”在算法圈和业务圈的理解经常不一样。我这里的口径是:MAPE = 5%,也就是对每个预测点计算|实际值 - 预测值| / 实际值,取平均后得到 0.05,业务上就习惯说“准确率 95%”。
这里要注意两点:第一,MAPE 对于接近零的负荷值会很敏感,分母太小会导致误差爆炸,所以夜里低谷时段的误差通常会偏大;第二,单纯看 MAPE 还不够,我同时会盯 RMSE(均方根误差)和 R²,因为 MAPE 有可能被大量“好预测”的小负荷点拉低,掩盖掉几个大偏差点。业内评估模型,从不只看单指标。
2. 数据决定上限:喂给 LSTM 之前,我做了这三层处理
2.1 数据采集与对齐:15 分钟粒度比 1 小时粒度好使
MyEMS 的采集器支持定时从智能电表读数据,默认可以按 15 分钟或 1 小时归档。我最终用的是15 分钟粒度,一个原因是用更细的时间步能让 LSTM 学到负荷曲线里面更丰富的形状,比如午休和下班前的短暂抬升;另一个原因是做日前预测时,15 分钟粒度可以输出 96 个点的预测曲线,对调度更有参考价值。
采集环节踩过一个坑:不同电表的采集时间戳不是严格对齐的,有的设备会在整点后 1-2 秒上报,有的会提前。我在预处理时统一做了“时间戳重采样”,把数据按 15 分钟区间聚合,用区间内的平均值填充每个时间桶。这一步不做,后面构造时序样本时会出现很多 NaN 和错位。
2.2 缺失值与异常值:宁可插值,不要硬撑
MyEMS 的采集链路偶尔会断,尤其是现场通信不稳定的时候,数据里常见的问题是:连续缺失几小时、单点跳零、数值突变到正常值的两三倍。我的处理策略是:
- 短时间缺失(小于 3 个小时):用前后向插值补上,简单有效;
- 长时间缺失(超过半天):不硬猜,直接剔除这一段,避免把模型带偏;
- 单点异常(比如某时刻负荷突变为前值的 5 倍以上):用 3σ 法则或者中位数滤波把它拉回正常范围,但要注意结合业务判断——如果那一天真的有大设备启动,就不该一刀切。
插值和清洗的尺度,取决于你对业务的了解。我自己踩过最痛的一坑是:把一次真实的“双炉同时开启”尖峰当异常值剔掉了,结果模型学不到冲击性负荷的模式。建议清洗前先画一次曲线、肉眼过一遍现场日志,再定规则。
2.3 特征工程:节假日、温度、滞后特征都往里塞
LSTM 虽然是神经网络,能自动学特征,但喂进去什么仍然决定了它的上限。我的输入特征分三类:
第一类是负荷历史值:用过去 168 个时间步(即 168 × 15 分钟 = 42 小时,但这里其实取的是 7 天×24 小时=672 个 15 分钟点)作为滑动窗口。窗口太短学不到周周期,太长又拖慢训练。最终我选了 96(一天)+ 672(一周)两种窗口做过对比,672 在周周期明显的场景下效果更好。
第二类是时间特征:小时、星期几、是否节假日、是否周末。这些要做成数值编码,比如小时用sin/cos双通道编码,避免 23 点和 0 点在数值上被拉得很远。
第三类是外部特征:温度、体感温度、天气状况。园区空调负荷占比高时,温度和负荷强相关。这里我不建议无脑塞一堆天气指标,加两个最相关的就行,特征多了反而容易过拟合。
3. 用 LSTM 建模负荷曲线:从公式到训练的一步步实操
3.1 为什么是 LSTM,它靠什么记住用电习惯
负荷预测本质是一个时间序列预测问题。传统方法里 ARIMA 能处理线性趋势,但面对负荷数据里那种“工作日和周末形状不同、节假日突然塌陷、高温天整体抬升”的复杂模式就力不从心。LSTM 是循环神经网络(RNN)的一种变体,它通过引入“门”机制来解决普通 RNN 的长期依赖问题。
这里用一个生活化类比:普通 RNN 像一个只有短期记忆的人,你跟他聊天,他只能记住最近一两句话;LSTM 则像一个带笔记本的人,他可以决定哪些信息要记住(输入门)、哪些要忘掉(遗忘门)、哪些要写进新的记忆里(候选状态),最后决定本时刻输出什么(输出门)。用电负荷里“上周二下午的峰值”这种相隔很久但规律性的信息,恰好就是 LSTM 能捕捉到的。
核心公式层面,LSTM 在时间步 t 的隐藏状态更新可以简化为:
- 遗忘门:决定上一时刻的记忆
C_{t-1}哪些被保留; - 输入门:决定当前输入
x_t中哪些新信息写入记忆; - 候选记忆:通过
tanh变换生成新内容; - 输出门:根据当前状态决定输出
h_t。
公式长这样(省略具体矩阵符号):
f_t = sigmoid(W_f · [h_{t-1}, x_t] + b_f) # 遗忘门 i_t = sigmoid(W_i · [h_{t-1}, x_t] + b_i) # 输入门 C_tilde_t = tanh(W_c · [h_{t-1}, x_t] + b_c) # 候选记忆 C_t = f_t * C_{t-1} + i_t * C_tilde_t # 更新记忆 o_t = sigmoid(W_o · [h_{t-1}, x_t] + b_o) # 输出门 h_t = o_t * tanh(C_t) # 隐藏状态理解这套公式不要求你手推反向传播,但要知道:记忆 C_t 是贯穿时间步的“传送带”,门的开关决定了信息的去留。这就是 LSTM 能记住“一周前同时段”模式的原因。
3.2 网络结构设计:不是越深越好
试过几组结构之后,我最终定下来的是一个比较克制的网络:
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam model = Sequential([ LSTM(64, return_sequences=True, input_shape=(window_size, n_features)), Dropout(0.2), LSTM(32, return_sequences=False), Dropout(0.2), Dense(16, activation='relu'), Dense(1) ]) model.compile(optimizer=Adam(learning_rate=0.001), loss='mse', metrics=['mae'])参数解释一下:第一个 LSTM 层设了return_sequences=True,是为了把完整的隐藏状态序列传给第二层;第二层输出一个向量,经过全连接层映射到单值预测。Dropout 0.2 是防过拟合的,一开始不加 Dropout,训练集 loss 降得很漂亮,但验证集误差在十几个 epoch 后就开始反弹,加了 Dropout 之后明显稳住。
至于为什么不用更深的结构,我对比过三层 LSTM 的效果,收益不到 0.3% 的 MAPE,训练时间却涨了一倍多。在时序预测这种数据量不算海量的场景里,浅层网络往往是性价比之王。
3.3 训练细节:数据切分、归一化、早停一个都不能少
训练时的数据处理顺序我踩过很多次坑,这里按最终正确版讲:
第一步,切分数据集。时序数据不能随机打乱切分,否则会造成未来信息泄露。我按时间顺序切:前 80% 训练,后 20% 作为验证集。验证集不参与训练,只用来观测过拟合。
第二步,归一化。LSTM 用 tanh 和 sigmoid 作为激活函数,输入特征如果不缩放到相近范围,梯度会不稳定。我对所有数值型特征做 MinMaxScaler 缩放到 [0,1],注意:scaler 只能在训练集上 fit,再用这个训练好的 scaler 去 transform 验证集和未来的实时数据。否则验证集信息会被“偷看”。
第三步,构造滑动窗口样本。用timeseries_dataset_from_array或者手写循环把原始序列切成长度为window_size的样本块,每个样本的标签是下一个时间点的值。
第四步,训练与早停。我用 batch_size=64,epochs 上限 100,加 EarlyStopping,patience=10,监控验证集 loss。实际训练中大约跑到 30-40 个 epoch 就会触发早停,单卡 GPU 训练一轮只需几分钟,CPU 上跑也能忍受。
训练完成后,保存模型文件,同时把 scaler 参数也保存下来。很多人只存模型不存 scaler,上线推理时数值全乱,这个细节特别容易忽略。
3.4 评估指标怎么读,95% 准确率怎么算出来的
模型评估我用三个指标一起看:
| 指标 | 公式 | 我项目上的数值 | 说明 |
|---|---|---|---|
| MAPE | mean( | y - y_hat | / y) |
| RMSE | sqrt(mean((y - y_hat)^2)) | 32.5 kW | 对大偏差敏感,看峰值时段是否离谱 |
| R² | 1 - SS_res / SS_tot | 0.96 | 拟合优度,越接近 1 越好 |
有一点要提醒:MAPE 在凌晨低谷时段会很难看,因为负荷绝对值小,误差占比被放大。如果业务方需要全时段都好看,可以考虑对低谷时段单独加权,但我个人不建议为了指标好看而强行加权,预测的物理意义更重要。
4. 从 Jupyter 到生产:模型怎么跑进 MyEMS 的日常流程里
4.1 整体架构:采集、训练、推理三条链路
模型在笔记本里跑到 95% 准确率只算成功了一半,真正让它发挥作用的是把预测流程接入 MyEMS 的日常运转。我的部署架构分三块:
- 训练链路:每天凌晨 2 点定时触发训练脚本,从 MyEMS 数据库拉取最近 90 天的干净数据,重新训练模型,计算验证集指标,如果指标达标就替换线上模型;
- 推理链路:每 15 分钟触发一次预测任务,读取最近 7 天的历史负荷与天气数据,调用已训练模型,输出未来 24 小时的 96 点预测曲线;
- 展示链路:预测结果写回数据库,MyEMS 前端用 ECharts 绘制实际值曲线和预测值曲线的对比图,同时设置阈值告警。
4.2 和 MyEMS 对接的几个关键点
MyEMS 的数据表结构是按设备、计量点、数据项组织的。我在对接时主要看两张核心表:一张是实时数据归档表,存原始读数;另一张是统计汇总表,存按时间粒度聚合后的值。预测模型消费的是聚合后的值,所以我在 SQL 层面先做了一次按 15 分钟粒度的聚合查询,用一个视图包装历史负荷变化,训练脚本直接查视图,代码更简洁。
预测结果我用单独的自建表存储,字段包括:预测时间点、预测值、实际值、模型版本号、运行时间。记录模型版本很重要,线上跑久了你会需要对预测效果做回溯分析,没有版本号根本不知道当前曲线是哪一版模型产出的。
这里还有一个经验:推理脚本不要直接用训练时的 Python 环境。我单独做了一个精简的推理服务,只安装 tensorflow-cpu 和 pandas,体积小、启动快,也方便用 systemd 或 Docker 守护。训练脚本和推理脚本拆分之后,训练出问题不会影响线上预测。
4.3 预测结果怎么用起来:告警与调度建议
模型输出的 96 点曲线,最有价值的用途是组合成一个“预测告警器”。我写了一个规则引擎:如果未来 24 小时内任意连续 30 分钟的预测负荷超过变压器额定容量的 85%,系统自动给运维人员推送告警,建议提前错峰。实测下来,这套机制能提前 4-8 小时发现潜在的尖峰风险,比事后看报表报警及时得多。
另外一个用法是给储能充放电策略做输入。储能的控制器拿到预测曲线后,可以在负荷低谷时段安排充电,在预测尖峰时段放电,削峰填谷的同时还能省一笔需量电费。这一步的价值其实超过“预测准确”本身——准确率再高,用不起来也只是个数字。
5. 实测排雷:这份复盘能帮你少走很多弯路
5.1 高频踩坑问题速查表
| 现象 | 根因 | 解决方案 |
|---|---|---|
| 训练 loss 降不下去 | 数据没归一化或特征分布差异过大 | 对特征做 MinMaxScaler,训练集 fit 验证集 transform |
| 验证集误差比训练集大很多 | 过拟合 | 加 Dropout、缩小 LSTM 单元数、提前早停 |
| 预测曲线整体偏低 | 节假日数据被当成普通工作日学 | 加入节假日特征,或在训练时对节假日样本加权 |
| 凌晨低谷时段 MAPE 巨大 | 负荷绝对值小导致误差比例放大 | 业务口径上按峰平谷分段评估,不混在一起看 |
| 模型上线一周后预测越来越差 | 季节变化或生产计划调整导致数据漂移 | 每日增量数据参与重训,监控预测误差超过阈值自动告警 |
| 预测值出现“阶梯状” | 输入特征里小时编码方式不连续 | 小时用 sin/cos 编码,避免 23 点和 0 点数值断层 |
5.2 节假日预测为什么难,我是怎么处理的
工厂和园区的负荷曲线里,节假日是最大的“特异点”。工作日早八点到晚八点负荷高,周末明显降低,法定节假日直接变成一条平线。如果模型没有节假日特征,它会把节假日的平线也预测成工作日曲线,MAPE 一下子就飙到 20% 以上。
我的处理方式:一是把“是否节假日”作为二进制特征喂进模型;二是节假日样本在训练集中占比太少(一年没几天),我会在训练时给节假日样本加权,让模型对这类样本更敏感。效果立竿见影,节假日的 MAPE 从 18% 降到了 7% 左右。
另外一个隐藏问题是节后恢复:长假结束后的第一个工作日,负荷往往不会立刻回到节前水平,而是分几天爬坡。这种“恢复效应”单靠模型很难自动学会,我的做法是在节假日特征之外,再加一个“距最近节假日结束的天数”特征,让模型能学习恢复节奏。
5.3 关于“95% 准确率”我最后想说的
很多人问是不是换一个更先进的模型——比如 Transformer、Informer——就能从 95% 提高到 98%?我的观点是:在单一园区级负荷预测场景里,LSTM 已经吃掉了大部分可学习的信息,剩下那 2-3% 的误差更多来自不可预测的随机事件(临时加班、设备故障、天气突变),换模型解决不了这些。
我个人在实际项目里的体会是:把 95% 做到 96% 的努力,不如把预测结果真正接进业务闭环带来的收益大。当运维团队因为预测告警而避免了两次变压器过载,当储能策略因为预测曲线而多省了一笔需量电费——这些才是这个项目真正站得住脚的地方。能源管理的 AI 落地,从来不是模型比赛,而是一个“数据—算法—业务”的完整回路。