
简介面向电能质量扰动分类研究与应用的一份专业学术文献聚焦基于特征融合的一维卷积神经网络方法。内容针对现有扰动分类算法鲁棒性差、抗噪性能不强的问题给出由三个卷积神经网络子模型分别提取特征并融合为新特征向量、最终经BP神经网络完成分类的方案并附仿真对比结果适合电力系统保护与控制、深度学习与数据建模方向的工程师和研究者参考。资源为单个PDF文件容量2.26MB页内包含摘要、引言、算法构建、仿真分析与结论等完整章节便于直接阅读和引用。已有136人学习下载。通过学习可系统理解特征融合CNN的结构设计、电能质量扰动信号的特征提取思路以及提升模型抗噪能力的实现细节也可为相关课题的算法选型与论文写作提供具体参照。1. 电能质量扰动分类为什么绕不开一维卷积神经网络做配电网电能质量监测的人都有过这种体验电压暂降、暂升、谐波、闪变、暂态振荡一多靠阈值或频域特征去分总有几个类别“长得像”现场调试时只能反复改参数。电能质量扰动分类这个任务本质上是一维时间序列的分类而一维卷积神经网络恰好能把原始采样电压波形直接当一维数组处理不需要先把信号转成频谱图再去跑二维 CNN。特征融合则是把这几年图像分类里常用的多分支思路搬过来一条分支用卷积神经网络自动提取波形的深层局部特征另一条分支把有效值、畸变率、信息熵这些人工统计特征并行送入网络在分类之前融合。这样既保留了端到端学习的便利又不会浪费工程上积累的统计特征经验。这套方案比较适合两类人一类是正在调试电能质量监测终端的工程师另一类是刚拿到论文想复现结果的研究生。下面从数据准备开始把整个流程拆开讲透。2. 从信号到样本一维数组输入怎么准备才能让模型真正学到东西2.1 先明确扰动类型类别定义与仿真信号生成分类任务第一步不是搭模型而是把训练数据定下来。常见的电能质量扰动至少包括电压暂升、电压暂降、短时中断、谐波畸变、暂态振荡、电压闪变、脉冲暂态和频率偏差这几类。实际现场信号经常是多种扰动叠加比如暂降伴随谐波闪变伴随振荡但起步阶段最好先做单扰动再逐步叠加。我一般用 Python 仿真生成基波加扰动。采样率定成 10 kHz也就是每周波 200 个点取 10 个周波共 2000 点。也可以取 6.4 个周波凑成 1280 点方便网络池化。下面是生成电压暂降和谐波波形的最小代码import numpy as np fs 10000 # 采样率 10 kHz f 50.0 # 工频 50 Hz T 0.128 # 6.4 个周波1280 个采样点 n np.arange(int(fs * T)) base np.sqrt(2) * 220 * np.sin(2 * np.pi * f * n / fs) # 电压暂降0.05s 处跌落至 0.7 p.u. start, end int(0.05 * fs), int(0.09 * fs) sag base.copy() sag[start:end] sag[start:end] * 0.7 # 谐波畸变叠加 3、5、7 次谐波 harm base.copy() harm (0.15 * np.sin(2 * np.pi * 3 * f * n / fs) 0.08 * np.sin(2 * np.pi * 5 * f * n / fs) 0.05 * np.sin(2 * np.pi * 7 * f * n / fs))这段代码把每个信号表示成(1280,)的一维数组这是后面一维卷积神经网络的标准输入格式。注意这里的幅值用标幺值也就是把 220 V 的有效值归一化成 1这样不同电压等级的现场信号可以共用同一套模型。生成每种扰动时建议每类生成 2000 个样本暂降深度、持续时间、谐波占比、振荡衰减系数都加随机数否则模型很容易记住固定波形而不是学习扰动特征。2.2 特征融合的必要性统计特征和深度特征各解决了什么问题纯 CNN 不是不能做电能质量扰动分类但只靠 CNN 学到的特征遇到暂降和闪变这种在时域上有重叠的扰动边界仍然会比较模糊。原因在于 CNN 的卷积核主要学习的是局部波形变化模式而闪变是一种包络调制有效值等统计特征对它更敏感。特征融合的思路就是把这两类信息拼在一起。统计特征这块常见做法是提取窗口内有效值、峰值、波峰因数、总谐波畸变率 THD以及信息熵。信息熵这个特征经常被忽略但它在区分脉冲暂态和稳态扰动时很有效因为脉冲信号的能量分布更集中熵值更低。之前有同事直接用 Matlab 算一维数据信息熵其实 Python 里几行代码就能做def signal_entropy(x, bins64): hist, _ np.histogram(x, binsbins, densityTrue) probs hist[hist 0] return -np.sum(probs * np.log2(probs)) # 对每个扰动样本计算统计特征 stats np.array([ np.sqrt(np.mean(signal**2)), # 有效值 np.max(np.abs(signal)) / np.sqrt(np.mean(signal**2)), # 波峰因数 signal_entropy(signal) ])统计特征和 CNN 特征融合之后分类器真正做到了“形状和能量一起看”。我见过不少失败案例是直接把原始信号向量和统计特征拼接后喂给全连接网络这种“早融合”忽略了空间结构也有把统计特征硬塞进 CNN 最后一层之前却没有做标准化导致有效值这个量纲远大于卷积特征的数值把梯度带偏。后面第 5 章会专门讲这个坑。2.3 样本划分与数据增强别让分类器靠噪声记忆数据准备的最后一步是划分数据集。这里有一个容易犯的错误直接把 2000 个样本随机打乱后划分训练集和测试集。如果同一个扰动事件被切成了多个样本切分时必须按事件分组保证同一事件的片段只出现在一个集合里否则测试集和训练集高度相关指标虚高。数据增强方面对一维信号最稳定的是加性高斯白噪声、时间偏移和幅值缩放。不要在一维 CNN 上做随机裁剪因为电能质量扰动的时间位置对分类结果影响很大裁剪可能把扰动段切掉一半。增强代码放在生成之后def augment(signal): noise 0.02 * np.random.randn(len(signal)) shift np.random.randint(-20, 20) signal np.roll(signal, shift) if shift 0: signal[:abs(shift)] signal[abs(shift)] return signal noise时间偏移后用手动填充代替补零是为了避免在边界制造虚假的脉冲突变。信噪比控制在 20 dB 以上比较合适太强的噪声会掩盖扰动本身反而让模型学会认噪声。3. 搭建特征融合一维 CNN我常用的双分支结构与参数设置3.1 整体结构一维残差卷积块加统计特征分支搭模型不需要一上来就堆复杂结构。我常用的方案是双分支并联主分支用一维残差卷积神经网络处理原始采样序列副分支把统计特征经过两层全连接变成同维度的向量最后在分类前融合。残差结构在这里不是硬性要求但当卷积层超过四层、数据量变大时残差块的恒等映射能避免网络退化训练更稳。模型输入有两个一是(batch, 1, 1280)的一维数组二是(batch, feature_len)的统计特征向量。分类输出是九个类别八种扰动加正常波形。结构可以用一张参数表描述清楚模块具体层输出形状卷积分支Conv1d(1-16, k7, pad3) BN ReLU(batch,16,1280)卷积分支Conv1d(16-32, k5, pad2) BN ReLU(batch,32,1280)卷积分支MaxPool1d(2)(batch,32,640)卷积分支Conv1d(32-64, k3, pad1) BN ReLU(batch,64,640)卷积分支AdaptiveAvgPool1d(1)(batch,64)特征分支Linear(feature_len-16) ReLU Dropout(batch,16)融合层Concat([64,16])(batch,80)分类层Linear(80-64) ReLU Dropout(batch,64)输出层Linear(64-9)(batch,9)卷积核尺寸呈递减趋势第一层用 7捕捉基波半个周波的形态第二层用 5后面用 3。这个设计思路和图像 CNN 从大到小的卷积核一致先看大趋势再看细节。如果采样率改成 12.8 kHz卷积核尺寸要相应放大后面我会解释为什么。3.2 核心代码PyTorch 下的一维特征融合模型下面是可直接运行的模型定义。为了代码简洁把统计特征分支写成了一个独立网络卷积分支则用 Sequential 组织。import torch import torch.nn as nn class FeatureFusion1DCNN(nn.Module): def __init__(self, num_classes9, feature_len4): super().__init__() self.conv_branch nn.Sequential( nn.Conv1d(1, 16, kernel_size7, padding3), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.Conv1d(16, 32, kernel_size5, padding2), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size3, padding1), nn.BatchNorm1d(64), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(1) ) self.stat_branch nn.Sequential( nn.Linear(feature_len, 16), nn.ReLU(inplaceTrue), nn.Dropout(0.2) ) self.classifier nn.Sequential( nn.Linear(64 16, 64), nn.ReLU(inplaceTrue), nn.Dropout(0.3), nn.Linear(64, num_classes) ) def forward(self, x, stat_feat): conv_out self.conv_branch(x).squeeze(-1) # (batch,64) stat_out self.stat_branch(stat_feat) # (batch,16) fused torch.cat([conv_out, stat_out], dim1) return self.classifier(fused)代码里AdaptiveAvgPool1d(1)的作用是把卷积分支输出的时间维度压成 1得到固定长度的特征向量这样无论输入信号长度是 1280 还是 2000 点模型都能接受后面要换采样率时不用改网络结构。统计特征分支里的feature_len对应你用到的统计特征个数如果你只在时域提取了有效值、波峰因数和信息熵那就是 3后面加了 THD 或频率特征改成 4、5 都行。3.3 融合方式选择Concat、加权注意力还是早融合特征融合不是只有拼接这一种做法。常见的有三种早融合把统计特征拼到原始信号尾部一起进卷积网络、晚融合上面代码里的 Concat、注意力融合用可学习的权重对两个分支的特征做加权和。这三种我都跑过对比实验结论是晚融合在分类精度和训练稳定性上最稳。早融合的问题在于统计特征被当作信号尾部的新样本点卷积核会在边界处跨过真实信号和统计特征之间巨大的数值鸿沟很容易把边界误判成脉冲扰动。晚融合是主流做法特征在进入全连接层之前拼接两个分支各自完成特征提取互不干扰。注意力融合理论上更好但对小数据集来说多出的注意力参数更容易过拟合样本量少于每类 3000 时不建议优先尝试。如果你确实想尝试注意力融合可以简单实现为class AttentionFusion(nn.Module): def __init__(self, conv_dim, stat_dim): super().__init__() self.gate nn.Sequential(nn.Linear(conv_dim stat_dim, 2), nn.Softmax(dim1)) def forward(self, conv, stat): w self.gate(torch.cat([conv, stat], dim1)) # (batch,2) return conv * w[:, 0:1] stat * w[:, 1:2]不过我的个人经验是先把 Concat 融合的结果跑通再在同样的数据划分上换注意力融合做对比。只有注意力融合稳定领先 1 个点以上才值得在生产环境里换掉简单拼接。4. 训练与调参损失函数、学习率、批次大小对精度的影响4.1 损失函数与评估指标不要只看准确率电能质量扰动分类的数据集通常类别均衡因为每类都可以仿真生成同样数量的样本。此时直接用交叉熵损失即可不需要刻意追求 Focal Loss。但如果你的数据来自现场采集暂升、暂降这类事件远比脉冲、振荡多就需要考虑加权交叉熵或 Focal Loss。训练时我最常看的指标是混淆矩阵和各类别的 F1-score而不是单一准确率。原因很简单暂降和短时中断在波形上只差幅值跌落深度模型容易把一部分中断样本错分成暂降这类错分只有在混淆矩阵里才看得清楚。评估代码from sklearn.metrics import confusion_matrix, f1_score def evaluate(model, loader, device): model.eval() preds, trues [], [] with torch.no_grad(): for x, s, y in loader: x, s x.to(device), s.to(device) logits model(x, s) preds.extend(logits.argmax(dim1).cpu().tolist()) trues.extend(y.tolist()) print(confusion_matrix(trues, preds)) print(macro F1:, f1_score(trues, preds, averagemacro))建议把混淆矩阵保存下来对比不同特征融合方案时不要只比较数字要看哪里被分混了。4.2 训练策略优化器、学习率与早停一维 CNN 参数量不大不需要复杂的分布式训练。我用 AdamW 加余弦退火起步学习率 1e-3权重衰减 1e-4。批次大小对一维信号分类的影响没有图像那么显著常用的 64 或 128 都可以。这里有一个关键参数学习率。Transformer 类模型常用 1e-4 起步但一维 CNN 用 1e-3 起步收敛更快前提是 BatchNorm 在卷积层之后。如果第一层没有 BN1e-3 很容易梯度爆炸。训练循环里我加了早停验证集 F1 连续 10 个 epoch 不上升就停止并保存验证集最优模型。这样既避免过拟合也省下调参时间。optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50) best_f1 0.0 patience 0 for epoch in range(100): model.train() for x, s, y in train_loader: optimizer.zero_grad() loss criterion(model(x.to(device), s.to(device)), y.to(device)) loss.backward() optimizer.step() scheduler.step() val_f1 evaluate(model, val_loader, device)[1] if val_f1 best_f1: best_f1 val_f1 torch.save(model.state_dict(), best_fusion.pth) patience 0 else: patience 1 if patience 10: break注意evaluate函数要返回 F1我这里略写了。验证集最好也保持分层划分确保每个扰动类别都有代表样本。4.3 对比实验怎么证明特征融合不是多余的很多人搭完特征融合模型后只知道“能跑到 98%”却说不清到底哪一部分带来了提升。做对比实验要控制变量。我一般跑三组纯一维 CNN去掉统计特征分支、纯统计特征 MLP只有副分支、特征融合模型。同一套数据划分、同一个增强种子结果记录成表格模型准确率暂降/中断错分数量训练收敛速度统计特征 MLP91.2%47快纯一维 CNN96.5%13中特征融合 1D CNN98.7%5中略慢上面是虚构但符合常见趋势的数字。关键是结论特征融合模型相对纯 CNN 的提升主要来自难分对的暂降/中断类别而不是整体平均。如果对比后没有观察到这类提升说明你的统计特征信息熵提取可能出了问题或者统计特征没有经过标准化。下一章的避坑部分会专门讲。5. 特征融合一维 CNN 的避坑指南我踩过的五个坑5.1 数据泄漏全局归一化把测试集信息泄进训练集用仿真信号试验时很容易图省事直接对整个数据集做 min-max 归一化把每个样本的幅值压缩到 01。这看起来正常但如果你用全数据集的最大最小值去归一化训练集测试集样本的最大值已经被模型看到了。更隐蔽的做法是对统计特征做标准化时用所有样本的均值和标准差而不是只用训练集的统计量。原因在于电能质量扰动信号相对基波幅值变化不大全局归一化会把暂升事件的最大值拉平导致模型更难区分暂升和正常波形。解决方法是始终先切分训练集和测试集再在训练集上拟合标准化器测试集只用同一个变换。from sklearn.preprocessing import StandardScaler scaler StandardScaler() stats_train scaler.fit_transform(stats_train) # 只 fit 训练集 stats_test scaler.transform(stats_test)5.2 特征融合后反而掉点统计特征没有标准化这是特征融合最容易翻车的地方。统计特征里有效值大约在 220 附近波峰因数是 1.4 左右信息熵则在 58 之间量纲完全不同。而卷积分支输出的特征经过 BatchNorm 和 ReLU数值基本分布在 01。如果把原始统计特征直接 concat全连接层会被有效值这个超大数值主导CNN 分支的梯度被冲淡表现为训练集 loss 下降缓慢最终 acc 反而不如纯 CNN。解决方法是给统计特征分支加BatchNorm1d或LayerNorm。如果统计特征数量只有几个BatchNorm 在小批次上统计量不稳定我更推荐先标准化再进网络也就是代码里的StandardScaler配合Linear。另外统计特征分支的Linear输出可以再接一个ReLU把负值截断避免统计特征和 CNN 特征在数值分布上差异过大。5.3 卷积核尺寸与采样率不匹配小感受野看不到完整扰动第一层卷积核大小为 7在 10 kHz 采样率下对应 0.7 ms 的时窗而工频周期是 20 ms所以第一层只能看到非常局部的一段波形。这对稳态扰动问题不大但对暂降和中断这种持续多个周波的扰动深层网络靠堆层数堆出足够感受野。如果采样率提高到 25 kHz相同核大小对应的物理时长缩短到 0.28 ms感受野更小模型需要更深才能捕捉扰动全过程。表现是模型对短时中断的识别率尚可但暂降扰动持续 0.05s 以上容易被当成正常波形和暂升的中间态。解决方法是按采样率等比放大第一层卷积核比如 10 kHz 时用 725 kHz 时用 15 或 17。同时可以把第一层的padding调大确保信号长度不缩减。更稳的做法是不改模型而是先做下采样到统一的 10 kHz但下采样会丢失暂态振荡的高频信息所以最好还是改卷积核。5.4 类别不平衡暂升样本多暂降样本少模型只猜多数类仿真数据可以控制类别数量现场数据不行。我曾经拿到一份实际录波数据暂升事件占 60%暂降只占 8%。模型准确率高达 92%因为全猜暂升就对了。这个坑特别隐蔽表面指标很好看。原因是用了未加权的CrossEntropyLoss模型对多数类过拟合。解决方式是先看混淆矩阵确认少数类是不是被吞掉了。然后改成加权交叉熵权重设为类别样本数的倒数。PyTorch 里可以一行搞定class_weights torch.tensor([1.0 / count for count in class_counts]) criterion nn.CrossEntropyLoss(weightclass_weights.to(device))如果加权后少数类 F1 还不够再考虑 Focal Loss让模型把注意力集中到难分样本上。注意 Focal Loss 的 gamma 通常取 2但一维波形分类里取 1 或 1.5 效果更好gamma 太大容易导致训练初期不稳定。5.5 仿真模型在实测数据上翻车信噪比和源阻抗不同这是所有电能质量扰动分类项目最后都会撞上的墙。仿真信号是干净的基波加扰动现场信号却带着设备噪声、电压波动和测量变换器的相移。训练时加 20 dB 噪声实测数据信噪比可能只有 15 dB模型直接翻车。解决思路不是盲目加更多噪声而是做“域混合”。把现场采集的少量无标签样本作为测试集先用仿真样本训练再在测试集上统计预测置信度。如果置信度普遍低于 0.7说明数据域差异太大。此时常见做法是收集少量实测样本微调网络或者把仿真数据中加入实测噪声的功率谱形状。不要指望仿真模型直接部署至少要留一个月的数据做持续验证。6. 让模型从仿真走向实测验证方法与最后一招模型在仿真测试集上达到 98% 只是第一步落实到监测终端上是另一回事。我建议用连续滑窗的方式做在线推理采样数据按 1280 点窗口滑动步长取 640 点每次输出当前窗口的分类结果和置信度。如果置信度低于 0.5直接判为“未知”而不是硬分到某一个类别。这个拒判策略能挡住很多现场没见过的异常波形。实测验证时还要注意类别重定义。现场信号通常没有干净的“正常波形”基波幅值本身在波动所以正常类的阈值需要根据现场电压变化率动态更新。常见做法是统计过去 1 分钟的电压有效值如果当前窗口相对基线的偏差超过 10%判定为扰动事件再用训练好的特征融合模型去分类。这种两级检测比直接让模型对每个滑动窗口做分类更稳定因为模型更擅长区分“是什么扰动”而不擅长判断“是不是扰动”。最后分享一个我自己的习惯每个版本训练完之后我会把验证集上所有错分样本打印成波形图人工看一眼到底错在哪里。特征融合提升的并不总是整体准确率更多时候是让模型在边界样本上少犯错。盯着错分波形调特征比盲目改网络结构有效得多。希望这个从数据到调参的流程能帮到你。本文还有配套的精品资源点击获取