ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

绝缘子图像分类数据集实战:从JSON标签到CNN训练全流程

绝缘子图像分类数据集实战:从JSON标签到CNN训练全流程 简介绝缘子污染物模拟图像分类数据集面向电力设备运维与深度学习图像分类方向的研究人员、算法工程师及高校学生。资源聚焦绝缘子污秽状态识别任务包含约14400张已标注图像覆盖灰尘、脏污、正常等4个类别能支撑从模型训练到评估的完整分类流程。压缩包共2000个文件主体为约1998张jpg图片已按训练集和测试集分目录存放另含1个json标签配置文件与1个py可视化脚本便于查看类别映射和快速抽样预览样本整体体积约267.75MB。已有86人学习使用。数据按类别整理、目录结构清晰可直接用于PyTorch、TensorFlow等框架的训练与迁移学习配套的json文件明确了类别编号可视化脚本降低了数据检查门槛。此外作者还提供CNN分类网络、图像分割、YOLOv5改进等系列项目参考适合需要开展电力巡检视觉检测实验或毕设课题的读者拓展使用。1. 绝缘子污染物模拟图像分类数据集不是脏图那么简单绝缘子污染物模拟图像分类数据集是那种看起来平平无奇、实际跑起来信息量很大的资源。大约 14400 张已标注图像四分类训练集和测试集已经分开每类图片放在各自目录里资源里还带了一个 show 脚本能把样本先摊开看一遍。放在电力巡检视觉场景里它解决的是眼前这片绝缘子表面到底是灰尘、脏污还是属于正常状态这种非目标检测问题。适合刚接触图像分类的人做全流程练习也适合已经跑过 CIFAR、MNIST、想换一份真实感更强的工程数据集的从业者如果你想做绝缘子分割或 YOLO 检测改进也可以拿这份数据先练分类基线再把标签迁移到目标检测格式。2. 拿到资源先做三件事目录、JSON、可视化这个数据集不是解压完直接开训那种里面有两样东西需要你先看清楚一是文件组织方式二是 JSON 标签字段。文件名并不直接告诉你类别所以别急着一股脑丢进 DataLoader。2.1 目录结构别被文件名骗了下载解压后我建议先跑一条tree命令把一级目录打出来别只看资源管理器的缩略图。tree -L 2 .你大概率会看到类似这样的结构dataset/ ├── train/ │ ├── 0_normal/ │ ├── 1_dust/ │ └── ... └── test/ ├── 0_normal/ ├── 1_dust/ └── ...也可能train和test下面直接是同类图片文件夹。具体用什么目录名以 JSON 里的标签为准。文件名像isolador_vidro_semTextura_512x_863.jpgisolador_vidro是玻璃绝缘子semTextura表示无纹理512x说明这张图来自 512×512 的采样窗口863只是序号。文件名里没有任何类别信息所以必须通过目录名或 JSON 拿到标签。2.2 JSON 标签文件先确认四个类到底叫什么资源提到分类个数是 4常见的是灰尘、脏污、正常外加一个需要在 JSON 里确认的类别。不同资源作者的字段命名习惯不一样有的用class_id、有的用label值可能是字符串也可能是索引。拿到手先用一小段代码把结构探出来import json with open(labels.json, r, encodingutf-8) as f: labels json.load(f) print(type(labels)) print(list(labels.keys())[:5]) for k in list(labels.keys())[:3]: print(k, labels[k])如果labels是字典通常 key 是图片文件名value 是类别号或类别名。如果 value 是整数 0、1、2、3那就需要再找一个class_to_idx的映射文件或者从训练集目录顺序推断。我一般会顺手打印set(labels.values())确认到底有几个不同的标签值防止 JSON 里藏着第五类脏数据。2.3 show 脚本先把图摊开看一遍资源中提到的 show 脚本建议在写任何训练代码之前先跑一次。它的作用是把某一路径下的图片读出来拼成一张网格图并在图上标出类别名。常见运行方式是这样的python show.py --data ./dataset/train --num 16脚本内部一般会用matplotlib把 16 张图按 4×4 排列标题栏显示class_name。这一步的价值主要是验证两件事图片本身是否能正常打开会不会有单通道灰度图混在里面目录和 JSON 标签是否对得上比如目录叫0_normalJSON 里对应的是不是normal。如果 show 脚本报错先看是不是--data路径写错或者labels.json的编码不是 UTF-8。跑通这一步后面训练才不至于在数据读取阶段翻车。3. 把 14400 张图喂给 CNN数据加载、模型选型与参数资源里提到这是图像分类数据集训练集和测试集已经划分好。但划分好不等于能直接训练——你需要把目录结构变成 PyTorch 能读的数据集再决定用什么模型和参数。3.1 用 ImageFolder 读目录还是自己写 Dataset如果训练集每个类别的图片确实放在独立子目录下最省事的方式就是torchvision.datasets.ImageFolderfrom torchvision import datasets, transforms train_transform transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomHorizontalFlip(), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_ds datasets.ImageFolder(./dataset/train, transformtrain_transform) print(train_ds.classes) print(train_ds.class_to_idx) print(len(train_ds))这段代码里ImageFolder会按文件夹名排序生成class_to_idx也就是类别名到数字标签的映射。坑在于它不是按文件夹出现在磁盘上的顺序编号而是按字母顺序排序所以0_normal、1_dust这种带序号前缀的命名还得小心0_和1_排完序可能和你预期一致但如果是normal、dust、clean这种纯英文名序号就是字典序。训练前务必把class_to_idx打印出来对一眼。如果 JSON 里已经明确给了每个文件名的类别而目录结构没有按类别分文件夹那就直接用自定义 Datasetimport os import json from PIL import Image from torch.utils.data import Dataset class InsulatorDataset(Dataset): def __init__(self, img_dir, label_json, transformNone): self.img_dir img_dir self.transform transform with open(label_json, encodingutf-8) as f: self.label_map json.load(f) self.samples [] for fname, label in self.label_map.items(): path os.path.join(img_dir, fname) if os.path.exists(path): self.samples.append((path, label)) def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] img Image.open(path).convert(RGB) if self.transform: img self.transform(img) return img, label这个类只在__init__阶段检查文件是否存在不在__getitem__里反复做os.path.exists因为训练时每次采样都检查路径会拖慢速度。默认 label 是 JSON 里的原始值如果你把它当成整数索引直接传给CrossEntropyLoss要保证 label 是 0 到 3 的整型不要传成字符串 normal。3.2 模型选型ResNet18 足够做基线对 14400 张、四分类、图像尺寸 512×512 的数据集我不建议一上来就堆大模型。优先级应该是先跑通再提精度最后再考虑效率。模型输入尺寸特点适合场景ResNet18224 或 256参数量适中ImageNet 预训练普遍基线首选单卡也能跑EfficientNet-B0224计算量小精度曲线不错显存有限或需要快速验证ResNet50224容量更大但要防止过拟合数据质量高、需要更高上限ViT-B/16224全局建模但需要更强正则想对比 Transformer 时再试我的习惯是先固定一个 ResNet18 预训练模型跑 30 轮把基线指标放到一边然后根据混淆矩阵决定要不要换更复杂的模型。直接上大模型容易让问题变得很难排查你分不清是数据加载的问题、标签错位的问题还是模型容量的问题。3.3 训练参数与数据增强模拟图该不该狠增强模拟绝缘子图像有典型的人工渲染痕迹表面纹理偏干净颜色分布和真实巡检照片有差异。针对这种数据增强策略要中庸一点翻转、裁剪、亮度扰动可以做但不要加过度颜色畸变否则会把模型逼到只靠颜色识别。from torch.utils.data import DataLoader train_loader DataLoader(train_ds, batch_size64, shuffleTrue, num_workers4, pin_memoryTrue) val_loader DataLoader(val_ds, batch_size64, shuffleFalse, num_workers4)训练超参可以这样起步import torch.nn as nn model resnet18(pretrainedTrue) model.fc nn.Linear(model.fc.in_features, 4) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max40)参数含义按我常用的设定解释一下batch_size64在 512×512 原图上偏大所以一般会先Resize到 224 或 256 再进网络lr1e-3对预训练模型是安全的起点如果使用从头训练的模型建议降到 5e-4weight_decay1e-4用来压住模拟数据带来的高频纹理过拟合CosineAnnealingLR把学习率从 1e-3 平滑降到接近 0比固定学习率省心。这里还有一个容易被忽略的点资源里的test是官方划分好的测试集训练过程中不要拿它反复调参。我一般会从train里按类别随机抽 10% 到 20% 当验证集把官方test留到最后测。4. 绝缘子分类的避坑记录域差异、类别失衡与标签错位跑这份数据集时下面五类问题最容易让人卡住。每一条我都按现象 → 原因 → 解决拆开方便你对照排查。4.1 训练和验证准确率都很高换到实拍绝缘子就崩现象模拟图测试集上准确率超过 95%拿到现场拍的绝缘子照片模型大量误判连正常都认不准。原因模拟图像和真实照片存在 domain gap。资源里semTextura这种命名说明很多样本是去纹理渲染的模型很可能学的是背景颜色、光照分布这类表面特征而不是真实的污染纹理。数据增强里的随机裁剪和翻转不够弥补这种分布差异。解决先确认测试场景是否和训练场景同分布。如果目标是真实巡检图像需要在训练里加入真实照片混合或者对模拟图做纹理扰动比如随机加高斯噪声、模糊、局部遮挡。更稳的做法是用模拟图做预训练再用少量真实标注图做 fine-tune。评估指标不要只看模拟测试集单独攒一个真实场景测试集哪怕只有几百张也能反映真实问题。4.2 模型不停往正常或灰尘这类样本多的类别倒现象训练 loss 下降正常但每个 batch 的预测结果几乎都是同一个类准确率卡在某个阈值上不去。原因四分类数据不一定均衡。如果灰尘类图片数量占了一半以上普通交叉熵会让模型学会全猜灰尘也能拿到低 loss。解决训练前用Counter统计 JSON 中的类别分布对样本数少的类别提高权重。可以在CrossEntropyLoss里传weight或者在 DataLoader 里用WeightedRandomSampler。我一般先用前者改动最小如果还是不改善就换成WeightedRandomSampler让每个 epoch 里少样本类别能多被抽到几次。4.3 show 脚本画出来的标签和 JSON 对不上现象可视化时图片下方显示的类别名和图片内容明显不对比如一片干净玻璃被标成了脏污。原因show 脚本如果是按目录名直接显示第几个文件夹就不会去读 JSON此时目录里图片顺序和 JSON 里文件名的顺序不一致错位就发生了。解决先看 show 脚本源码里使用的是class_to_idx还是label_map。如果脚本只按子目录名显示标签就不要在 JSON 里再查一个 label否则标准不一致。标准做法是让目录名 → 标签和JSON → 标签统一走同一个映射。我建议以 JSON 作为唯一真相源目录名只用来归档不参与标签解析。4.4 文件名是连续编号随手 split 导致数据泄漏现象训练集 98%验证集 99%甚至验证集比训练集还高怎么想都不合理。原因这类数据集的图片编号常常来自同一批原始大图切出来的 patch比如 860 到 869 是同一张绝缘子源图的不同区域。如果按文件名的前 80% 做训练、后 20% 做验证相邻编号的 patch 可能出现在两边模型其实是见过这些像素块的。解决别按文件名顺序机械切分。先对文件名集合做随机train_test_split并且加随机种子保证可复现更稳妥一点把每个原始图 ID 从文件名里解析出来按 ID 做 group split保证同一个源图的 patch 不会跨训练集和验证集。跑完分组后再检查一下set(train_names) set(val_names)是否为空。4.5 后面接 YOLO 分类或检测时标签格式又乱了现象用这份数据在 ResNet 里没问题但想切到 YOLOv8 训练自定义分类器直接套用原来的目录结构训练时 warning 一堆。原因YOLO 分类模式对数据集目录约定比ImageFolder更严格它要求dataset/train/class_name/xxx.jpg和dataset/val/class_name/xxx.jpg并且类别名不能有特殊字符。原来的 JSON 标签映射在 YOLO 的datasets.YOLO接口里不会被自动读取。解决从这份资源迁移到 YOLO 时先用脚本读 JSON把图片复制或软链接成 YOLO 风格的目录结构再生成一个dataset.yaml里面写清楚path、train、val、names四类名称。我踩过最狠的一次是忘了把names的顺序和文件夹名字典序对齐导致 YOLO 把第 0 类和第 1 类对调了。这类问题在分类任务里比检测更难发现因为混淆矩阵不打印原始文件名。5. 从 JSON 到可训练清单标签解析、校验、转 CSV很多从业者拿到数据集后直接把 JSON 丢一边用文件夹名硬训。这种做法短期能跑但一旦要排查错误样本、做坏数据清洗你会发现没有一个统一的清单可用。所以我会花两分钟把 JSON 转成 CSV后续所有流程都基于 CSV。5.1 把 JSON 转成 CSV假设labels.json的结构是{filename: label}转 CSV 的脚本很简单import json import csv with open(labels.json, r, encodingutf-8) as f: data json.load(f) with open(labels.csv, w, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow([filename, label]) for fname, label in data.items(): writer.writerow([fname, label])如果 JSON 的 value 本身是字典比如{filename: ..., label: 2}那就先取出内层 label。CSV 的好处是可以用 Excel、pandas 或者任意文本工具直接打开看到文件名和标签的对应关系不用每次调试都重新 load JSON。之后训练脚本改成读 CSV逻辑也更简单。5.2 校验标签和文件系统的一致性JSON 转 CSV 之后先做一次双向校验确保 JSON 里的文件都存在目录里多出来的文件也能被筛出来。import os img_dir ./dataset/train missing [] for fname in data: path os.path.join(img_dir, fname) if not os.path.exists(path): missing.append(fname) print(missing count:, len(missing)) print(missing[:10])反向校验用os.listdir(img_dir)遍历实际文件再看是否出现在data里。这一步能提前发现两类坑一是 JSON 里引用了被删除的图片二是目录里混入了非图片文件。如果发现缺失文件很多优先检查路径前缀。5.3 类别分布统计分类任务的第一个数字不是准确率而是类别占比。from collections import Counter counter Counter(data.values()) for cls, cnt in sorted(counter.items(), keylambda x: x[1], reverseTrue): print(f{cls}: {cnt} ({cnt / len(data):.2%}))如果某个类占比超过 60%后续的 loss 权重、采样策略都要调整。如果某个类占比低于 5%我一般不指望靠数据增强解决而是先把这类样本在 show 脚本里过一遍看是不是标注本身就有问题——比如脏污和灰尘边界模糊有些图标得连人都无法达成一致。这种情况下先清洗标签比调模型更有效。6. 最后再逼自己一步分层验证与混淆矩阵训练完只有一个总准确率是不够的四分类里正常被误判成灰尘和脏污被误判成正常的代价完全不同。我一般会在验证阶段强制输出按类别的混淆矩阵并且把预测错误的编号回写到 CSV 里对照。先按类别分层抽样生成验证集保证每个类都进验证而不是随机乱切from sklearn.model_selection import train_test_split import pandas as pd df pd.read_csv(labels.csv) train_df, val_df train_test_split( df, test_size0.2, stratifydf[label], random_state42 )stratifydf[label]是关键它会按照原始 label 比例划分random_state42保证每次重跑结果一样。这个验证集比ImageFolder默认切出来的结果可靠得多。训练完成后把模型在验证集上的预测结果和真实标签拼成一张表import torch from sklearn.metrics import confusion_matrix y_true, y_pred [], [] for images, labels in val_loader: with torch.no_grad(): out model(images) pred out.argmax(dim1) y_true.extend(labels.tolist()) y_pred.extend(pred.tolist()) cm confusion_matrix(y_true, y_pred) print(cm)看到矩阵之后我习惯把每一行视为真实类别每一列视为预测类别。对角线低就是类内特征没学好如果非对角线集中在相邻类别比如灰尘被大量预测成脏污那说明这两个类在模拟图里本身就难分优先去看这批样本的原始图像确认是否需要合并类别或者增加细粒度标注。把混淆矩阵的误判样本名倒查出来用 show 脚本单独显示一遍import pandas as pd mis pd.DataFrame({ filename: val_filenames, true_label: y_true, pred_label: y_pred }) mis mis[mis[true_label] ! mis[pred_label]] print(mis.head(20))这一步看着简单但能让你从训练完了直接跳到知道下一次优化往哪走。从那以后我每次跑新数据集都会强制走一遍 show 脚本 → 校验 JSON → 分层抽样 → 混淆矩阵这条流程尤其是模拟图像数据集不这么走一遍我根本不敢拿测试集准确率出去说事。希望帮到你。本文还有配套的精品资源点击获取
返回列表