资讯详情

变压器故障诊断增量学习:渐进式神经网络pnnfd实现与工程实践

📅 2026/10/29 21:32:53 | 华诺云谱 👁 阅读
变压器故障诊断增量学习:渐进式神经网络pnnfd实现与工程实践
简介面向变压器故障诊断场景的概率神经网络PNN实现源码包适合电力系统研究人员、电气工程专业学生以及算法爱好者参考学习。资源以PNN模型为核心包含完整MATLAB代码和配套数据文件能够帮助读者理解PNN在变压器故障识别中的完整建模流程包括输入层、模式层、分类层设计以及故障特征处理思路。压缩包共2个文件包含1个m脚本和1个mat数据文件m脚本实现算法主逻辑mat文件提供训练与测试样本整体仅6KB轻量易用便于快速验证算法效果。目前已有329人学习下载适合需要入门或复现变压器故障诊断案例的读者。通过运行源码可直观观察不同故障类型的分类输出结果再结合描述中提到的高斯核均值与方差调整、数据清洗与标准化等要点能够进一步掌握PNN参数优化和防止过拟合的实践方法。整体上资源短小精悍兼顾原理演示与代码实操对理解概率神经网络在电力设备状态监测中的应用具有直接帮助。1. 变压器故障诊断的难点为什么 pnnfd 值得试一次变压器故障诊断里我见过太多模型在测试集上漂亮、上现场就翻车的例子。这也是我一直关注 pnnfd 这条路线的原因——它把渐进式神经网络Progressive Neural Network的迁移思路用在故障诊断上不推翻旧模型而是每遇到一个新工况、新故障类型就新增一列网络保留之前学到的全部能力。对做油中溶解气体DGA分析、局部放电图谱识别或者振动信号诊断的工程师和研究生来说这条路最直接的收益是旧数据不用丢、旧模型不用重训新知识往里加就行。这套方案并不复杂但有几个关键点决定成败任务怎么切、特征怎么算、横向连接怎么设、旧列冻结有没有生效。这篇文章里我会把从数据准备到 PyTorch 实现再到验证的完整链路拆开讲参数给到可以直接抄作业的程度也会把最容易踩的坑单独列出来。2. 变压器故障诊断前期DGA 信号和经典判据的边界2.1 油中溶解气体和局部放电图谱能告诉我们什么变压器内部故障会分解绝缘油和固体绝缘材料产生特征气体。工程上最常用的是七种氢H2、甲烷CH4、乙烷C2H6、乙烯C2H4、乙炔C2H2、一氧化碳CO、二氧化碳CO2。过热、电弧放电、局部放电三类故障在这七种气体上呈现不同比例比如电弧放电产生大量乙炔高温过热时乙烯明显上升而局部放电倾向于氢和甲烷偏高。只看单组分绝对浓度不可靠因为油龄、脱气处理、负载变化都会影响数值所以主流做法是把气体之间的关系换算成比值特征。局部放电是另一条重要信号路径。用高频电流互感器或超声波传感器采集放电信号再按工频周期做相位分解可以得到相位分辨局部放电图谱也就是常说的 PRPD 图谱。只要抓到典型图谱就能区分自由金属颗粒、绝缘内部气隙、沿面放电等不同放电源这也是“变压器局部放电典型图谱”在实战里最有价值的地方。DGA 和 PRPD 图谱一个偏化学、一个偏物理覆盖的故障模式互补这正好是 pnnfd 多任务增量学习的天然素材。2.2 三比值法的盲区在哪里如果只靠传统判据比如三比值法问题很明显三组比值编码有大量组合没有对应故障解释遇到多故障叠加时会给出一个看似合理但实际错误的结论。更常见的问题是当乙炔浓度很低、只有高温过热特征时编码容易落在没有定义的区域而当一台老变压器本身产气速率就不低时绝对浓度判据几乎失效。这些盲区不是三比值法本身做错了而是它把连续的气体浓度硬编码成离散状态信息损失太大。pnnfd 的思路不是替代三比值法而是把经过对数变换的浓度特征和比值特征一起交给神经网络让模型自己找边界同时在多个任务之间共享低层特征。这样既保留了可解释的输入又避开了离散编码的断层。2.3 用特征工程把原始气体变成 pnnfd 的输入我一般会把 DGA 特征做成两层浓度对数特征和比值对数特征。浓度跨度从几个 uL/L 到几万 uL/L直接喂给网络会放大数值尺度差异所以先做 log1p。比值特征保留 CH4/H2、C2H2/C2H4、C2H4/C2H6、C2H2/CH4、CO2/CO 这五项它们分别刻画热分解程度、放电能量高低、温度区间和固体绝缘老化程度。import numpy as np import pandas as pd def dga_features(raw: pd.DataFrame) - pd.DataFrame: # raw 至少包含 H2, CH4, C2H6, C2H4, C2H2, CO, CO2 七列单位 uL/L cols [H2, CH4, C2H6, C2H4, C2H2, CO, CO2] missing [c for c in cols if c not in raw.columns] if missing: raise ValueError(缺少气体列: , .join(missing)) df raw[cols].copy() # 用极小值兜底避免除零后出现 inf 或 NaN df df.replace(0, 1e-9) ratio pd.DataFrame(indexdf.index) ratio[CH4/H2] df[CH4] / df[H2] ratio[C2H4/C2H6] df[C2H4] / df[C2H6] ratio[C2H2/C2H4] df[C2H2] / df[C2H4] ratio[C2H2/CH4] df[C2H2] / df[CH4] ratio[CO2/CO] df[CO2] / df[CO] conc_log np.log1p(df) conc_log.columns [flog_{c} for c in df.columns] feat pd.concat([conc_log, np.log1p(ratio)], axis1) # 截断极端值防止个别样本把后续标准化拉偏 feat feat.clip(lower-5, upper10) return feat这段代码的逻辑按顺序看先做缺失列检查避免后面访问不存在的字段再用 1e-9 替换零值这一步处理的是气体浓度出现 0 的样本目的是让比值计算不会产生 inf最后特征拼接和 clip 是为了后续在训练集上做标准化时不至于被离群样本带偏。注意这里的标准化应该单独在训练集上拟合再用同一组参数转换测试集代码里没有体现这一步是为了保持转换函数的纯粹性。3. 任务划分是 pnnfd 的第一道坎三种切法与数据泄漏检查3.1 任务顺序影响迁移效果先易后难还是先难后易pnnfd 的任务顺序不是随便排的它直接影响横向连接能迁移出什么样的特征。以我自己的经验第一列最好放样本量最充足、类别最均衡的任务通常是“正常 过热 一般放电”这类的三分类或四分类因为这一列会冻结成为后面所有列的骨干特征提取器。如果把一个数据很少的稀有故障类型放第一列后面所有列都会在一个不够鲁棒的特征空间上长出来效果很难救回来。任务顺序的另一个原则是复杂度递增。第一列用 DGA 表格数据第二列再上 PRPD 图谱的二维输入第三列如果需要可以接入 Simulink 仿真的暂态电流特征。每新增一列旧列权重完全冻结新列通过横向连接去读旧列的中间层激活。先易后难不是硬性规定但我会尽量避免把高维输入放第一列因为高维输入需要更大的网络容量和更多样本否则早期特征很容易过拟合。3.2 按设备、按工况、按故障类型切任务三种切法对比任务切法直接影响这套方案是“能泛化”还是“看起来能泛化”。按变压器 ID 切是最严格的做法训练集里出现过的变压器测试集里彻底不出现这样才能验证模型换一台同型号设备时是否还能用。按故障类型切适合做增量故障识别比如先学会“正常/过热/放电”三个大类后面再新增“匝间短路”“铁芯多点接地”等细分类别。按工况切则是针对同一批设备的轻载、额定、过载和励磁涌流场景。切法验证目标适用场景风险按设备 ID 切跨变压器泛化能力产品化、故障诊断系统部署训练数据不足时指标偏低按故障类型切增量学习新故障在线监测系统持续升级新类别样本少导致不均衡按工况切同一设备多工况适应变电站现场状态检修时间跨度内的概念漂移三种切法不是互斥的实践里我会把设备 ID 切作为主评估方式再叠加工况切作为压力测试。原因很简单现场真正需要的是换台变压器还能用而不是在测试集上刷分。pnnfd 之所以适合这种切法是因为它天然按任务组织数据每个任务可以对应一个或一组变压器旧变压器的知识被冻结保留新变压器来了就新建一列适配它的数据分布。3.3 用分层抽样和代码检查数据泄漏数据泄漏是这类方案里最隐蔽的坑。常见错误是直接对整张表做 train_test_split导致同一台变压器的多组采样同时出现在训练集和测试集里模型实际上靠记忆设备特征来打分换台设备就失效。正确的做法是把数据先按变压器 ID 分组再对组做划分或者在组内做分层抽样保证故障类别比例一致。from sklearn.model_selection import GroupShuffleSplit def split_by_device(df, group_coldevice_id, test_size0.2, seed42): gss GroupShuffleSplit( n_splits1, test_sizetest_size, random_stateseed ) train_idx, test_idx next(gss.split(df, groupsdf[group_col])) return df.iloc[train_idx].copy(), df.iloc[test_idx].copy() # 用分组切分后再检查每个故障类别在两个集合里的比例 train, test split_by_device(data) print(train[label].value_counts(normalizeTrue)) print(test[label].value_counts(normalizeTrue))这里的核心参数是 GroupShuffleSplit 的 groups 参数它保证同一个 group 的所有样本只落在训练集或只落在测试集。我习惯在切分后打印两个集合的类别分布这一步能提前暴露训练集里根本没有某类故障的问题。如果测试集里的某个类别占比过高说明原始数据集本身类别不均衡这时候应该在后续训练里对少数类加权重而不是简单换一次随机种子。4. 从零把 pnnfd 故障诊断代码跑通PyTorch 列网络实现与训练参数4.1 列网络和横向连接单文件实现按 pnnfd 的结构每个任务对应一个新列旧列冻结新列通过横向连接读取旧列隐藏层激活。横向连接的本质是一个线性映射把旧列的高维隐层向量压缩到 adapter_dim 维再与新列自己的输入拼接。这里的关键参数是 adapter_dim我一般取 16 到 64 之间太小会丢失旧列的特征信息太大会让新列输入维度过高、过拟合。import torch import torch.nn as nn class Column(nn.Module): # 每个任务一个列自己输出分类结果同时暴露中间层激活给后续列 def __init__(self, in_dim, hidden_dim, n_classes): super().__init__() self.backbone nn.Sequential( nn.Linear(in_dim, hidden_dim), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), ) self.head nn.Linear(hidden_dim, n_classes) def forward(self, x): h self.backbone(x) return self.head(h), h class Lateral(nn.Module): # 横向连接把旧列的隐藏层激活投影到低维 adapter 空间 def __init__(self, hidden_dim, adapter_dim): super().__init__() self.proj nn.Linear(hidden_dim, adapter_dim) self.act nn.ReLU() def forward(self, h_old): return self.act(self.proj(h_old)) class ProgressiveNet(nn.Module): def __init__(self, hidden_dim128, adapter_dim32): super().__init__() self.columns nn.ModuleList() self.laterals nn.ModuleList() # laterals[i] 对应第 i 列新增时创建的横向连接集合 self.hidden_dim hidden_dim self.adapter_dim adapter_dim def add_task(self, in_dim, n_classes): # 新列输入 原始特征维度 所有旧列横向连接的输出维度 lateral_in_dim len(self.columns) * self.adapter_dim col Column(in_dim lateral_in_dim, self.hidden_dim, n_classes) # 冻结旧列 for old in self.columns: for p in old.parameters(): p.requires_grad False # 为每个旧列创建一个横向连接层 new_laterals nn.ModuleList() for _ in self.columns: new_laterals.append(Lateral(self.hidden_dim, self.adapter_dim)) self.columns.append(col) self.laterals.append(new_laterals) def forward(self, x, task_idx): # 提取所有旧列的隐藏层激活经横向连接后拼接到当前列输入 old_features [] for j in range(task_idx): with torch.no_grad(): _, h_old self.columns[j](x) old_features.append(self.laterals[task_idx][j](h_old)) if old_features: x torch.cat([x] old_features, dim1) out, h self.columns[task_idx](x) return out, h这段代码有几个必须注意的点。第一add_task 里冻结的是旧列的 backbone 和 head 所有参数冻结后反向传播不会改动它们这是防止灾难性遗忘的核心。第二横向连接层属于新任务的可训练参数它只负责把旧列特征投影给新任务不回改旧列。第三forward 里每次都要重新计算旧列的前向结果虽然冻结了权重但数据还是要过一遍网络这在数据量大的时候会增加推理开销所以实际工程里可以对旧列输出做缓存。4.2 冻结旧列的训练循环忘了微调那套习惯训练循环里最容易犯错的是优化器参数没筛干净。如果你直接把 net.parameters() 传给 Adam旧列虽然标记了 requires_gradFalse但优化器里挂着这些参数也不会更新可问题在于某些实现会因此报错或者你在别处解除了冻结。更稳妥的做法是只用新列的参数构建优化器从根源上保证只有新任务在学习。def train_task(net, train_loader, task_idx, lr1e-3, epochs80): # 只收集新列和它对应横向连接的参数 new_params list(net.columns[-1].parameters()) for lat in net.laterals[-1]: new_params.extend(lat.parameters()) optimizer torch.optim.Adam(new_params, lrlr, weight_decay1e-4) criterion nn.CrossEntropyLoss() net.train() for epoch in range(epochs): total_loss 0.0 for xb, yb in train_loader: out, _ net(xb, task_idx) loss criterion(out, yb) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() if epoch % 10 0: print(ftask {task_idx} epoch {epoch} loss {total_loss / len(train_loader):.4f})学习率我习惯从 1e-3 开始批次大小 64epoch 数看损失曲线是否走平一般 60 到 120 之间足够。weight_decay 设 1e-4 起正则作用。如果发现新任务训练早期损失震荡明显可以先降到 3e-4 再跑一遍如果出现过拟合把 Dropout 从 0.3 提到 0.5 比调小网络更有效。4.3 评估指标别只用 accuracy看低压放电类召回变压器故障诊断的数据几乎都是不均衡的正常样本远多于故障样本放电类又远少于过热类。只看 accuracy 会让一个“永远预测正常”的模型拿到很高的分。我一般会同时看两类指标多分类 macro-F1 和每个类别的召回率尤其是低能放电和高能放电这一类容易被吞掉的小众故障。from sklearn.metrics import f1_score, recall_score def eval_learned_tasks(net, eval_loaders, task_idx_list): net.eval() results {} for tid in task_idx_list: preds, y_true [], [] with torch.no_grad(): for xb, yb in eval_loaders[tid]: out, _ net(xb, tid) preds.extend(out.argmax(dim1).cpu().tolist()) y_true.extend(yb.tolist()) results[tid] { macro_f1: f1_score(y_true, preds, averagemacro, zero_division0), recall: recall_score(y_true, preds, averagemacro, zero_division0), } return results这套故障诊断代码的评价逻辑是对已经学过的每个任务都单独打分因为每个任务有自己的分类头不能混在一起算准确率。输出结果里如果某个任务的 macro-F1 明显低于其他任务去看混淆矩阵通常会发现少数类严重漏报。这时候返回去调整损失函数里的类别权重是比换模型更快的解法。5. pnnfd 变压器故障诊断排查记录5 个最容易翻车的点5.1 准确率 0.93 却在低压放电类上翻车现象测试集总体准确率 93%但低压放电类的召回率只有 20%几乎全被归到正常类。原因样本量严重不平衡低压放电类在整个数据集里占比不足 5%交叉熵损失被多数类主导模型没有动机去学少数类的边界。解决在损失函数里按类别频率的倒数设置权重或者对少数类做 SMOTE 过采样。我一般先在 CrossEntropyLoss 里传 weight 参数试一轮如果召回率上不去再用过采样。加权重后低压放电类召回率通常能从 20% 提到 60% 以上代价是正常类的误报率会略升这是可接受的。5.2 换了台同型号变压器指标断崖式下跌现象在训练过的变压器上测试效果很好换到一台同型号但从未参与训练的变压器上F1 直接掉了二十多个点。原因数据划分时没有按变压器 ID 分组同一台变压器的数据同时出现在训练集和测试集模型其实记住了设备本身的偏置而不是故障特征。解决用第 3.3 节的 GroupShuffleSplit 重新切数据保证测试集里全是没见过的变压器。如果重新切分后指标本身降得厉害说明你的特征里有和设备强相关的字段比如油温、负载率需要评估是否保留它们。这个坑我踩过不止一次做状态检修类项目时一定要先问一句测试集里的变压器训练时见过吗5.3 特征里全是 inf 和 NaN比值特征的除零陷阱现象训练时报错输入里出现 inf 或 NaN网络损失变成 nan。原因DGA 数据里乙炔经常为 0C2H2/C2H4 直接变成除零CO 或 CO2 缺失也会让 CO2/CO 变为 inf。解决在特征工程里用 1e-9 替换零浓度再计算比值最后对结果做 clip 截断。注意不要用 0 填充0 的倒数仍然是 inf。另外如果用的是现成的特征工程脚本检查它有没有处理缺失值pandas 的默认除法遇到 0 会产生 inf 而不是报错这是最容易漏掉的一环。5.4 明明写了 requires_gradFalse老任务还是被“洗掉”现象新增第二个任务训练完后回到第一个任务的测试集上评估指标比训练前掉了很多。原因有两个典型原因。一是优化器仍然包含了旧列参数虽然 requires_grad 为 False 不会更新梯度但如果有代码改动了模型的 state_dict比如保存和加载时覆盖了旧权重就会前功尽弃。二是你在训练新任务时没有把旧列切成 eval 模式Dropout 和 BatchNorm 的统计量被新任务的数据污染。解决优化器只传新列参数同时在新增任务后固定旧列用 net.columns[j].eval() 保证旧列的 Dropout 不参与推理。做完这两个动作后再跑一次旧任务评估指标应该和训练前一致否则继续查保存和加载逻辑。5.5 加入局放图谱任务后DGA 任务跟着掉点现象第二列是用 PRPD 图谱训练的 CNN 列训练完返回去评估第一列的 DGA 分类发现 macro-F1 下降约 3 个百分点。原因冻结机制本身没有失效但旧列在推理时接收的是新任务输入的二维图谱数据如果旧列设计时只接受一维表格特征数据形状不匹配会导致推理路径改变甚至重新初始化了旧列的输入层。解决多模态任务接入时不要把新旧特征塞进同一个列。正确做法是保留原 DGA 列不动新建一个以 PRPD 图谱为输入的 CNN 列两个列各自输出最后在决策层做融合。如果确实需要在新列里引用旧列特征横向连接的输入应该取自旧列的隐藏层激活而不是让旧列直接吃新任务的原始数据。提示遇到这类问题先用保存的旧任务 checkpoint 单独评估旧列看指标是否恢复到训练前水平。能恢复说明冻结逻辑正确掉点是推理路径的问题恢复不了说明冻结逻辑本身有漏洞。6. 用 Matlab/Simulink 励磁涌流仿真压测 pnnfd局放图谱扩展与投稿前实验6.1 用励磁涌流仿真生成负样本变压器空载合闸会产生励磁涌流涌流波形里含有大量二次谐波和内部故障电流很像如果模型分不清很容易把一次正常的合闸误判成匝间短路。我一般会在 Simulink 里搭一个 50MVA、110kV/10kV 的三相变压器饱和模型在 0.02s 时合闸采集高压侧三相电流再用 FFT 算基波和二次谐波的幅值比。二次谐波占比我习惯取 15% 作为涌流和内部故障的初始分界这部分数据作为负样本任务加入 pnnfd让模型见过这个暂态过程。基于 Matlab/Simulink 的变压器励磁涌流仿真与特征分析并不难难的是把仿真数据转换成和真实监测数据一致的格式。我的做法是只取涌流前 5 个工频周期的二次谐波含量、直流分量和波形畸变率作为特征不直接丢原始波形避免让模型依赖仿真环境里的采样频率。6.2 给 PRPD 图谱留一个扩展位如果你打算把变压器局部放电典型图谱作为第二类输入需要在网络设计阶段就给二维输入留好位置。DGA 列用 MLPPRPD 图谱列用两层卷积加全局池化横向连接同样从卷积层的展平特征里取。任务顺序建议 DGA 在前、PRPD 在后因为表格数据样本更充足先学出稳定的故障初分类再用图谱列学更细的放电源类型。6.3 投稿前必须补的三组实验如果你打算投故障诊断方向 SCI 期刊我做过的经验是光有准确率不够审稿人现在要求持续学习类方法做三组对照。第一组是任务顺序鲁棒性实验固定数据只换 task 顺序报告 macro-F1 的均值和方差第二组是和微调、EWC、LwF 三个基线的对比证明 pnnfd 无遗忘的优势第三组是类别不均衡下的混淆矩阵和 PR 曲线。这三组都补齐方案的可信度和中稿概率会明显提升。我自己现在拿到新的现场数据都会先问三个问题这批变压器训练时见过吗故障类别均衡吗有没有励磁涌流和局放这种容易误判的工况想清楚之后再决定是开一个新列还是重训旧模型。这套 pnnfd 方案不一定是最聪明的模型但它是目前让我在换设备、加工况时最省心的路线。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑