简介:面向电力系统故障检测与机器学习研究者的电信号局部放电图像分类数据集,共收录8712张jpg图片,划分为无局部放电(nopd,8187张)与有局部放电(pd,525张)两类,图片已按类别分文件夹存放,无额外标注文件,专用于图像分类而非目标检测场景。该压缩包共1个文件,为docx格式说明文档,总大小约1.41MB,体积小巧、内容集中,便于快速查看与部署使用。目前已有165人学习浏览,适合图像识别算法训练、局部放电自动判别及电力设备状态监测等研究场景。借助该数据集,研究者可直接开展二分类模型的训练与验证,省去自行采集和整理图像的环节,从而更高效地评估算法在局部放电识别任务上的表现,对模式识别与电力故障诊断工作具有实用参考价值。
1. 8712张局部放电信号图做2分类,值不值得花两周时间入场
做电力设备状态监测的同行应该都有过这种经历:现场传感器装好了,数据也采回了一大堆波形,但一到算法阶段就卡住了。原因很直接——缺一份整理干净、类别明确、量级够用的样本集。所以当我看到“电信号图局部放电分类数据集8712张2类别”这个标题时,第一反应是:这份数据刚好卡在一个很舒服的规模上。8712张图做2分类,不算大数据量,但足够把“有没有放电”这件事训练出可用的模型,也足够让刚接触这个方向的人在一台普通显卡上把完整流程跑通。这篇文章想聊的就是:拿到这样一份数据后,怎么把它变成能交付的分类模型,以及中间那些不跑一遍就发现不了的坑。
2. 读懂电信号图:先把“局部放电的图谱”和“普通图片”区分开
2.1 PRPD谱图与原始波形的区别:为什么2分类能用图像模型
局部放电的电信号,传统上是一维时间序列。采样率动辄几十兆赫兹,直接拿原始波形喂给深度学习模型,要做对齐、降噪、变长处理,非常折腾。行业里更常见的做法,是把一个或多个工频周期的放电脉冲,按相位和幅值累积投射到二维平面上,形成相位分辨局部放电图谱,业内一般叫PRPD谱图。横轴是0到360度的工频相位,纵轴是放电幅值或放电量,颜色或灰度表示该位置上放电脉冲出现的次数。这样处理后,放电模式就变成了图像中的纹理和聚集形态,图像分类模型就可以直接被拿来用。
2分类任务在工程上对应的通常是一个很现实的问题:这组信号里到底有没有威胁绝缘的放电活动。类别A可以定义为“存在局部放电”,类别B定义为“噪声/干扰”或“无放电”,也可能是“内部放电”对“电晕放电”这种更细的区分。具体取决于采集场景,但模型结构不需要变化,本质就是一个二选一的图像判别问题。用图谱而不是原始波形,还有另一个好处:图谱已经去掉了采样率、触发相位、幅值尺度这些现场变量,模型学到的特征更稳定,过拟合风险更低。
2.2 8712张和2类别在工程上意味着什么:规模、边界与验收标准
8712张图放在图像分类领域是真的不多,但2分类任务的困难程度也被这数字拉低了不少。同样8712张,如果做10分类,平均每类不到900张,会相当难受;而2分类平均每类约4300张,在迁移学习下已经足够训练一个不太容易过拟合的轻量模型。
一个最朴素的经验判断:如果两类样本平衡,并且采集来源不是单一设备,这数据完全可以支撑“训练集6969张、验证集872张、测试集871张”这样的划分,测试集结果能在92%以上。听起来指标很漂亮,但工程验收不能只看准确率。局部放电检测属于负样本敏感的监控类任务,漏放一个放电信号,后果远比误报一次严重。所以验收要同时看灵敏度(recall)和特异性(specificity),最好配混淆矩阵判定。2分类的模型可以很简单,但验收口径必须按设备状态监测的逻辑来定,不能拿一个通用图像分类的报表糊弄过去。
对刚接触这个方向的人来说,这份数据的边界也很清楚:它能帮你验证“有没有放电”的判断链路,但它替代不了现场验证。图谱上的放电模式会随传感器类型、电压等级、环境噪声变化,需要后续用自己设备采的样本做微调。我的建议是把它当做一个标准起点,而不是终点。先把基准确立住,后面加了新场景数据才知道往哪个方向调。
3. 数据集落地第一步:目录整理、划分策略与预处理参数
3.1 按类别建目录:一个脚本把散装图片整理成ImageFolder格式
拿到这份数据集后,第一件事不是训练,而是先把目录结构理清。PyTorch的torchvision.datasets.ImageFolder可以直接按子目录名读取类别标签,目录结构越规范,后面代码越省事。比较理想的形态是dataset/class_a/*.jpg和dataset/class_b/*.jpg这样。
但现实中的数据包往往不是这种形态。有的按采集日期分目录,有的文件名里带标签,有的用Excel记录对应关系。我一般会先整个目录扫描一遍,统计文件分布,再决定怎么抽取标签。如果标签可以从目录名或文件名里提取,就用一个脚本统一整理:
# organize.py import shutil from pathlib import Path src = Path("raw_samples") # 原始图片所在目录 label_map = {"label_a": "discharge", "label_b": "noise"} # 目录名到标准类别的映射 for f in src.rglob("*.jpg"): raw_label = f.parent.name # 先取上一级目录名 if raw_label not in label_map: print(f"[skip] 未识别的目录: {f}") # 打印跳过的文件便于人工核对 continue dst_dir = Path("dataset") / label_map[raw_label] dst_dir.mkdir(parents=True, exist_ok=True) shutil.copy(f, dst_dir / f.name)这里的rglob("*.jpg")会递归匹配所有jpg文件,不关心原始目录嵌套多深。label_map是真实的分类映射,按数据包附带的说明去填,不要靠猜。打印跳过信息这一步很重要,它能帮你发现哪些文件是放错位置或者命名不规范的,后续做数据清洗时有个依据。如果数据包是用csv标注的,就把raw_label = f.parent.name换成去查表,其他逻辑不变。
整理完成后,一定手动抽查一下文件夹里的图片内容。局部放电谱图长得很像,像与不像要人眼过一遍,不要想当然认为标注文件一定是对的。我见过二次整理时把两个类别的目录搞反的,几十行脚本跑完,整个数据集就废了,那才是真的血泪教训。
3.2 模型划分不能随机到底:分层划分与同源样本的控制
8712张图划分成训练、验证、测试,很多人直接random.shuffle就切了,这是很大的隐患。局部放电数据集经常按采集时段或设备编号组织,同一个设备同一天采的图,放电模式高度相似。如果这些图同时出现在训练集和测试集里,模型的测试分数会被严重高估,这叫重叠样本导致的“数据泄漏”。
所以划分策略里必须加两个东西:分层抽样和组划分。分层抽样保证两个类别在训练/验证/测试里的比例和全集一致,避免某类全跑进训练集、另一类全跑进测试集。组划分则保证同一来源的样本不被拆开。如果数据包里有设备编号或文件名前缀这种来源信息,优先按这个分组,再组内划分:
# split.py from sklearn.model_selection import train_test_split from torchvision.datasets import ImageFolder dataset = ImageFolder("dataset") # 自动按目录生成标签 targets = dataset.targets # 每个样本的类别索引 # 第一次划分:分出20%作为验证+测试 train_idx, tmp_idx = train_test_split( range(len(targets)), test_size=0.2, stratify=targets, random_state=42, ) # 第二次划分:把20%再对半分,得到10%验证、10%测试 tmp_targets = [targets[i] for i in tmp_idx] val_idx, test_idx = train_test_split( tmp_idx, test_size=0.5, stratify=tmp_targets, random_state=42, ) print(f"train={len(train_idx)}, val={len(val_idx)}, test={len(test_idx)}")stratify=targets是分层抽样的关键参数,它会按类别比例做随机切分;random_state=42固定随机种子,保证每次跑结果一致,方便复现对比。如果你的数据包里样本是按设备分组存储的,就应该在train_test_split之前先对设备ID做划分,再把同一个设备的样本整体划入同一侧,而不是用上面的纯随机版本。这里宁可少拿几个训练样本,也要保证测试集的独立性,否则后面模型上线时落差会很大。
3.3 resize、归一化与灰度转彩色:预处理参数这样设最省事
局部放电图谱的原始文件常见有两种形态:单通道灰度图和RGB伪彩图。模型侧的输入通常固定为三通道、224×224像素。灰度图转三通道时,不能直接np.stack三个相同通道再用全零填充,而是把灰度值复制到RGB三个通道里,让模型在三个通道上看到一致的信息。很多数据处理脚本在这步用了torch.zeros填充,结果就是空白区域变成纯黑,模型很容易把“纯黑边缘”当成分类特征。
from torchvision import transforms train_transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomAffine(degrees=5, translate=(0.05, 0.05)), transforms.RandomVerticalFlip(p=0.5), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ])Resize((224, 224))直接拉伸到固定尺寸,而不是Resize后CenterCrop。局部放电谱图的放电区域有的集中在某个相位区段,居中裁剪容易把放电区域裁掉,直接拉伸虽然在比例上有点失真,但对CNN来说通常更好学。RandomAffine只做小角度旋转和小幅平移,模拟传感器相位偏移。RandomVerticalFlip可以翻,但水平翻转不建议加,因为PRPD谱图的左右对应工频正负半周,翻转后物理意义就变了。Normalize用的是ImageNet的均值和方差,这是迁移学习的常规配置,除非你从零训练,否则不要自定义均值和方差,徒增调参难度。
4. 用轻量CNN把2分类跑起来:模型选型、训练脚本与三个必调参数
4.1 模型选型:为什么先试ResNet18而不是直接上EfficientNet
8712张图、2类别、轻量部署,这个组合下模型选型考虑的不是“谁的准确率最高”,而是“谁最好调、最好部署、出问题好解释”。我一般第一版固定会用ResNet18,理由很实际:结构简单,预训练权重多,训练速度快,显存占用低,出bug时定位也快。MobileNetV3和EfficientNet虽然参数更少,但训练时需要更多技巧才能发挥优势,作为第一版基线并不划算。
| 模型 | 适用场景 | 第一版选用的理由 | 需要担心的点 |
|---|---|---|---|
| ResNet18 | 中规模数据集的通用基线 | 收敛快,预训练权重稳定,调试容易 | 模型偏大,但不影响CPU推理 |
| MobileNetV3 | 嵌入式设备部署 | 参数量小,适合边缘端 | 对训练技巧敏感,容易欠拟合 |
| EfficientNet-B0 | 追求高精度上限 | 精度好,FLOPs低 | 预训练权重差异影响迁移效果,调试周期长 |
如果你是新手,第一版直接用ResNet18,把流程跑通后再考虑换小模型。如果是上边缘设备,先拿ResNet18把准确率基线打出来,再蒸馏或者换轻量模型,这样你的每一步都有对比依据,不会一上来就被MobileNetV3的玄学问题卡住。
4.2 PyTorch最小训练脚本:从加载8712张图到输出准确率
有了整理好的目录和预处理流程,训练代码可以收敛到一套固定套路。下面这个脚本是可运行的最小骨架,核心结构不会因为换数据集而改变,你只需要替换路径和类别数。
import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import models, datasets from torchvision.transforms import transforms # 预处理 transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) # 加载数据集 fullset = datasets.ImageFolder("dataset", transform=transform) # train_idx / val_idx / test_idx 来自 split.py 的输出 trainset = torch.utils.data.Subset(fullset, train_idx) valset = torch.utils.data.Subset(fullset, val_idx) testset = torch.utils.data.Subset(fullset, test_idx) train_loader = DataLoader(trainset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True) val_loader = DataLoader(valset, batch_size=32, shuffle=False) # 模型 model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1) model.fc = nn.Linear(model.fc.in_features, 2) # 把1000分类改成2分类 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = model.to(device) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode="min", patience=3, factor=0.5) for epoch in range(20): model.train() for images, labels in train_loader: images, labels = images.to(device), labels.to(device) optimizer.zero_grad() loss = criterion(model(images), labels) loss.backward() optimizer.step() # 验证 model.eval() val_loss, correct, total = 0.0, 0, 0 with torch.no_grad(): for images, labels in val_loader: images, labels = images.to(device), labels.to(device) outputs = model(images) val_loss += criterion(outputs, labels).item() * labels.size(0) correct += (outputs.argmax(1) == labels).sum().item() total += labels.size(0) val_loss /= total accuracy = correct / total scheduler.step(val_loss) print(f"epoch={epoch+1}, val_loss={val_loss:.4f}, acc={accuracy:.4f}")model.fc = nn.Linear(...)是替换最后一层全连接,把ImageNet的1000类输出改成2类,这一步很多新手会漏掉,结果输出维度对不上。AdamW从3e-4起步是迁移学习的安全区间。ReduceLROnPlateau监控验证集loss,连续3个epoch不降就把学习率减半,这个机制能明显减少手动调学习率的次数。训练阶段model.train()和验证阶段model.eval()必须成对出现,dropout和BN层的行为依赖这个开关,漏了会导致验证集指标异常跳变。
跑完20个epoch,把测试集也过一遍,记录准确率、每个类别的精确率和召回率,这组数字就是你的基线。
4.3 影响收敛的3个参数:学习率、batch size与权重衰减
同样的数据和代码,参数不同,收敛速度能差出好几倍。3个参数要优先关注。
学习率是头号敏感参数。用预训练权重时,从头开始训练的学习率一上来就用0.01基本必炸,loss会直接飞掉。从3e-4起步,然后监控loss曲线的下降斜率。如果你的训练loss前三五个batch还在0.7附近不动,大概率学习率偏小;如果loss直接跳到nan,那就是学习率大了。配ReduceLROnPlateau后,基本不用手动干预。
batch size影响的是收敛稳定性和显存占用。32对于224×224的ResNet18是安全值,16也行,但梯度噪声更大,loss曲线会更抖;64则要求显存至少6GB,收敛速度不会成比例提升。2分类任务不必追求大batch,稳定优先。
权重衰减是很多人忽略的。局部放电图谱纹理比较单一,模型容易死记硬背训练集中的细微模式,2分类的过拟合信号还不明显,但weight_decay从1e-4开始设好了,后面省去一次重训的麻烦。AdamW自带解耦权重衰减,效果优于传统Adam+L2,直接用它。
5. 电信号图2分类避坑:标签噪声、数据泄漏与过拟合的5个现场
5.1 训练集92%、测试集69%:八成是数据泄漏
现象:训练集和验证集的准确率都在90%以上,换到测试集直接掉到70%不到,模型看起来练得很好,一上线就露馅。
原因:数据划分时没有考虑样本来源,同一个设备、同一时段采集的图片同时出现在训练集和测试集,模型记住了这套设备的特有模式,而不是泛化的放电特征。
解决:回到第3.2节,按设备ID或采集批次分组划分。如果没有这些信息,一个补救办法是把文件名里的时间戳提出来,用时间维度做划分。验证集提升到和测试集一致的水平后,再继续调参,这比任何trick都重要。
5.2 验证集loss往下走,准确率却不动:标签噪声在现场的表现
现象:训练loss平稳下降,验证loss也在降,但验证准确率卡在某个值附近不动,比如长时间在87%上下波动。
原因:数据集中存在标注错误的样本,或者是边界样本(比如很弱的放电信号和噪声在视觉上几乎一样)。模型在努力拟合噪声标签,但学不到更细的判别边界。
解决:先输出混淆矩阵,看是哪一类被经常混淆。然后拿被错分的样本做人工复核。这类数据集常见的标签噪声来源是采集时的自动标注程序阈值设错。如果你有精力和时间,重标一遍写不出来的类别,比换任何模型都有效。
5.3 只学图像不学信号:模型把背景当成放电特征的翻车现场
现象:训练时准确率很高,但把图片四周加了一圈黑边后,预测结果全乱了;或者新数据上一遇到不同的边框、字体、水印发型,准确率骤降。
原因:图谱图片里除了放电区域,还有坐标轴、刻度线、图例、水印这些“背景物”,模型如果偷懒,会直接学这些视觉标记,因为它们跟标签之间存在偶然的相关性。
解决:预处理时把图片统一裁剪到只保留谱图主体区域,或者用掩膜把坐标轴区域置为统一灰度值。再做一点随机扰动:加轻微模糊、改变对比度。最终验证时,一定准备一批从别的来源截取、样式更杂的图谱做鲁棒性测试,模型不能只看样式不看内容。
5.4 类别数量不均衡:用加权采样而不是盲目复制样本
现象:两个类别数量如果差异大,比如一类7000张、另一类1712张,训练出来的模型对少数类基本不敏感,少数类样本几乎全被预测成多数类。
原因:模型在最小化整体损失,学到的决策边界天然偏向数量多的类别。对2分类的局部放电任务来说,如果少数类恰好是“有放电”,这在设备监测场景是致命的。
解决:优先用WeightedRandomSampler做加权采样,让每个batch里两类样本数量接近,而不是简单复制少数类图片。复制会造成模型对同一样本反复拟合,泛化能力反而下降。代码里加两行即可。
from torch.utils.data import WeightedRandomSampler # labels 是全量训练样本的标签 class_counts = torch.bincount(torch.tensor(targets)) weights = 1.0 / class_counts[targets].float() sampler = WeightedRandomSampler(weights, num_samples=len(weights), replacement=True) train_loader = DataLoader(trainset, batch_size=32, sampler=sampler, num_workers=4)5.5 过拟合信号图:相位翻转增广不一定安全
现象:训练准确率接近100%,验证准确率反而在某个epoch后开始下降,典型的过拟合曲线。
原因:模型容量对8712张来说偏大,训练轮次过长,加上增广策略不当。很多人在PRPD谱图上顺手加水平翻转增广,这会让模型把左右对称的放电模式当成同一个模式,但真实场景中正负半周的放电特性是不同的物理过程,学习被误导。
解决:把水平翻转去掉,保留小幅度的垂直翻转和仿射扰动,同时加EarlyStopping。ReduceLROnPlateau负责降学习率,但后面仍然容易过拟合,直接按验证集准确率连续5个epoch不升就保存当前权重并停止。别小看这个习惯,能帮你避免深夜守着loss曲线干瞪眼的尴尬。
6. 验证模型到底学到了什么:混淆矩阵与Grad-CAM的行为分析
6.1 五折交叉验证:对8712张的2分类任务更可靠
单次划分的测试集只有871张,结论的置信度有限。8712张的规模正好可以跑五折交叉验证,把五份测试结果合并统计,得到的准确率和置信区间才是敢写在交付文档里的数字。每折训练一个ResNet18,五个模型独立评估,最后一并统计。这个操作能顺带暴露数据划分中的偶然偏差,比如某一折明显偏低,基本可以断定那一折里混入了难样本或脏样本。
6.2 混淆矩阵与Grad-CAM可视化:别只盯准确率
准确率在2分类上很容易虚高,尤其是类别不平衡时。交付前,两个验证动作必须做:一是打印混淆矩阵,看模型把哪类样本误判成另一类;二是用Grad-CAM类激活映射,把模型判断“有放电”时关注的区域叠回原图,确认高亮区域是否落在图谱的放电相位区间。比如电缆内部放电通常出现在第一、三象限的特定相位区间,如果模型高亮的区域集中在坐标轴或图例上,说明它又在偷看样式特征,这个模型需要回炉重训。
我自己做这类项目时,最早也吃过亏。第一次跑完准确率94%,心里很满意,直到用Grad-CAM看了一张样本,发现模型高亮的区域是右下角的采集时间水印。那次之后,我再也不敢拿准确率作为唯一交付指标,混淆矩阵、可视化、交叉验证一个都不少。做电信号图的分类,模型不该只学会“认图”,而是真的学会“认放电”。希望这篇笔记能帮你把8712张图的2分类任务一次跑通,少走这些弯路。
本文还有配套的精品资源,点击获取