基于特征融合的一维CNN电能质量扰动分类实战 简介这份PDF面向电力系统保护与控制、电能质量分析方向的研究生与工程技术人员聚焦电能质量扰动分类中传统特征选取算法鲁棒性差、抗噪能力弱的问题。文中提出基于特征融合的一维卷积神经网络模型先用三个CNN子模型分别提取扰动信号特征向量再融合为新特征向量最后经BP神经网络完成分类并与改进前一维CNN及现有算法进行对比验证。资源包共1个PDF文件大小约2.26MB内容为完整期刊论文含摘要、引言、算法原理、仿真实验与结论等章节便于系统研读模型结构与实验设计。目前已有136人学习适合深度学习、机器学习与数据建模方向的读者参考可从中获取特征融合思路、一维CNN建模方法及电能质量扰动分类的完整研究方案。1. 电能质量扰动分类为什么值得用一维 CNN 重做一遍做过电能质量监测的人大概都有过这种体验现场采回来的电压波形肉眼一看就知道是暂降还是谐波但要让算法自动分出来识别率死活卡在 95% 上下噪声一大就崩。传统路子是先做特征提取——傅里叶、小波、S 变换轮着上——再喂给 SVM 或 BP 网络分类。问题在于特征选多了冗余选少了丢信息换个台区或换种扰动组合模型就得重新调。这份来自西华大学和国网四川电科院的论文提出了一种基于特征融合的一维卷积神经网络FCNN方案核心思路是用三个不同深度的 CNN 子模型各自提取特征向量拼成一个 768 维的融合特征再交给全连接层分类。它解决的就是“不想手工选特征、又要在强噪声下稳住识别率”这个具体问题。适合正在做电能质量扰动识别、手里有波形数据、想用深度学习替换传统特征工程路线的工程师也适合想搞明白一维 CNN 在序列信号上到底怎么落地的人。2. FCNN 模型拆解三个子模型怎么搭、特征怎么融2.1 为什么选一维 CNN 而不是二维图像转换电能质量扰动信号本质是一维时间序列采样频率 3200 Hz、单样本 1000 个点、基波频率 50 Hz。有些方案会先把一维信号转成二维灰度图像再上二维 CNN但转换过程本身就引入额外计算而且暂降和中断的灰度图特征差异不明显识别率反而掉。一维 CNN 直接对原始序列做卷积卷积核沿时间轴滑动计算量小、速度快对这类序列信号是更自然的选择。论文里也引了心电图分类、电机故障诊断等场景用一维 CNN 的案例说明这条路子在工程上已经跑通了。2.2 三个子模型的结构差异与选型逻辑FCNN 不是随便堆三个网络。它选了 LeNet-5、VGGNet、ResNet 三个经典结构做改造分别得到 Net_6、Net_18、Net_21。选这三个是因为它们代表了不同的深度和感受野Net_6 在 LeNet-5 基础上加层偏浅抓局部细节快Net_18 借鉴 VGGNet 反复叠 3×1 小卷积核中等深度特征抽象能力更强Net_21 参考 ResNet 优化层数和超参数最深能捕捉更长范围的模式。三个子模型第一个卷积层的卷积核长度都设为 32目的是减少训练时间——第一层核太长后面计算量会炸。每个子模型最后一个池化层输出 256 维向量三个拼起来就是 768 维。这里有个关键设计子模型不是端到端一起训练的。论文的训练流程分三步走。第一步用训练集 1 分别训练三个子模型让它们各自收敛。第二步把训练集 2 输入训练好的子模型取最后一个池化层的输出作为特征向量丢弃后面的全连接部分。第三步把三个 256 维向量首尾拼接成 768 维用这个融合特征去训练最后的分类层。这种分阶段训练的好处是每个子模型可以独立调参不会因为某个子模型拖后腿导致整体梯度混乱。2.3 融合层、Dropout 与分类层的参数设置Merge 层做的是拼接concatenate不是相加或平均。拼接保留了每个子模型特征的完整信息维度从 256 变成 768。拼接后接一个 Dropout 层Dropout 率设 0.5这是防止过拟合的常规操作——融合特征维度高了参数量上去不加 Dropout 很容易在训练集上过拟合。最后全连接层的输出节点数设为 8对应八类扰动暂升 C0、暂降 C1、中断 C2、谐波 C3、闪变 C4、振荡 C5、暂升谐波 C6、暂降谐波 C7。输出层用 Softmax 激活损失函数用交叉熵。用 Keras 搭这个模型的话子模型的定义大概长这样import keras from keras.models import Model from keras.layers import Input, Conv1D, BatchNormalization, Activation, MaxPooling1D, Dropout, Dense, concatenate def build_sub_model(input_len1000, num_classes8, depthshallow): inputs Input(shape(input_len, 1)) # 第一层卷积核长度固定 32减少训练时间 x Conv1D(filters32, kernel_size32, strides1, paddingsame)(inputs) x BatchNormalization()(x) x Activation(relu)(x) x MaxPooling1D(pool_size2)(x) if depth shallow: # Net_6 风格 x Conv1D(64, 16, paddingsame)(x) x BatchNormalization()(x) x Activation(relu)(x) x MaxPooling1D(2)(x) elif depth medium: # Net_18 风格反复叠 3x1 卷积 for _ in range(3): x Conv1D(64, 3, paddingsame)(x) x BatchNormalization()(x) x Activation(relu)(x) x MaxPooling1D(2)(x) else: # Net_21 风格带残差思路 for _ in range(4): x Conv1D(128, 3, paddingsame)(x) x BatchNormalization()(x) x Activation(relu)(x) x MaxPooling1D(2)(x) # 取最后一个池化层输出作为特征向量此处输出 256 维 feature MaxPooling1D(pool_size2)(x) return Model(inputs, feature) # 三个子模型分别构建 net_6 build_sub_model(depthshallow) net_18 build_sub_model(depthmedium) net_21 build_sub_model(depthdeep) # 融合层拼接三个 256 维特征 merged concatenate([net_6.output, net_18.output, net_21.output]) # 768 维 x Dropout(0.5)(merged) outputs Dense(num_classes, activationsoftmax)(x) fcnn Model(inputs[net_6.input, net_18.input, net_21.input], outputsoutputs) fcnn.compile(optimizerkeras.optimizers.SGD(lr0.005), losscategorical_crossentropy, metrics[accuracy])这段代码里几个参数值得注意。kernel_size32只用在第一层后面层用 3 或 16这是论文明确说的“减少训练时间”的做法。BatchNormalization加在激活函数之前论文里 BN 层的作用是让每层输入分布一致可以用更大学习率、加快收敛。SGD(lr0.005)是论文里用的随机梯度下降学习率 0.005Mini-Batch 大小 30。实际复现时如果发现收敛太慢可以换成 Adam 先跑通再切回 SGD 调参。2.4 训练集划分与数据准备论文的数据设置是每类扰动 1000 个样本随机选 600 个做训练集剩下 400 个做测试集。采样频率 3200 Hz单样本 1000 点基波 50 Hz。信噪比测试在 25 dB 和 15 dB 两档下做。如果你手头有自己的波形数据建议按同样比例划分并且确保训练集和测试集的噪声水平一致——否则模型在干净数据上训练、在噪声数据上测试识别率会虚低。数据预处理阶段常见做法是对幅值做归一化把电压标幺值缩放到 [0,1] 或 [-1,1]这样卷积核的初始权重不用反复调。3. 从训练到验证复现 FCNN 的完整操作链路3.1 分阶段训练的具体步骤复现 FCNN 最容易翻车的地方是训练顺序。论文的流程是先用训练集 1 训练三个子模型再用训练集 2 提取特征最后用融合特征训练分类层。如果你直接把三个子模型和分类层拼在一起端到端训练梯度会同时回传到三个子模型导致它们互相干扰收敛慢且识别率不稳定。正确做法是分三步第一步单独训练每个子模型。每个子模型后面临时接一个全连接层输出 8 类用训练集 1 训练到收敛。论文里 Net_6、Net_18、Net_21 在 25 dB 下分别训练 12、30、40 代后识别率趋于稳定在 15 dB 下分别需要 17、40、50 代。这个代数是参考值实际训练时看验证集 loss 不再下降就可以停。第二步冻结子模型权重把训练集 2 输入进去取最后一个池化层的输出。这一步只做前向传播不更新权重。三个子模型各输出 256 维拼成 768 维特征向量。训练集 2 可以和训练集 1 相同也可以重新采样论文里没有强制要求不同。第三步用 768 维特征训练最后的分类层。分类层就是一个 Dropout(0.5) 加一个 8 节点的 Dense 层训练到收敛即可。这一步训练量很小因为特征已经提取好了。# 第一步单独训练子模型 for sub_model, name in zip([net_6, net_18, net_21], [Net_6, Net_18, Net_21]): x sub_model.output x Dense(8, activationsoftmax)(x) temp_model Model(inputssub_model.input, outputsx) temp_model.compile(optimizerSGD(lr0.005), losscategorical_crossentropy, metrics[accuracy]) temp_model.fit(X_train1, y_train1, batch_size30, epochs50, validation_split0.2) # 保存子模型权重 sub_model.save_weights(f{name}_weights.h5) # 第二步冻结子模型提取特征 for sub_model in [net_6, net_18, net_21]: for layer in sub_model.layers: layer.trainable False features_6 net_6.predict(X_train2) features_18 net_18.predict(X_train2) features_21 net_21.predict(X_train2) X_train3 np.concatenate([features_6, features_18, features_21], axis1) # (N, 768) # 第三步训练分类层 classifier Sequential([ Dropout(0.5, input_shape(768,)), Dense(8, activationsoftmax) ]) classifier.compile(optimizerSGD(lr0.005), losscategorical_crossentropy, metrics[accuracy]) classifier.fit(X_train3, y_train2, batch_size30, epochs30)这段代码里layer.trainable False是关键冻结后子模型权重不再更新只做特征提取。np.concatenate沿 axis1 拼接得到 768 维。分类层用Sequential单独搭输入维度 768输出 8。3.2 用 t-SNE 验证特征融合是否真的有效论文用 t-SNE 把子模型特征和融合特征降到二维做可视化结论是融合后不同类别的距离加大、重合减少。这个验证方法很实用建议复现时也跑一遍。具体做法是取测试集的一部分样本分别用单个子模型和融合特征提取向量然后用 sklearn 的 TSNE 降到二维画散点图不同类别用不同颜色。如果融合后的散点图里各类别分得更开说明融合确实增加了区分度。这一步不需要写进生产代码但作为调试手段很有价值——如果融合后反而更混说明子模型选得不对或者特征拼接方式有问题。from sklearn.manifold import TSNE import matplotlib.pyplot as plt # 提取融合特征 feat_6 net_6.predict(X_test) feat_18 net_18.predict(X_test) feat_21 net_21.predict(X_test) feat_fused np.concatenate([feat_6, feat_18, feat_21], axis1) # t-SNE 降维 tsne TSNE(n_components2, random_state42) feat_2d tsne.fit_transform(feat_fused) plt.figure(figsize(8, 6)) for cls in range(8): idx y_test.argmax(axis1) cls plt.scatter(feat_2d[idx, 0], feat_2d[idx, 1], labelfC{cls}, s5) plt.legend() plt.title(FCNN fused feature t-SNE) plt.show()TSNE(n_components2)把 768 维降到 2 维random_state42固定随机种子保证可复现。散点大小设 5 避免重叠太严重。如果某两类始终混在一起比如暂降 C1 和中断 C2说明这两个类在特征空间里确实难分可以考虑增加子模型数量或换更深的网络。3.3 不同信噪比下的识别率对比与参数敏感度论文给出的结果25 dB 下 FCNN 平均识别率 99.47%15 dB 下 99.31%。单类看暂降 C1 在 25 dB 下 96.83%、15 dB 下 97.50%中断 C2 在 25 dB 下 98.96%、15 dB 下 97.00%其余六类基本都是 100%。暂降和中断识别率偏低的原因是噪声环境下两者的空间特征相似——都是幅值下降只是下降程度不同。对比传统方法FCNN 在 30 dB 下识别率 99.53%而基于生长-修剪优化 RBF 神经网络的方案只有 95.63%基于拉格朗日延迟器优化小波基的方案只有 92.00%。无噪声下 FCNN 99.88%也高于这两个对比方案。参数敏感度方面学习率 0.005 是论文用的值实际复现时如果 loss 震荡厉害可以降到 0.001如果收敛太慢可以升到 0.01 但别超过否则容易跳过最优解。Mini-Batch 大小 30 对 1000 点长度的序列来说偏小显存够的话可以加到 64 或 128训练更稳。Dropout 率 0.5 是常规值如果发现训练集识别率远高于测试集可以加到 0.6如果两者都低降到 0.3 试试。4. 避坑与排查复现 FCNN 时最容易翻车的五个地方4.1 子模型端到端训练导致识别率上不去现象把三个子模型和分类层拼在一起直接训练训练集识别率能到 99%但测试集只有 95% 左右曲线波动大。原因端到端训练时梯度同时回传到三个子模型它们的参数更新方向互相干扰导致提取的特征不稳定。论文里 LeNet-5 单模型测试识别率只有 96.11% 且曲线波动大就是这个问题的体现。解决严格按论文的三阶段训练流程走。先单独训练子模型到收敛冻结权重后再提取特征最后只训练分类层。如果非要端到端微调学习率要设得很小比如 0.0001并且先冻结子模型训练几轮分类层再解冻微调。4.2 融合特征维度对不上现象拼接三个子模型输出时报错维度不匹配。原因子模型最后一个池化层的输出维度取决于输入长度和池化层数。论文里输入 1000 点、输出 256 维但如果你改了网络层数或池化窗口大小输出维度会变。三个子模型如果结构不同输出维度可能不一致没法直接拼接。解决在每个子模型最后加一个GlobalAveragePooling1D或Flatten再接Dense(256)强制统一到 256 维。或者打印每个子模型output_shape确认维度后再拼接。4.3 噪声水平不匹配导致测试识别率虚低现象训练时用干净数据测试时加 15 dB 噪声识别率从 99% 掉到 80% 多。原因模型在干净数据上学到的特征对噪声敏感噪声一加特征分布偏移分类层就懵了。解决训练集里就要混入不同信噪比的样本。论文在 25 dB 和 15 dB 下分别测试说明训练时应该覆盖目标噪声范围。常见做法是训练集按信噪比分层采样比如 30 dB、25 dB、20 dB、15 dB 各占一部分让模型学会在噪声下提取稳定特征。4.4 BN 层加错位置导致收敛慢现象训练 loss 下降很慢几十轮还在高位震荡。原因BatchNormalization 加在激活函数之后或者加在池化层之后效果会打折扣。论文里 BN 层是加在卷积层和激活层之间的。解决按Conv1D - BatchNormalization - Activation - MaxPooling1D的顺序搭。BN 层的作用是让卷积输出分布标准化加在激活前才能让激活函数工作在敏感区间。4.5 学习率设太大导致 loss 爆炸现象训练几个 batch 后 loss 变成 NaN。原因SGD 学习率 0.005 对某些初始化权重来说偏大梯度爆炸。解决先降学习率到 0.001 跑通确认模型能收敛后再逐步升到 0.005。或者加梯度裁剪clipnorm1.0。另外检查输入数据是否归一化没归一化的数据幅值大卷积输出也大更容易爆。5. 进阶技巧用特征融合思路迁移到其他序列分类任务FCNN 这套“多子模型提特征 拼接融合 分类层”的架构不只能用在电能质量扰动分类上。任何一维序列分类任务——比如轴承故障诊断、心电图异常检测、电机振动信号识别——都可以套这个框架。关键是把子模型的差异拉开不要用三个结构一样的网络那样融合没意义。可以一个浅层抓局部、一个中层抓纹理、一个深层抓全局模式或者用不同卷积核长度比如 3、7、15让感受野不同。迁移时有个参数要重新调融合后的特征维度。论文里是 256×3768如果你的子模型输出不是 256 维分类层的输入维度要跟着改。另外 Dropout 率也要根据融合维度调整维度越高Dropout 率可以适当加大。我一般会先跑一个子模型看单模型识别率如果单模型已经 98% 以上融合提升空间不大如果单模型只有 95% 左右且波动大融合通常能拉到 99% 且曲线更稳。验证融合是否有效除了看识别率一定要跑 t-SNE。我见过融合后识别率涨了但 t-SNE 图里类别反而更混的情况那是子模型提取的特征冗余融合只是增加了维度没增加信息量。这时候要换子模型结构而不是继续加网络深度。从那以后我每次做序列分类只要单模型识别率卡在 95% 上下波动就强制走一遍“三个不同深度子模型 特征拼接 t-SNE 验证”的流程比调学习率和加数据都管用。希望帮到你。本文还有配套的精品资源点击获取