能源行业AI负荷预测的工程全链路:从SCADA数据管道到LSTM+Attention在线推理服务的设计与运维
一、电力负荷预测为什么不是"拿个LSTM跑一下"就能解决的事
电力负荷预测是AI在传统行业中落地最成功的场景之一——国家电网和南方电网每年在负荷预测上的投入以亿计。但这不意味着它是一个"成熟到没有挑战"的领域。恰恰相反,正是因为预测精度直接影响发电机组启停决策和电力市场出清价格,这个场景对模型的要求极为严苛。
传统方法使用ARIMA和指数平滑,在平稳时序上表现尚可。电力负荷的真正挑战在于非平稳性:夏季高温导致空调负荷激增、节假日负荷模式突变、突发性政策(如限电)造成的结构性断点。线性模型在这些场景下失效的根本原因在于它们假设过去的变化模式会延续到未来——而电力负荷在极端天气和政策事件面前,模式的"连续性"会被打破。
LSTM之所以在这个场景表现优异,不在于它比ARIMA"更先进",而在于它能捕获两个关键模式:短期依赖(前1-4小时的负荷变化趋势直接决定下一小时)和长期周期(周一和上周一的负荷模式高度相似,即7天周期)。这种双尺度的时序建模能力是统计方法无法同时满足的。
二、特征工程是负荷预测的天花板:不是特征越多越好,而是每个特征都要有物理含义
电力负荷预测的特征工程有一条铁律:你加进去的每一个特征,都必须能用电力系统的物理规律解释。不能用"因为模型效果提升了"作为加特征的理由——如果一个特征在物理上解释不了它为什么有助于预测,那它大概率只是过拟合了当前数据集。
以下是经过生产验证的五大类特征及其物理含义:
# load_features.py — 电力负荷特征工程管道
import numpy as np
import pandas as pd
from typing import Tuple
class LoadFeatureEngine:
"""电力负荷特征工程:所有特征的物理含义都有明确解释"""
def __init__(self, seq_length: int = 168): # 7天 × 24小时
self.seq_length = seq_length
def build_features(self, raw_df: pd.DataFrame) -> pd.DataFrame:
df = raw_df.copy()
# 第一类:时序滞后特征 — 捕获短期依赖
# 物理含义:前N小时的负荷直接决定下一小时的负荷
for lag in [1, 2, 4, 8, 24, 48, 72, 120, 168]:
df[f'lag_{lag}h'] = df['load_mw'].shift(lag)
# 第二类:滚动窗口统计 — 捕获趋势和波动性
# 物理含义:不同时间窗口内的负荷水平和波动程度
for window in [4, 24, 96, 168]:
roll = df['load_mw'].rolling(window=window, min_periods=1)
df[f'roll_mean_{window}h'] = roll.mean()
df[f'roll_std_{window}h'] = roll.std()
df[f'roll_max_{window}h'] = roll.max()
df[f'roll_min_{window}h'] = roll.min()
# 第三类:差分特征 — 捕获负荷变化的速率
# 物理含义:负荷爬坡速率是电网调度的核心约束
for diff in [1, 24, 168]:
df[f'diff_{diff}h'] = df['load_mw'].diff(diff)
# 第四类:时间循环编码 — 捕获日/周/月的周期模式
# 物理含义:电力负荷具有显著的日周期和星期周期
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
df['dow_sin'] = np.sin(2 * np.pi * df['day_of_week'] / 7)
df['dow_cos'] = np.cos(2 * np.pi * df['day_of_week'] / 7)
df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
# 第五类:节假日邻近度衰减评分
# 物理含义:节假日前后72小时内负荷模式逐渐偏离正常
df['holiday_proximity'] = self._holiday_proximity(df)
# 温度衍生特征:体感温度对空调负荷的影响非简单线性
if 'temperature' in df.columns and 'humidity' in df.columns:
# 简化体感温度公式(炎热指数)
df['heat_index'] = (
df['temperature']
+ 0.05 * (df['humidity'] – 50)
)
return df.dropna()
def _holiday_proximity(self, df: pd.DataFrame) -> pd.Series:
"""节假日邻近度:指数衰减,72小时后影响趋零"""
scores = pd.Series(0.0, index=df.index)
for idx in df[df['is_holiday'] == 1].index:
distance = np.abs(df.index – idx)
mask = distance <= 72
scores[mask] = np.maximum(
scores[mask],
np.exp(-0.1 * distance[mask])
)
return scores
三、LSTM+Attention架构的选型逻辑:不是堆层数,而是控制梯度流动路径
单纯的双层LSTM在负荷预测上有一个明显缺陷:它会平等对待序列中的每一个时间步。但在电力负荷中,最近1小时的负荷和前168小时(上周同一时间)的负荷对预测的贡献度完全不同。Attention机制解决的就是这个"重要性加权"问题——让模型自己学习哪些历史时间步对当前预测最重要。
# lstm_attention_model.py — LSTM+Attention负荷预测模型
import tensorflow as tf
from tensorflow.keras import layers, Model
class TemporalAttention(layers.Layer):
"""时间维度的注意力:自动学习历史时间步的重要性权重"""
def __init__(self, units: int = 32):
super().__init__()
self.score_dense = layers.Dense(units, activation='tanh')
self.weight_dense = layers.Dense(1)
def call(self, encoder_outputs):
# encoder_outputs: (batch, seq_len, hidden_dim)
scores = self.weight_dense(self.score_dense(encoder_outputs))
# scores: (batch, seq_len, 1)
attention_weights = tf.nn.softmax(scores, axis=1)
# 加权求和: (batch, hidden_dim)
context = tf.reduce_sum(attention_weights * encoder_outputs, axis=1)
return context, attention_weights
class LoadForecastModel(Model):
"""电力负荷预测LSTM+Attention模型"""
def __init__(self, seq_length: int = 168,
n_features: int = 38,
forecast_horizon: int = 24):
super().__init__()
# 双层LSTM + LayerNorm稳定训练
self.lstm1 = layers.LSTM(128, return_sequences=True,
kernel_regularizer=tf.keras.regularizers.l2(1e-5))
self.dropout1 = layers.Dropout(0.3)
self.lstm2 = layers.LSTM(64, return_sequences=True,
kernel_regularizer=tf.keras.regularizers.l2(1e-5))
self.layer_norm = layers.LayerNormalization()
# Attention机制
self.attention = TemporalAttention(32)
# 预测头
self.dense1 = layers.Dense(32, activation='relu')
self.dropout2 = layers.Dropout(0.2)
self.output_layer = layers.Dense(forecast_horizon)
def call(self, inputs, training=False):
x = self.lstm1(inputs, training=training)
x = self.dropout1(x, training=training)
x = self.lstm2(x, training=training)
x = self.layer_norm(x)
context, _ = self.attention(x)
x = self.dense1(context)
x = self.dropout2(x, training=training)
return self.output_layer(x)
def compile_model(self):
self.compile(
optimizer=tf.keras.optimizers.AdamW(
learning_rate=1e-3, weight_decay=1e-4),
# Huber损失对负荷尖峰(异常值)更鲁棒
loss=tf.keras.losses.Huber(delta=2.0),
metrics=['mae', 'mape']
)
四、在线推理的数据漂移检测:模型再好,数据分布变了就是废了
电力负荷预测模型部署后最大的运维风险不是模型精度衰减,而是数据漂移。当出现极端高温天气、新的产业园区投产、或者电网结构改变时,输入特征的分布会显著偏离训练集。此时模型的预测结果已经不可信,但传统的MAPE监控可能要有24小时后才能发现(因为需要实际负荷数据来对比)。
PSI(Population Stability Index,人口稳定指数)是数据漂移检测最直接的指标。它比较当前输入特征的分布与训练集基线分布的差异。公式为:PSI = Σ(Actual% – Expected%) × ln(Actual% / Expected%)。业界标准是PSI < 0.1为无漂移,0.1-0.2为轻微漂移需关注,> 0.2为显著漂移需告警。
# drift_monitor.py — 数据漂移检测与自动告警
import numpy as np
class DriftMonitor:
"""数据漂移监控器"""
def __init__(self, baseline_distribution: np.ndarray,
bins: int = 10):
self.baseline = baseline_distribution
self.bins = bins
def psi(self, current_data: np.ndarray) -> float:
"""计算PSI指标"""
baseline_hist, _ = np.histogram(
self.baseline, bins=self.bins, density=True)
current_hist, _ = np.histogram(
current_data, bins=self.bins, density=True)
# 防止除零
baseline_hist = baseline_hist + 1e-6
current_hist = current_hist + 1e-6
psi = np.sum(
(current_hist – baseline_hist) *
np.log(current_hist / baseline_hist)
)
return float(psi)
def check_and_alert(self, recent_data: np.ndarray) -> dict:
psi_value = self.psi(recent_data)
if psi_value > 0.2:
level = "CRITICAL"
action = "触发模型重训练流程,暂停自动调度决策"
elif psi_value > 0.1:
level = "WARNING"
action = "通知运维团队关注,增加监控频率"
else:
level = "NORMAL"
action = "无需操作"
return {
"psi": round(psi_value, 4),
"level": level,
"action": action,
}
五、总结
电力负荷预测的特征工程必须从物理约束出发:时序滞后捕获短期依赖、滚动窗口捕获趋势和波动、差分捕获变化速率、时间循环编码捕获周期性、节假日邻近度捕获模式突变。每一个特征都能在电力系统运行规律中找到对应的物理含义。
LSTM+Attention不是"用了就更好"而是"选择性地加权历史":Attention让模型自动学习"最近1小时的负荷比168小时前的重要多少",这种自适应的时序加权能力是纯粹LSTM无法实现的。Huber损失替代MSE是为了控制负荷尖峰(如夏季空调峰值)对模型训练的过度影响。
生产环境有三个运维信号不能忽视:MAPE监控预测精度(整体<8%、峰值<10%)、PSI监控数据漂移(>0.2自动触发重训练)、预测延迟(<200ms确保实时性)。三者任何一个告警都需要人工介入。
模型更新周期建议"滚动7天重训练":每天使用最近90天的数据重新训练模型,确保模型始终适应当前的负荷模式。重训练期间由旧模型提供服务,训练完成后进行A/B对比验证新模型MAPE不劣于旧模型后再切换。
在线推理管道的架构设计:Kafka实时消费SCADA数据→特征在线提取(用流处理确保特征延迟<50ms)→模型推理(TF Serving或Triton)→预测结果推回Kafka→调度中心消费。整个链路端到端延迟控制<200ms。




