GRA-LSTM多元负荷预测:从特征筛选到短期时序建模实践
简介这是基于GRA-LSTM神经网络的区域综合能源系统多元负荷短期预测模型的完整学术论文PDF面向从事能源系统预测、深度学习应用研究与电力调度的工程师和科研人员。文章针对区域综合能源系统中冷、热、电负荷短期预测的不确定性与耦合性问题提出将灰色关联度分析GRA与长短期记忆LSTM神经网络相结合实现对多元负荷及气象因素耦合性的定量分析与预测建模并基于DeST软件建立写字楼建筑模型通过动态模拟获得全年逐时负荷数据验证了模型精度。论文内容涵盖多元负荷相关性分析、GRA权重计算、LSTM网络结构设计及算例对比对构建综合能源负荷预测模型具有直接参考价值。资源包内仅含1个PDF文件大小约1.52MB正文与图表完整便于直接阅读和引用。该资源已有417人浏览学习适合作为区域综合能源系统、负荷预测方向的研究参考帮助读者快速掌握GRA-LSTM模型的构建思路、实验设计及结果分析要点。1. 为什么区域综合能源系统需要 GRA-LSTM 来做多元负荷短期预测一个园区或一座城市级的区域综合能源系统里冷、热、电、气负荷从来不是孤立变化的。今天午后的气温直接影响空调负荷同时也改变余热回收机组的出力进而牵动燃气消耗。如果分别用四个模型去预测四类负荷往往会在能源调度时自相矛盾电负荷预测偏低导致向电网购电偏少冷负荷预测偏高又让制冷机空转。反直觉的是用一个共享底层时序特征的模型把它们联合起来预测准确率反而更高因为负荷间的耦合关系被模型显式利用了。这就是 GRA-LSTM 的出发点先用灰色关联分析找出影响各类负荷的强相关因素再用长短期记忆网络捕捉负荷序列的时序依赖最后使用一个多任务输出结构同时给出未来一小时或未来一天的各负荷预测值。这篇博文面向正在做区域综合能源系统预测的工程师和对时序预测原理熟悉的算法从业者。原理部分会讲清楚灰色关联分析为什么适合小样本、非线性特征筛选实现部分会给出可以直接改的 Python 代码和参数设置思路最后落到模型评估和调参上。你会得到一套从特征筛选到网络训练再到性能验证的完整方案而不是零散的命令片段。2. GRA 灰色关联分析在多元负荷场景下筛出真正影响预测的特征2.1 为什么不用皮尔逊相关系数而是选 GRA做特征筛选时很多人第一反应是算皮尔逊相关系数但区域综合能源系统的负荷数据往往呈现两个特点样本量可能只有几百条且受恶劣天气影响产生明显非线性波动。皮尔逊系数要求变量间线性关系遇到非线性关联时数值偏低甚至失真而灰色关联分析基于几何形状的相似程度来判断关联强弱不要求大样本也不要求线性假设。以某工业园区为例电负荷与湿度的关系在夏季呈非线性上升曲线皮尔逊系数可能只有 0.3而灰色关联度却能到 0.7 以上这直接决定了湿度特征是否会被模型看到。GRA 的核心思想很简单把待评估的特征序列和参考序列比如电负荷序列做初值化处理再计算两者在每个时刻的关联系数最后求平均得到关联度。关联度越大说明该特征对目标负荷的影响越强。需要明确的是GRA 不是一种预测算法而是特征选择的预处理工具它的输出结果决定了 LSTM 输入端应该放哪些变量。2.2 灰色关联度的计算步骤与 Python 实现灰色关联度的计算分四步。第一步确定参考序列和比较序列参考序列是待预测的负荷序列比较序列是候选特征序列。第二步做无量纲化处理因为温度是 30 度量级电负荷是兆瓦量级不归一化会让关联度失真。第三步计算差序列找出每个时刻参考值与其他特征值的绝对差。第四步计算关联系数和关联度通过两级最小差和最大差来构造灰色关联度系数。下面代码可以直接运行输入是一个二维数组 x_data第一列是参考序列其余列是比较序列输出每个比较序列与参考序列的关联度。import numpy as np def grey_relational_analysis(x_data, rho0.5): x_data: 形如 (n_samples, n_features) 的 numpy 数组 第一列为参考序列其余列为比较序列 rho: 分辨系数一般取 0.5 返回: 每个比较序列的灰色关联度列表 data x_data.astype(float) # 无量纲化采用均值化处理每个值除以该列均值 mean_val np.mean(data, axis0) data_norm data / mean_val # 参考序列 ref data_norm[:, 0] # 差序列 diff np.abs(data_norm[:, 1:] - ref.reshape(-1, 1)) # 两级最小差与最大差 min_diff np.min(diff) max_diff np.max(diff) # 计算关联系数 gamma (min_diff rho * max_diff) / (diff rho * max_diff) # 关联度是关联系数按均值聚合 degree np.mean(gamma, axis0) return degree这段代码中rho0.5是分辨系数用于削弱最大差对关联系数的影响。通常取 0.5但当你发现关联度普遍偏大、区分度不够时可以降低到 0.3 或 0.4反之如果普遍偏小可以升到 0.6。均值化处理适用于负荷和气温、湿度这类数值型特征如果特征是电价这种带量纲的数值也适用。但遇到节假日之类的是否为法定节假日的 0/1 标志位需要单独处理要么直接剔除要么放到 LSTM 的辅助特征里而不参与 GRA 筛选。2.3 在多元负荷预测场景中怎么设定阈值当你得到所有特征对某类负荷的关联度后面临一个决策哪些特征保留我的经验是不要一刀切用固定阈值而要看关联度的分布形态。如果关联度排序后出现明显拐点就取拐点前的特征如果排序比较平滑可以取前 5 到 7 个特征或者取关联度大于某一值如 0.6 的特征。下面是特征筛选的参考表格。候选特征对电负荷关联度对冷负荷关联度对热负荷关联度历史电负荷0.920.780.65历史冷负荷0.760.940.71干球温度0.840.890.62相对湿度0.610.820.54电价序列0.420.380.33风速0.310.290.45从这个表格可以看到电价对三类负荷的关联度都很低可能因为该园区电价恒定或用户对电价不敏感此时就可以直接将电价剔除。而历史冷负荷对热负荷的关联度虽然不高但也不低这反映了冷热联供机组的部分耦合特性可以保留。需要注意的是GRA 只考虑了变量与负荷之间的关联没有考虑变量之间的共线性。如果有两个特征彼此高度相关GRA 会把它们都选进来导致 LSTM 输入端冗余这时需要额外计算特征间的关联度来排除重复特征。3. LSTM 神经网络处理多元负荷时序依赖的结构设计与参数要点3.1 LSTM 的适用前提和对抗 RNN 梯度消失的机制多元负荷短期预测本质上是一个多变量时序回归问题输入的是过去几个时刻的多维特征序列输出是未来一个或多个时刻的负荷值。传统的循环神经网络在处理这种长序列时容易发生梯度消失也就是说前面时刻的信息无法有效传到后面导致模型只能记住最近几个时刻的规律。LSTM 通过门控机制改变这一点它用遗忘门决定丢弃什么信息用输入门决定新增什么信息用输出门决定输出什么信息使得信息可以在序列中长距离传递。在区域综合能源系统场景中这种长距离记忆能力至关重要。例如一个多日连续的降温事件热负荷不仅依赖前一两小时的状态更与前一天同一时刻的热负荷有强相关。LSTM 的细胞状态允许这种按天周期性的规律被保留下来。当然短周期效应比如秒级的波动LSTM 也能捕获因为门控机制是自适应调整的。3.2 构建一个可训练的多变量 LSTM 回归网络下面用 Keras 搭建一个基础版 LSTM输入是经过 GRA 筛选后的特征序列输出是未来一个时刻的电负荷。在实际项目中你会把这个结构扩展为多输出这部分在第 4 章展开。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout seq_len 24 # 用过去 24 小时的数据 n_features 6 # 经过 GRA 筛选后保留的特征数 n_units 64 # LSTM 隐藏单元数 model Sequential() model.add(LSTM(n_units, activationtanh, return_sequencesTrue, input_shape(seq_len, n_features))) model.add(Dropout(0.2)) model.add(LSTM(n_units // 2, activationtanh, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(1)) # 输出未来一个时刻的负荷值 model.compile(optimizeradam, lossmse, metrics[mae]) model.summary()第一层 LSTM 设置了return_sequencesTrue是因为需要向第二层 LSTM 输出完整序列保证网络可以继续学习时间维度的依赖。第二层return_sequencesFalse则只输出最后一个时间步的隐藏状态交给全连接层做预测。Dropout 放在每层 LSTM 之后防止模型对训练数据过度拟合。因为负荷预测是回归任务损失函数用mse比交叉熵合适评价指标同时监控mae。关键参数seq_len决定模型能看到多长的历史窗口。对短时预测24 小时窗口通常够用如果要预测未来 4 小时可以尝试用 48 小时窗口来覆盖日周期性。n_units则控制模型容量64 起步是稳妥的如果训练损失不下降可以增至 128但同时要加大 Dropout 比例。3.3 LSTM 训练中两个容易忽视的问题与设置建议第一是序列数据的归一化方式。LSTM 的激活函数是 tanh输出在 -1 到 1 之间如果输入特征范围过大梯度会不稳定。常见做法是对每个特征做 MinMaxScaler将值压缩到 0 到 1 之间。但要注意如果预测目标也做了归一化在计算误差时要把预测值反归一化回原始量纲否则看着 MAE 很小实际没有意义。第二是训练集和验证集的划分方式。对于时序数据不能随机打乱样本否则会造成数据泄露。应该按时间顺序划分比如用前 70% 的数据做训练后 30% 做验证。同时在构建样本时每个样本包含连续的 seq_len 小时数据那么两个相邻样本之间会有一部分重叠这会导致训练集样本不独立。这个问题对模型影响不大但你在划分时要保证验证集的起始时间晚于所有训练样本的最后时间。4. 多元负荷短期预测整体流程从 GRA 特征筛选到 LSTM 多输出预测4.1 把多元负荷问题建模为多任务 LSTM 结构上一章的 LSTM 输出层只有一个神经元对应单类负荷。但区域综合能源系统往往需要同时预测电、冷、热三类负荷。一种最简单的做法是训练三个独立 LSTM但这样做不能利用负荷之间的耦合信息而且在实际落地时运维成本高。更好的方式是在共享 LSTM 网络之上接三个不同的输出层构成多任务学习框架。共享层学习三类负荷共同的时间依赖模式比如外界温度和人类活动规律对冷热负荷的同步影响而各个输出分支保留自己的非线性映射。多任务结构在 Keras 函数式 API 中很容易实现。下面代码展示了输入层、共享的 LSTM 层、以及三个独立输出分支的结构。输入的不是三维原始特征而是一个已经准备好的序列样本集合形状是(samples, seq_len, n_features)的张量。from tensorflow.keras.models import Model from tensorflow.keras.layers import Input, LSTM, Dense, Dropout, Concatenate seq_len 24 n_features 8 inputs Input(shape(seq_len, n_features)) # 共享 LSTM 层 x LSTM(64, activationtanh, return_sequencesTrue)(inputs) x Dropout(0.2)(x) x LSTM(32, activationtanh, return_sequencesFalse)(x) x Dropout(0.2)(x) # 三个输出分支分别预测电、冷、热负荷 elec_out Dense(16, activationrelu)(x) elec_out Dense(1, activationlinear, nameelec)(elec_out) cool_out Dense(16, activationrelu)(x) cool_out Dense(1, activationlinear, namecool)(cool_out) heat_out Dense(16, activationrelu)(x) heat_out Dense(1, activationlinear, nameheat)(heat_out) model Model(inputs, [elec_out, cool_out, heat_out]) model.compile(optimizeradam, lossmse, metrics[mae])每个输出分支先经过一个 16 维的全连接层目的是让共享特征向不同负荷做非线性特化。三个输出分支共用同一个优化器和损失函数但需要根据各类负荷的量级来设置 loss 权重。如果电负荷的数值范围为几百兆瓦热负荷只有几十兆瓦直接相加会让模型偏向电负荷。常见做法是对每个输出设置不同的 loss 权重比如loss{elec: 1.0, cool: 0.5, heat: 0.5}或者更精细地根据验证集误差的比例来确定。4.2 完整的数据预处理与训练流程代码现在把 GRA 特征筛选和 LSTM 多输出训练串成一个可运行的流程。下面代码不是完整的项目代码但覆盖了从数据加载到模型训练的骨架部分你可以直接替换数据源后运行。核心步骤包括读取原始数据、GRA 筛选特征、构造时间序列样本、划分训练验证集、以及训练并保存模型。import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler from tensorflow.keras.models import Model from tensorflow.keras.layers import Input, LSTM, Dense, Dropout # 读取数据列包括 timestamp, elec_load, cool_load, heat_load, temperature, humidity df pd.read_csv(energy_data.csv, parse_dates[timestamp]) # 假设已经做过缺失值处理 # 第一步GRA 筛选特征 # 构造一个特征矩阵第一列是目标例如电负荷 feature_cols [elec_load, cool_load, heat_load, temperature, humidity, wind_speed] gra_data df[feature_cols].values from grey_relational_analysis import grey_relational_analysis # 使用前面定义的函数 degrees grey_relational_analysis(gra_data) # 取关联度大于 0.6 的特征索引加入 selected_features selected_idx np.where(np.array(degrees) 0.6)[0] 1 # 跳过参考序列 selected_features [feature_cols[i] for i in selected_idx] # 第二步归一化 scaler MinMaxScaler() scaled_data scaler.fit_transform(df[selected_features]) # 第三步构造时间序列样本seq_len 为 24 seq_len 24 X, y [], [] for i in range(len(scaled_data) - seq_len): X.append(scaled_data[i:iseq_len]) # 多条负荷预测目标分别存放在三个 list 中 y.append(scaled_data[iseq_len, :3]) # 假设前三列是电冷热负荷 X np.array(X) y np.array(y) # 第四步按时间顺序划分训练验证集 split_ratio 0.7 split_idx int(len(X) * split_ratio) X_train, X_val X[:split_idx], X[split_idx:] y_train, y_val y[:split_idx], y[split_idx:] # 第五步构建多输出 LSTM 模型 inputs Input(shape(seq_len, len(selected_features))) x LSTM(64, activationtanh, return_sequencesTrue)(inputs) x Dropout(0.2)(x) x LSTM(32, activationtanh, return_sequencesFalse)(x) x Dropout(0.2)(x) elec_out Dense(1, nameelec)(x) cool_out Dense(1, namecool)(x) heat_out Dense(1, nameheat)(x) model Model(inputs, [elec_out, cool_out, heat_out]) model.compile(optimizeradam, loss{elec: mse, cool: mse, heat: mse}, loss_weights{elec: 1.0, cool: 0.5, heat: 0.5}, metricsmae) # 第六步训练并保存 history model.fit(X_train, {elec: y_train[:, 0], cool: y_train[:, 1], heat: y_train[:, 2]}, validation_data(X_val, {elec: y_val[:, 0], cool: y_val[:, 1], heat: y_val[:, 2]}), epochs100, batch_size32, verbose1) model.save(gra_lstm_multiload.h5)特征筛选时selected_idx的索引加一是因为grey_relational_analysis返回的关联度列表是从第二列开始的。而feature_cols列表里第一列是电负荷参考序列所以选出的特征索引需要加 1 才能对应特征名称。y 数组取scaled_data[iseq_len, :3]是因为前三个特征是电冷热负荷这一步要求你把负荷列放在前三位。训练时loss_weights的调节很有讲究。如果验证集上冷负荷的 MAE 高于电负荷可以适当提高cool损失权重迫使模型更关注冷负荷。但权重总和不宜过大否则会使训练不稳定。epochs100是初值具体要根据早停回调来截断避免过拟合。4.3 多步预测如何调整输出层和损失函数前面的代码默认是预测未来一个时刻的负荷值这个时刻是当前样本最后一个时刻的后一个小时。如果业务要求预测未来 24 小时的曲线直接把 Dense 输出的神经元数改为 24 即可。但这样做会损失中间时刻的时序连续性因为 LSTM 的最后一个隐藏状态输出的是对整个未来窗口的抽象表示。一个更稳妥的做法是采用递归策略用模型预测下一小时把这个预测值作为下一个时刻的输入特征再预测再下一小时。但这种做法会把误差逐步放大。另一个变通方法是把输出层设置为一个序列也就是让 LSTM 的最后一层return_sequencesTrue然后使用TimeDistributed层对每个时间步输出所有负荷的预测值。这种 seq2seq 结构复杂度更高但精度上限也更高。在工程项目中我一般先做单步预测验证模型框架再扩展为多步以避免一开始就陷入调参泥潭。5. 模型评估与调参技巧用对指标才不会误判 GRA-LSTM 的收益评价一个短期负荷预测模型光看训练损失是不够的。你需要一组能够反映预测值与真实值偏离程度的指标并且要知道它们各自强调什么。常用的有 MAE平均绝对误差直接反映误差大小RMSE均方根误差放大大误差的影响MAPE平均绝对百分比误差用来衡量相对误差但对接近零的负荷值非常敏感。对于区域综合能源系统冷负荷在夜间可能接近零MAPE 会被这些点带偏此时更适合用 MAE 加 RMSE 组合来评价。import numpy as np from sklearn.metrics import mean_absolute_error, mean_squared_error def evaluate_prediction(y_true, y_pred): y_true np.asarray(y_true) y_pred np.asarray(y_pred) mae mean_absolute_error(y_true, y_pred) rmse np.sqrt(mean_squared_error(y_true, y_pred)) mape np.mean(np.abs((y_true - y_pred) / (y_true 1e-6))) * 100 return {MAE: mae, RMSE: rmse, MAPE(%): mape} # 示例真实电负荷与模型预测值 true_load np.array([101.2, 102.5, 98.7, 95.3]) pred_load np.array([99.8, 101.0, 100.2, 96.1]) metrics evaluate_prediction(true_load, pred_load) print(metrics)在评估多输出模型时应该分负荷单独计算指标不要把所有负荷的真实值和预测值拼在一起算一个统一 MAE因为各类负荷量纲不同混合计算会掩盖某一项的退化。你可以把三组指标打印成一张表格对比不同特征组合或不同模型结构的成绩。模型版本电负荷 MAE冷负荷 MAE热负荷 MAE平均 RMSE独立 LSTM每类一个3.22.11.83.1GRA-LSTM 多输出2.41.61.32.3GRA-LSTM 早停2.11.41.22.0GRA-LSTM 学习率衰减1.91.31.11.8这张表是示意数据但反映的规律很典型。第一行到第二行的提升来自多任务共享第二行到后面的提升来自调参。模型收敛需要时间的建议先使用早停机制来防止过拟合Keras 的EarlyStopping可以在验证损失连续若干个 epoch 不下降时停止训练。然后把学习率从默认的0.001改为自适应衰减比如每隔 20 个 epoch 降为原来的 0.8。在 Keras 中可以用ReduceLROnPlateau回调来监测验证损失如果连续 10 个 epoch 没有改善学习率自动减半。另外有一个容易被忽略的参数是滑动窗口的长度seq_len。你可以分别尝试 12、24、48对比验证集误差来选最优。有一个经验起点如果预测步长为未来 1 小时24 小时窗口通常足够如果预测未来 4 小时窗口可以适当延长到 36 或 48 小时。窗口太长会引入过多历史噪声太短会丢掉日周期信息。在区域综合能源系统中你还可以把时间刻度和气象预报数据一起作为附加特征拼到 LSTM 输入里比如未来 1 小时的预测温度这比纯历史特征更能提升短期预测精度因为负荷变化对天气变化有滞后效应。注意在拼接时features数量会变化需要同步调整n_features参数和 GRA 筛选流程中的列索引避免模型维度不匹配。调参时建议先固定住 GRA 阈值和网络结构只动一个变量比如先调seq_len再调n_units最后调学习率。这样你能清楚地判断每个改动带来的效果而不是在多个参数同时变化时无从归因。本文还有配套的精品资源点击获取