ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

变压器DGA油色谱数据建模避坑指南:从数据体检到随机森林基线

变压器DGA油色谱数据建模避坑指南:从数据体检到随机森林基线

简介:这份变压器溶解气体数据.rar面向电力设备运维、故障诊断研究与机器学习建模人员,聚焦变压器油中溶解气体分析这一典型场景,帮助解决故障类型识别与状态评估问题。数据覆盖中低温过热、高温过热、低能放电、高能放电、局部放电及正常状态六类样本,涉及氢气、甲烷、乙烷、乙烯、乙炔等特征气体的浓度规律,可用于比值法、判别函数法及智能诊断算法的验证与对比。压缩包约6.97MB,文件总数与类型明细上游暂未提供,但整体体量轻便,便于快速下载与本地实验。目前已有177人学习下载,具备一定参考热度。读者可借助实测数据开展特征工程、分类模型训练与故障预测实验,理解不同故障下气体生成规律,为智能监控系统开发和电力系统可靠性研究提供数据支撑。

1. 变压器溶解气体数据.rar:一份油色谱数据到手后,先别急着训练模型

拿到一个名为「变压器溶解气体数据.rar」的压缩包,很多人的第一反应是解压、读表、扔进模型跑一遍准确率。但真正做过电力设备状态评估的人会先停一下:这份数据到底是 DGA(Dissolved Gas Analysis,油中溶解气体分析)的原始浓度记录,还是已经被人为标注过故障类型的分类数据集?两种情况下,后面的处理路径完全不同。变压器溶解气体数据在工程里通常对应七种特征气体——氢气、甲烷、乙烷、乙烯、乙炔、一氧化碳、二氧化碳,有时还带总烃和微水。这些数值背后是油纸绝缘在过热、放电、受潮等不同劣化机制下产气规律的变化。这份数据能解决的问题很具体:用气体浓度组合去判断设备是否存在潜伏性故障,以及故障大概属于哪一类。适合谁?做电力设备在线监测、状态检修、故障诊断的算法工程师,以及想把 DGA 数据用起来的运维分析人员。如果你只是想要一个干净的特征表直接调库,那大概率会在数据清洗和标签定义上翻车。

2. 先搞清楚七种气体和三类标签:DGA 数据的字段含义与选型逻辑

2.1 七种特征气体的物理意义与量纲陷阱

油中溶解气体不是随便选的七个指标。氢气主要来自局部放电和水分分解;甲烷、乙烷是低温过热产物;乙烯在较高温度过热时显著上升;乙炔则是高能放电的典型标志,浓度一高基本可以锁定电弧或火花放电。一氧化碳和二氧化碳反映固体绝缘纸的劣化。问题在于,很多压缩包里的数据表只给列名,不给单位。常见单位有 μL/L(即 ppm)和 mg/L 两种,前者是体积分数,后者是质量浓度,数值能差出好几倍。我一般会先看乙炔那一列的数值范围:如果最大值只有零点几,大概率是 ppm;如果出现几十甚至上百,要么是严重故障样本,要么单位是别的。另一个坑是「总烃」列,它通常是甲烷到乙炔四者之和,如果数据里既有分项又有总烃,训练时不要同时放进去,否则共线性会让树模型的重要性排序失真。

2.2 标签体系:三比值、大卫三角和故障类型编码

DGA 数据最常见的标签来源是 IEC 三比值法或罗杰斯比值法,也有用大卫三角的。三比值用 C2H2/C2H4、CH4/H2、C2H4/C2H6 三个比值编码,每个比值分区间给 0、1、2 三档,组合成故障类型。如果你拿到的数据里有一列「故障类型」写着「低温过热」「中温过热」「高温过热」「局部放电」「火花放电」「电弧放电」这类中文,那基本就是按比值法或改良版标注的。选型时要注意:如果标签是规则生成的,模型学到的其实是规则本身,换一批数据可能就不准;如果标签来自实际检修结论,样本量通常很小,且类别极不平衡。我的做法是先把标签分布打出来,少于 30 个样本的类别要么合并,要么用分层抽样,别硬做多分类。

2.3 用 pandas 读压缩包并做第一轮体检

拿到 .rar 文件,Windows 上可以用 7-Zip 或 WinRAR 解压,Linux 下用 unrar。解压后常见格式是 .xlsx 或 .csv。下面这段代码做三件事:读表、看字段、查缺失和量纲异常。

import pandas as pd import numpy as np # 读入解压后的 csv,注意编码可能是 gbk 或 utf-8 df = pd.read_csv("dga_data.csv", encoding="gbk") # 统一列名,去掉空格和括号 df.columns = [c.strip().replace("(", "(").replace(")", ")") for c in df.columns] # 查看前五行和字段类型 print(df.head()) print(df.dtypes) # 七种气体列名,按实际表头调整 gas_cols = ["H2", "CH4", "C2H6", "C2H4", "C2H2", "CO", "CO2"] # 缺失值统计 print(df[gas_cols].isnull().sum()) # 描述性统计,重点看 min 和 max 判断量纲 print(df[gas_cols].describe().loc[["min", "max", "mean"]]) # 检查是否有负值,气体浓度不应为负 for col in gas_cols: neg_count = (df[col] < 0).sum() if neg_count > 0: print(f"{col} 存在 {neg_count} 个负值,需要处理")

逻辑说明:先统一列名是为了后续按固定列名索引,避免因为空格或全半角括号导致 KeyError。缺失值统计能告诉你哪些气体列是「稀疏」的,比如乙炔在很多正常样本里就是 0 或未检出,这不是缺失,而是真实工况。描述性统计里的 max 值如果远大于 mean,说明有极端故障样本,后面标准化时要考虑用 RobustScaler 而不是 StandardScaler。负值直接置为 0 或按缺失处理,因为浓度不可能为负。

参数说明:encoding参数要根据实际文件调整,中文表头常见 gbk,如果报 UnicodeDecodeError 就换 utf-8 或 gb18030。gas_cols列表必须和表头完全一致,大小写敏感。如果表头是中文「氢气」「甲烷」,要么改列表,要么在读入后重命名。

3. 从原始浓度到可训练特征:比值、编码和标准化怎么做

3.1 三比值编码的代码实现与边界处理

三比值法的核心是把三个比值映射到整数编码。下面这段代码按 IEC 60599 的常见分区实现,注意分母为 0 的情况。

def ratio_code(df): # 避免分母为 0,加一个极小值 eps = 1e-6 df = df.copy() # C2H2/C2H4 r1 = df["C2H2"] / (df["C2H4"] + eps) # CH4/H2 r2 = df["CH4"] / (df["H2"] + eps) # C2H4/C2H6 r3 = df["C2H4"] / (df["C2H6"] + eps) def code_r1(x): if x < 0.1: return 0 elif x < 3: return 1 else: return 2 def code_r2(x): if x < 0.1: return 1 elif x < 1: return 0 else: return 2 def code_r3(x): if x < 1: return 0 elif x < 3: return 1 else: return 2 df["code1"] = r1.apply(code_r1) df["code2"] = r2.apply(code_r2) df["code3"] = r3.apply(code_r3) df["ratio_combo"] = df["code1"].astype(str) + df["code2"].astype(str) + df["code3"].astype(str) return df df = ratio_code(df) print(df["ratio_combo"].value_counts())

逻辑说明:加 eps 是为了防止分母为 0 导致除零错误,但要注意如果 H2 本身是 0,CH4/H2 会变成一个极大值,编码会落到最高档。实际工程中,H2 为 0 且 CH4 也接近 0 的样本通常是正常老化,直接归到正常类更合理。ratio_combo把三个编码拼成字符串,方便做分组统计或独热编码。如果要做多分类,可以把 combo 映射到故障类型,但映射表要按你采用的导则来,不同导则的分区边界有差异。

参数说明:eps取 1e-6 是经验值,太小可能受浮点精度影响,太大可能改变比值量级。三个 code 函数的边界值 0.1、1、3 来自常见三比值分区,如果你的数据标签是按其他导则生成的,边界要相应调整。

3.2 标准化与不平衡处理:为什么不能直接上 SMOTE

七种气体的浓度跨度极大,乙炔可能是个位数,二氧化碳可能上千。不做标准化,基于距离的模型(KNN、SVM)会被大数值列主导。我一般用 RobustScaler,因为它对异常值不敏感。下面代码同时做标准化和类别权重计算。

from sklearn.preprocessing import RobustScaler from sklearn.utils.class_weight import compute_class_weight gas_cols = ["H2", "CH4", "C2H6", "C2H4", "C2H2", "CO", "CO2"] X = df[gas_cols].values y = df["fault_type"].values # 假设标签列叫 fault_type scaler = RobustScaler() X_scaled = scaler.fit_transform(X) # 计算类别权重,用于模型训练 classes = np.unique(y) weights = compute_class_weight("balanced", classes=classes, y=y) class_weight_dict = dict(zip(classes, weights)) print(class_weight_dict)

逻辑说明:RobustScaler 用中位数和四分位距做缩放,比 StandardScaler 更适合含极端故障样本的 DGA 数据。compute_class_weight给出每个类别的权重,训练时传给模型的class_weight参数,比直接过采样更稳妥。为什么不推荐 SMOTE?因为 DGA 样本的特征空间不是欧氏的,两个故障样本之间做线性插值可能生成物理上不存在的浓度组合,比如乙炔和乙烯的比例关系被破坏,模型学到的是假模式。

参数说明:class_weight="balanced"会按类别频率反比给权重,频率低的类别权重大。如果某个类别只有几个样本,权重会非常大,可能导致模型偏向该类别,这时可以考虑合并稀有类或改用 focal loss。

3.3 特征工程:除了比值还能加什么

原始七种气体加三个比值编码是最小特征集。实际项目中我还会加几个衍生特征:总烃(CH4+C2H6+C2H4+C2H2)、乙烯占比(C2H4/总烃)、乙炔占比(C2H2/总烃)、CO2/CO 比值。CO2/CO 比值对区分固体绝缘劣化和油劣化有帮助,一般大于 7 认为纸绝缘正常,小于 3 要警惕。这些特征不需要复杂计算,但能显著提升树模型的分裂效果。注意不要加太多高度相关的特征,否则特征重要性会分散,解释性变差。

4. 避坑与排查:DGA 数据建模中最容易翻车的五个地方

4.1 现象:模型准确率 99%,换一批数据掉到 60%

原因:标签是规则生成的,且训练集和测试集来自同一批规则标注的数据,模型学的是规则边界而不是真实故障模式。解决:如果标签来自三比值法,直接查表输出结果即可,不需要训练模型;如果一定要用机器学习,确保测试集来自不同时间段或不同设备,并且标签来自检修结论而非规则。

4.2 现象:乙炔列大量为 0,模型完全忽略这个特征

原因:乙炔在正常样本中普遍未检出,数值为 0,但在故障样本中可能很高。如果直接标准化,0 值会被缩放到负值区间,树模型虽然不受影响,但线性模型会出问题。解决:对乙炔单独做二值化特征「乙炔是否检出」,再保留原始浓度。或者用对数变换 log1p 压缩量纲。

4.3 现象:训练时 loss 震荡,验证集准确率不上升

原因:学习率太大,或者批次里混入了极端值样本。DGA 数据中个别故障样本的乙炔浓度可能是正常值的几百倍,梯度会被拉爆。解决:先用 RobustScaler 缩放,再检查是否有超过 10 倍四分位距的离群点,必要时截断或单独分析。学习率从 1e-3 降到 1e-4 试试。

4.4 现象:三比值编码后某些组合从未出现

原因:样本量太小,或者数据采集时已经过滤掉了某些故障类型。三比值有 27 种组合,实际数据可能只覆盖 8 到 10 种。解决:不要强行做 27 类分类,把稀有组合合并到「其他故障」或按故障大类(过热、放电、受潮)重新标注。

4.5 现象:用 CO2 和 CO 做特征后模型性能下降

原因:CO2 和 CO 受油中气体扩散和吸附剂影响大,且与故障类型的关系不如烃类气体直接。解决:可以保留但降低权重,或者只保留 CO2/CO 比值。如果数据里 CO2 列缺失严重,直接去掉。

5. 一个可复现的基线:用随机森林做 DGA 故障分类并验证

5.1 完整训练脚本与交叉验证

下面这段代码从读数据到输出分类报告,是一条能跑通的最小路径。假设数据已经解压为 csv,标签列名为 fault_type。

import pandas as pd import numpy as np from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import StratifiedKFold, cross_val_predict from sklearn.preprocessing import RobustScaler from sklearn.metrics import classification_report, confusion_matrix # 读数据 df = pd.read_csv("dga_data.csv", encoding="gbk") df.columns = [c.strip() for c in df.columns] gas_cols = ["H2", "CH4", "C2H6", "C2H4", "C2H2", "CO", "CO2"] df = df.dropna(subset=gas_cols + ["fault_type"]) # 衍生特征 df["total_hydrocarbon"] = df["CH4"] + df["C2H6"] + df["C2H4"] + df["C2H2"] df["C2H4_ratio"] = df["C2H4"] / (df["total_hydrocarbon"] + 1e-6) df["C2H2_ratio"] = df["C2H2"] / (df["total_hydrocarbon"] + 1e-6) df["CO2_CO_ratio"] = df["CO2"] / (df["CO"] + 1e-6) feature_cols = gas_cols + ["total_hydrocarbon", "C2H4_ratio", "C2H2_ratio", "CO2_CO_ratio"] X = df[feature_cols].values y = df["fault_type"].values # 标准化 scaler = RobustScaler() X_scaled = scaler.fit_transform(X) # 随机森林,带类别权重 rf = RandomForestClassifier( n_estimators=300, max_depth=8, min_samples_leaf=3, class_weight="balanced", random_state=42 ) # 分层交叉验证 cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) y_pred = cross_val_predict(rf, X_scaled, y, cv=cv) print(classification_report(y, y_pred)) print(confusion_matrix(y, y_pred)) # 特征重要性 rf.fit(X_scaled, y) importance = pd.Series(rf.feature_importances_, index=feature_cols).sort_values(ascending=False) print(importance)

逻辑说明:先做缺失值剔除,DGA 数据里气体列缺失的样本通常无法用均值填充,因为缺失可能意味着未检测。衍生特征里 total_hydrocarbon 是总烃,C2H4_ratio 和 C2H2_ratio 反映故障类型倾向,CO2_CO_ratio 辅助判断绝缘纸状态。随机森林的 max_depth 设 8 是为了防止过拟合,min_samples_leaf 设 3 保证叶子节点有足够样本。交叉验证用 StratifiedKFold 保持类别比例。特征重要性输出能告诉你哪些气体对分类贡献大,通常乙炔和乙烯会排前面。

参数说明:n_estimators=300是精度和速度的折中,样本量小可以降到 100。max_depth根据样本量调整,样本少于 500 时设 5 到 6。class_weight="balanced"处理不平衡,如果类别已经均衡可以去掉。random_state固定后结果可复现。

5.2 结果怎么看:混淆矩阵和特征重要性的解读

分类报告里重点看 macro avg 的 f1-score,因为类别不平衡时 accuracy 会虚高。混淆矩阵能看出哪些类别容易被混淆,比如低温过热和中温过热经常互相误判,因为它们的产气特征重叠。特征重要性里如果 CO2 排最后,说明它对当前标签体系贡献小,可以考虑去掉。如果 H2 重要性异常高,检查是不是 H2 的量纲和其他气体差太多,标准化没做好。

5.3 一个容易忽略的验证:按设备分组划分数据集

如果数据里有一列设备 ID,不要随机划分训练集和测试集。同一台设备的不同时间样本高度相关,随机划分会导致测试集信息泄漏,准确率虚高。正确做法是按设备 ID 分组,用 GroupKFold。这个细节在论文里经常被忽略,但在工程落地时是致命的。我一般会在数据体检阶段就检查有没有设备 ID 列,有的话优先用分组划分。

5.4 从基线到落地:阈值调整和在线更新

基线模型跑通后,实际部署时还要做两件事。一是调整分类阈值,比如把「电弧放电」的召回率调到 0.95 以上,宁可误报不可漏报,因为漏掉一个电弧放电样本可能导致设备损坏。二是设计在线更新机制,新样本积累到一定数量后重新训练,但要用时间序列划分验证,确保模型没有退化。DGA 数据的分布会随设备老化缓慢漂移,固定模型半年后性能下降是常态。

我自己的习惯是每次拿到新的 DGA 数据包,先花二十分钟做字段体检和标签分布,再决定是直接查表还是训练模型。这个顺序反过来,后面返工的时间会多得多。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进