基于K-means聚类与LSTM的电能质量预测:从原理到工程落地 简介这份PDF文献面向电力系统、智能电网及数据分析方向的研究生与工程技术人员聚焦主动配电网电能质量预测这一难题。针对电能质量数据在较长时间跨度上的时序性与非线性特征文中提出将K-means聚类与长短期记忆网络相结合的预测方法先对历史电能质量数据、环境因素及负荷数据做多维向量聚类再对每类数据分别训练LSTM模型并评估性能最后通过IEEE-13节点含分布式电源的仿真算例验证其优于时间序列法、BP神经网络和标准LSTM。资源包共1个PDF文件约2.73MB内容完整呈现论文的摘要、系统框架、算法伪代码与实验对比适合深度学习与数据研究方向的读者研读。目前已有218人学习可作为电能质量预测建模、聚类与LSTM融合思路的参考文献与专业指导。1. 一份把 K-means 和 LSTM 串起来的电能质量预测论文到底能跑出什么配电网里做电能质量预测的人大多经历过这种尴尬手上攒了几个月甚至一年的监测数据温度、光照、负荷、电压偏差、谐波畸变率全都有但真到要预测未来某时段的稳态指标时用 ARIMA 拟合出来的曲线要么滞后、要么在峰值点直接跑偏。这份《基于聚类LSTM深度学习模型的主动配电网电能质量预测》给出的思路是别拿全量数据硬训一个 LSTM先用 K-means 把历史数据按欧式距离聚成几类再对每一类单独训一个 LSTM预测时先判断未来工况属于哪一类再调用对应模型。论文用 IEEE-13 节点含光伏 DG 的仿真算例做了对比聚类 LSTM 的 RMSE 和 MAE 都优于 ARIMA、BP 和标准 LSTM48 小时预测里近 70% 的相对误差落在 5% 以内。适合正在做电能质量态势感知、负荷预测或时间序列多变量预测的从业者尤其是想搞清楚“聚类到底加在哪一步、LSTM 参数怎么设”的人。2. 聚类 LSTM 预测框架拆解四个模块和 K 值怎么定2.1 从数据采集到预测输出的完整链路论文把整个预测流程拆成四个功能模块这个拆法本身就值得借鉴因为它把“数据从哪来、怎么分、怎么训、怎么用”四件事分得很清楚不会在实现时把预处理和建模搅在一起。第一个模块是 PQ 数据获取。在含分布式电源的主动配电网里需要在合适位置布置环境变量监测装置、负荷监测仪和电能质量监测仪采集较长时间跨度内的光照强度、温度、负荷数据以及对应的电能质量稳态指标项按相同时间标记关联保存。这里的关键是“相同时间标记”——如果环境数据和电能质量数据的时间戳对不齐后面聚类出来的类别就是错的。第二个模块是 PQ 数据聚类。拿到关联历史数据后先做归一化预处理然后用轮廓系数法确定最优分类数 K再用 K-means 把数据按欧式距离分成 K 类。论文里每个时刻的数据被组织成[温度, 光照, 用电负荷, 电能质量指标]的 4 维向量这个结构直接决定了聚类的输入维度。第三个模块是 LSTM 模型训练与测试。把每一类数据转成监督学习序列按 70% 和 30% 划分训练集和测试集分别构建 LSTM 网络进行训练和性能评估直到每一类都有自己确定好的预测模型。第四个模块是 PQ 数据预测。获取未来某时段的环境因素预测数据和负荷预测数据先判断它属于哪个聚类类别然后调用对应类别的 LSTM 模型输出电能质量稳态指标项的预测值。注意这四个模块的顺序不能乱。我见过有人先把全部数据训一个 LSTM再拿预测结果去聚类那是完全反了——聚类必须在训练之前而且是对输入特征加输出指标一起聚。2.2 轮廓系数法确定 K 值为什么是 3 而不是 2 或 4K-means 最让人头疼的就是 K 值怎么选。论文用的是轮廓系数法这个方法的逻辑不复杂但实操时容易算错。对数据集中的某个样本点 $P_i$定义其轮廓系数$$S_i \frac{c - a}{\max(a, c)}$$其中 $a$ 是凝聚度表示 $P_i$ 与同簇其他样本的平均距离$c$ 是分离度表示 $P_i$ 与最近簇中所有样本的平均距离。最近簇的定义是$$q \arg\min \frac{1}{n} \sum |q - P_i|$$把所有样本的轮廓系数求平均就得到平均轮廓系数 $\bar{S}$取值范围在 $[-1, 1]$。簇内样本距离越近、簇间样本距离越远$\bar{S}$ 越大聚类效果越好。论文在 $K \in [2, 8]$ 范围内逐一计算平均轮廓系数发现当 $K3$ 时 $\bar{S}$ 最大所以最优类别数取 3。这个结论不是拍脑袋来的是算出来的。实操时用 Python 复现这段逻辑import numpy as np from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score from sklearn.preprocessing import MinMaxScaler # 假设 data 是 [温度, 光照, 负荷, 电压偏差] 的 N x 4 矩阵 scaler MinMaxScaler() data_norm scaler.fit_transform(data) silhouette_scores {} for k in range(2, 9): kmeans KMeans(n_clustersk, initk-means, n_init10, random_state42) labels kmeans.fit_predict(data_norm) score silhouette_score(data_norm, labels) silhouette_scores[k] score print(fK{k}, 平均轮廓系数{score:.4f}) best_k max(silhouette_scores, keysilhouette_scores.get) print(f最优类别数 K{best_k})这段代码里几个参数需要说明。initk-means是让初始聚类中心尽量分散避免陷入局部最优n_init10表示用不同随机种子跑 10 次取最好的结果因为 K-means 对初始中心敏感random_state42是为了结果可复现。归一化用MinMaxScaler把各变量压到 $[0,1]$这一步不能省——温度和光照的量纲差了好几个数量级不归一化的话聚类结果会被大数值变量主导。2.3 LSTM 细胞单元的门控机制与 BPTTAdam 训练论文对 LSTM 内部结构的描述比较细这里只挑实现时真正影响参数设置的部分说。LSTM 细胞单元包含遗忘门、输入门、输出门三个门控结构。遗忘门以上一个单元输出 $h_{t-1}$ 和本单元输入 $x_t$ 为输入通过 Sigmoid 函数为上一单元状态 $C_{t-1}$ 的每一项产生一个 $[0,1]$ 内的值控制遗忘程度$$f_t \sigma(W_f \cdot [h_{t-1}, x_t] b_f)$$输入门配合 tanh 函数控制新信息的加入$$i_t \sigma(W_i \cdot [h_{t-1}, x_t] b_i)$$ $$\tilde{C}t \tanh(W_C \cdot [h{t-1}, x_t] b_C)$$ $$C_t f_t \cdot C_{t-1} i_t \cdot \tilde{C}_t$$输出门控制当前单元状态有多少被过滤$$o_t \sigma(W_o \cdot [h_{t-1}, x_t] b_o)$$ $$h_t o_t \cdot \tanh(C_t)$$训练过程用 BPTT 算法加 Adam 梯度优化。BPTT 的四个步骤是前向计算各细胞输出值、反向计算每个细胞的误差项、根据误差项计算权重梯度、用 Adam 更新权值。Adam 的优势在于适合处理大规模数据和参数、对噪声和稀疏度不敏感这在电能质量数据里很实用——监测数据里总有缺失值和异常波动。在 Keras 里搭建这个 LSTM 网络核心代码大概长这样from keras.models import Sequential from keras.layers import LSTM, Dense from keras.optimizers import Adam model Sequential() # 隐藏层用 50 个 LSTM 细胞输入维度为 4温度、光照、负荷、历史PQ指标 model.add(LSTM(50, input_shape(time_steps, 4), return_sequencesFalse)) # 全连接输出层输出 1 个预测值如电压偏差 model.add(Dense(1)) # 损失函数用 mae优化器用 adam model.compile(lossmae, optimizeradam) model.fit(X_train, y_train, epochs100, batch_size32, validation_split0.1, verbose1)LSTM(50)里的 50 是隐藏层神经元数量论文没有明确给出这个值但根据同类时间序列预测的经验50 到 128 之间比较常见数据量小的时候取小一点防止过拟合。input_shape(time_steps, 4)里的time_steps是时间窗口长度论文用series_to_supervised()把原始时间序列转成监督学习格式这个窗口长度需要根据数据采样频率和预测跨度来调。lossmae对应论文里的平均绝对误差optimizeradam对应适应性动量估计算法。epochs100和batch_size32是常见起点实际训练时看验证集 loss 曲线来定早停。3. 从原始数据到预测输出可复现的完整实现步骤3.1 数据归一化与反归一化的参数选择论文用式(9)把温度、光照、用电负荷和电能质量指标项都归算到 $[0,1]$ 之间$$X_{norm} \frac{X - X_{min}}{X_{max} - X_{min}}$$预测完成后用式(10)反归一化$$X X_{norm} \times (X_{max} - X_{min}) X_{min}$$这里有个容易翻车的地方$X_{max}$ 和 $X_{min}$ 必须用历史数据集里的最大最小值不能用训练集和测试集各自的最大最小值。如果用测试集自己的极值做反归一化预测结果会被人为拉回测试集范围看起来误差很小实际上模型根本没有预测能力。我一般会把训练集的 min/max 存下来预测时直接用这组参数做反归一化。# 保存训练集的归一化参数 train_min data_train.min(axis0) train_max data_train.max(axis0) # 归一化 data_train_norm (data_train - train_min) / (train_max - train_min) data_test_norm (data_test - train_min) / (train_max - train_min) # 预测后反归一化 pred_original pred_norm * (train_max[target_idx] - train_min[target_idx]) train_min[target_idx]3.2 监督学习序列构造与训练测试集划分论文用series_to_supervised()把时间序列转成监督学习格式。这个函数的核心逻辑是用前 $n$ 个时间步的 $m$ 个特征预测下一个时间步的目标值。比如用过去 24 小时的温度、光照、负荷和电压偏差预测第 25 小时的电压偏差。import pandas as pd def series_to_supervised(data, n_in24, n_out1, dropnanTrue): 将时间序列转换为监督学习格式 data: DataFrame 或 array每列是一个变量 n_in: 输入时间步数 n_out: 输出时间步数 n_vars 1 if type(data) is list else data.shape[1] df pd.DataFrame(data) cols [] # 构造输入序列列名 for i in range(n_in, 0, -1): cols.append(df.shift(i)) # 构造输出序列列名 for i in range(0, n_out): cols.append(df.shift(-i)) agg pd.concat(cols, axis1) if dropnan: agg.dropna(inplaceTrue) return agg.valuesn_in24表示用过去 24 个时间步作为输入这个值需要根据数据的自相关性和预测跨度来调。论文里一天有 24 组稳态数据所以取 24 作为输入窗口是合理的。n_out1表示预测下一个时间步。dropnanTrue会删掉因为 shift 产生的 NaN 行这是必须的否则训练时会报错。数据分割按时间顺序取前 70% 做训练集、后 30% 做测试集。注意不能随机打乱——时间序列的先后顺序本身就是信息打乱之后 LSTM 学到的时序依赖就是假的。3.3 分类训练与预测调用的完整流程论文表 1 给出了聚类 LSTM 预测算法的伪代码翻译成可执行的 Python 逻辑# 第一步读取历史数据并归一化 data_norm normalize(raw_data) # 第二步轮廓系数法确定最优 K best_k find_optimal_k(data_norm, k_rangerange(2, 9)) # 第三步K-means 聚类 kmeans KMeans(n_clustersbest_k, initk-means, n_init10, random_state42) labels kmeans.fit_predict(data_norm) # 第四步对每一类分别训练 LSTM models {} for i in range(best_k): class_data data_norm[labels i] # 转监督学习格式 supervised series_to_supervised(class_data, n_in24, n_out1) # 按 70/30 划分 train_size int(len(supervised) * 0.7) train, test supervised[:train_size], supervised[train_size:] X_train, y_train train[:, :-1], train[:, -1] X_test, y_test test[:, :-1], test[:, -1] # 重塑为 LSTM 需要的 3D 输入 X_train X_train.reshape((X_train.shape[0], 24, 4)) X_test X_test.reshape((X_test.shape[0], 24, 4)) # 构建并训练 LSTM model build_lstm_model(time_steps24, n_features4) model.fit(X_train, y_train, epochs100, batch_size32, verbose0) models[i] model # 第五步预测时先判断类别再调用对应模型 future_data_norm normalize(future_data) future_label kmeans.predict(future_data_norm) pred models[future_label[0]].predict(future_X) pred_original inverse_normalize(pred, train_min, train_max)这段代码里最关键的是第五步——预测时不能直接拿未来数据去调模型必须先判断它属于哪个聚类类别。判断方法是用训练好的 K-means 模型对未来的环境因素和负荷数据做predict得到类别标签后再调用对应的 LSTM。如果未来数据跨越了多个类别就需要分段预测再拼接。3.4 性能评估指标 RMSE 与 MAE 的计算论文用均方根误差和平均绝对误差来评估预测性能$$RMSE \sqrt{\frac{1}{N} \sum_{i1}^{N} (y_i - \hat{y}_i)^2}$$$$MAE \frac{1}{N} \sum_{i1}^{N} |y_i - \hat{y}_i|$$from sklearn.metrics import mean_squared_error, mean_absolute_error import numpy as np rmse np.sqrt(mean_squared_error(y_true, y_pred)) mae mean_absolute_error(y_true, y_pred) print(fRMSE: {rmse:.4f}, MAE: {mae:.4f})论文的对比结果里聚类 LSTM 的 RMSE 平均为 5.59MAE 平均为 4.40标准 LSTM 的 RMSE 平均为 6.62MAE 平均为 5.40BP 网络最差RMSE 平均 9.61MAE 平均 7.66ARIMA 的 RMSE 平均 8.37MAE 平均 6.75。聚类 LSTM 比标准 LSTM 的 RMSE 降低了约 15.6%这个提升幅度在电能质量预测场景里是有实际意义的。4. 避坑与排查聚类 LSTM 落地时最容易翻车的五个地方4.1 聚类前不归一化K-means 被大数值变量带偏现象聚类结果里某一类样本数特别多其他类样本极少LSTM 训练时某一类数据不够导致模型欠拟合。原因温度可能在 10 到 40 之间光照强度可能在 0 到 4000 之间负荷可能在 0 到 5000 之间量纲差了两三个数量级。K-means 用欧式距离算相似度数值大的变量会主导距离计算聚类结果实际上只反映了负荷的分布温度和光照的信息被淹没了。解决聚类前必须对每个变量单独做归一化把所有变量压到 $[0,1]$ 或标准化到均值为 0、方差为 1。论文里明确写了“在进行归一化预处理后”再做聚类这一步不能省。4.2 训练集和测试集随机划分时序信息泄漏现象测试集上的 RMSE 很低但拿实际未来数据预测时误差很大。原因用train_test_split随机划分时间序列数据会把未来时刻的数据混进训练集LSTM 在训练时已经“看到”了未来信息测试集评估结果虚高。解决按时间顺序划分前 70% 做训练、后 30% 做测试。论文里明确写了“按照时间顺序划分为训练集与测试集”这是时间序列预测的基本纪律。4.3 预测时忘记判断聚类类别直接调用单一模型现象预测结果在某些时段误差突然变大但模型在测试集上表现正常。原因未来工况可能属于不同的聚类类别如果只用其中一个类别的 LSTM 模型做预测遇到其他类别的工况时预测性能会明显下降。解决预测前先用训练好的 K-means 模型对未来的环境因素和负荷数据做类别判断再调用对应类别的 LSTM。如果未来数据跨越多个类别需要分段预测。4.4 LSTM 输入维度与series_to_supervised输出不匹配现象model.fit()时报错提示输入维度不对。原因series_to_supervised返回的是 2D 数组但 Keras 的 LSTM 层需要 3D 输入(samples, time_steps, features)。很多人忘了 reshape 这一步。解决在fit之前加X_train X_train.reshape((X_train.shape[0], time_steps, n_features))。time_steps要和series_to_supervised里的n_in一致n_features是变量个数。4.5 反归一化用错极值预测结果被“压缩”现象预测曲线看起来太平滑波动幅度明显小于实际值。原因反归一化时用了测试集或预测数据自己的最大最小值而不是训练集的极值。这样会把预测值强行拉回一个很小的范围看起来误差小实际上模型没有预测能力。解决归一化和反归一化必须用同一组极值也就是训练集的最大最小值。把这组参数存下来预测时直接调用。5. 进阶技巧用滑动窗口和场景聚类把预测精度再推一步论文在结论里提到后续可以按季节、节假日、天气类型等不同场景进行更合理的聚类划分。这个方向在实际项目里比论文里的基础版更实用因为电能质量的季节性差异和天气敏感性都很明显。我一般会做两件事。第一件是把固定窗口改成滑动窗口。论文里用 24 个时间步做输入但如果数据采样频率更高比如 15 分钟一个点一天就是 96 个点这时候用 96 做输入窗口比 24 更合理。滑动窗口的代码实现def sliding_window(data, window_size, step1): 生成滑动窗口样本 data: 归一化后的时间序列 window_size: 窗口长度 step: 滑动步长 X, y [], [] for i in range(0, len(data) - window_size, step): X.append(data[i:i window_size, :-1]) # 输入特征 y.append(data[i window_size, -1]) # 目标值 return np.array(X), np.array(y)window_size根据数据采样频率和预测跨度来定step控制窗口滑动步长取 1 表示逐点滑动取大于 1 的值可以降采样减少计算量。第二件事是按场景分别聚类。比如先按季节把数据分成四组每组内部再用轮廓系数法确定 K 值做 K-means然后每组单独训 LSTM。这样做的代价是模型数量变多、训练时间变长但预测精度通常能再提升一截。论文里 48 小时预测中聚类 LSTM 有 67% 的相对误差在 5% 以内按场景细分后这个比例还能往上走。验证方法上我习惯用滚动预测的方式做交叉验证用前 3 个月数据训模型预测第 4 个月再用前 4 个月训模型预测第 5 个月以此类推。这样能看出模型在不同时间段的稳定性比单次划分训练集测试集更有说服力。从那以后我每次做时间序列预测项目都强制走一遍“先归一化、再按时间划分、聚类后分类训练、预测前判类别”的流程少一步都可能在后期的实际部署里翻车。希望帮到你。本文还有配套的精品资源点击获取