简介:本资源为面向电力巡检与计算机视觉方向的YOLO绝缘子缺陷检测数据集,适合目标检测初学者、算法工程师及电力智能运维研究人员使用,用于训练和验证绝缘子缺陷识别模型。压缩包共1257个文件,约121.86MB,包含314张jpg图像、314个txt标注文件、628个xml标签文件及1个yaml配置文件,图像与标注一一对应,可直接用于YOLO系列模型的训练与评估。数据集覆盖不同环境、光照与缺陷类型,如裂纹、剥落、污秽、异物附着等,有助于模型学习绝缘子缺陷特征。目前已有188人学习下载。通过该数据集,读者可快速搭建绝缘子缺陷检测实验流程,掌握从数据组织、标签解析到模型训练与验证的完整方法,为输电线路智能巡检、事故损伤评估等场景提供可靠的数据支撑与排错参考。
1. 314 张绝缘子缺陷图像:这份 YOLO 数据集到底能跑出什么结果
输电线路巡检这行有个共识:绝缘子缺陷检测是典型的「样本难凑、场景难复现、标注难统一」三难问题。你让一个班组一年爬几百基塔,拍回来的照片里真正带裂纹、剥落、污秽、异物附着的可能不到十分之一,剩下的全是正常件。这份数据集直接给了 314 张带标签的绝缘子图像,覆盖巢、绝缘体、铁塔三类目标,文件名从 img_0723_72.jpg 到 img_0962_139.jpg 这种现场编号风格,说明是从真实巡检素材里筛出来的。它解决的不是「有没有数据」的问题,而是「能不能快速验证一个 YOLO 训练流程跑不跑得通」的问题。适合两类人:一是刚接触 YOLO 目标检测、想拿电力场景练手的工程师;二是做边缘部署前需要小样本快速验证模型收敛性的开发者。314 张不算多,但足够你把标注格式、训练配置、验证指标这条链路完整走一遍。
2. 从压缩包到训练集:目录结构、标签格式与 YOLO 数据规范
拿到一个 zip 包,第一件事不是急着解压看图片,而是先搞清楚它的组织方式。这份数据集的文件名是扁平的,没有按 train/val 分目录,标签大概率是 YOLO 格式的 txt 文件,和图片同名同目录。如果你直接拿这种结构去喂 YOLOv8 或 YOLOv5,训练脚本会在找标签那一步就报错。所以这一章的核心任务是:把原始结构转成框架能吃的标准格式,同时搞清楚标签里到底存了什么。
2.1 解压后先做三件事:文件清点、图片校验、标签配对
解压之后别急着写训练脚本,先跑一段清点代码。我一般会检查三件事:图片能不能正常打开、标签文件是不是和图片一一对应、标签内容是不是合法的 YOLO 格式(每行 5 个值:类别 id、中心 x、中心 y、宽、高,全部归一化到 0~1)。
import os from PIL import Image img_dir = "insulator_dataset/images" lbl_dir = "insulator_dataset/labels" img_files = sorted([f for f in os.listdir(img_dir) if f.endswith(".jpg")]) lbl_files = sorted([f for f in os.listdir(lbl_dir) if f.endswith(".txt")]) print(f"图片数量: {len(img_files)}, 标签数量: {len(lbl_files)}") # 检查配对 img_stems = {os.path.splitext(f)[0] for f in img_files} lbl_stems = {os.path.splitext(f)[0] for f in lbl_files} missing_lbl = img_stems - lbl_stems missing_img = lbl_stems - img_stems print(f"缺标签的图片: {len(missing_lbl)}, 缺图片的标签: {len(missing_img)}") # 校验图片可读性 bad_imgs = [] for f in img_files: try: with Image.open(os.path.join(img_dir, f)) as im: im.verify() except Exception as e: bad_imgs.append((f, str(e))) print(f"损坏图片: {len(bad_imgs)}") # 校验标签格式 bad_labels = [] for f in lbl_files: with open(os.path.join(lbl_dir, f)) as fh: for line_no, line in enumerate(fh, 1): parts = line.strip().split() if len(parts) != 5: bad_labels.append((f, line_no, "字段数不对")) continue cls, cx, cy, w, h = parts vals = [float(cx), float(cy), float(w), float(h)] if any(v < 0 or v > 1 for v in vals): bad_labels.append((f, line_no, "坐标越界")) print(f"异常标签行: {len(bad_labels)}")这段代码的逻辑很直白:先确认图片和标签数量是否一致,再逐张验证图片文件完整性,最后逐行检查标签的字段数和归一化范围。参数方面,img_dir和lbl_dir按你实际解压后的路径改,如果你的标签和图片混在同一个目录,把两个路径都指向同一处即可。跑完如果missing_lbl不为零,说明有图片没标;如果bad_labels有输出,那些行在训练时会被框架跳过或报错,最好提前修掉。
2.2 划分 train/val 并生成 data.yaml:别让验证集变成「假验证」
314 张图如果全拿去训练,你连模型有没有过拟合都看不出来。常见做法是按 8:2 或 7:3 划分训练集和验证集,但这里有个坑:如果同一基塔、同一光照条件下拍的多张图被随机分到两边,验证集里的样本和训练集高度相似,指标会虚高。我一般会按文件名前缀做分组划分,比如 img_0723 开头的归一组,img_0962 开头的归另一组,尽量让验证集覆盖不同的拍摄批次。
import os import random import shutil from pathlib import Path random.seed(42) src_img = Path("insulator_dataset/images") src_lbl = Path("insulator_dataset/labels") dst_root = Path("dataset_split") # 按文件名前缀分组 groups = {} for f in sorted(os.listdir(src_img)): if not f.endswith(".jpg"): continue prefix = f.split("_")[1] # img_0723_xx -> 0723 groups.setdefault(prefix, []).append(f) # 每组按 8:2 划分 train_files, val_files = [], [] for prefix, files in groups.items(): random.shuffle(files) split = int(len(files) * 0.8) train_files.extend(files[:split]) val_files.extend(files[split:]) for split_name, files in [("train", train_files), ("val", val_files)]: (dst_root / "images" / split_name).mkdir(parents=True, exist_ok=True) (dst_root / "labels" / split_name).mkdir(parents=True, exist_ok=True) for f in files: shutil.copy(src_img / f, dst_root / "images" / split_name / f) lbl = os.path.splitext(f)[0] + ".txt" if (src_lbl / lbl).exists(): shutil.copy(src_lbl / lbl, dst_root / "labels" / split_name / lbl) print(f"训练集: {len(train_files)}, 验证集: {len(val_files)}")这段脚本的关键在prefix = f.split("_")[1]这一行,它把文件名里的批次号抽出来做分组依据。random.seed(42)保证每次划分结果一致,方便复现。划分完成后,目录结构是dataset_split/images/train、dataset_split/labels/train这种标准布局,YOLOv8 直接认。
接下来生成 data.yaml:
path: ./dataset_split train: images/train val: images/val names: 0: nest 1: insulator 2: tower这里names的类别名和顺序必须和标签里的类别 id 严格对应。如果你不确定原始标签里 0、1、2 分别代表什么,用下面这行命令统计一下每个类别出现的次数,再结合文件名和图像内容判断:
awk '{print $1}' dataset_split/labels/train/*.txt | sort | uniq -c | sort -rn提示:如果统计出来某个类别只有个位数样本,训练时该类别的召回率会很难看,考虑做数据增强或者合并类别。
3. YOLOv8 训练配置:从环境搭建到第一轮收敛
数据准备好了,接下来是训练。YOLOv8 是目前小样本目标检测里上手最快的选择,ultralytics 库把训练、验证、导出串成了一条命令。但「能跑」和「跑出可用结果」之间隔着几个关键参数,尤其是学习率、批次大小和输入尺寸这三项,在 314 张图这种小数据集上特别敏感。
3.1 环境搭建:conda 建环境 + ultralytics 安装
我一般用 conda 建一个干净环境,避免和系统里的 torch 版本打架。YOLOv8 对 PyTorch 版本有要求,截至我写这篇笔记时,ultralytics 最新版需要 torch>=1.8,CUDA 版本按你显卡驱动来选。
conda create -n yolo_insulator python=3.10 -y conda activate yolo_insulator # 按你的 CUDA 版本装 torch,这里以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 装 ultralytics pip install ultralytics # 验证 yolo checksyolo checks会输出当前环境、GPU 可用性、依赖版本等信息。如果 GPU 那一栏显示 not available,先检查驱动和 CUDA 版本是否匹配,别急着往下跑训练,否则会用 CPU 硬扛,314 张图虽然不多,但 CPU 训练一轮也要好几分钟。
3.2 训练命令与关键参数:小数据集怎么设才不翻车
环境好了,训练命令本身不复杂:
yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ patience=20 \ project=insulator_runs \ name=exp1逐项说参数。model=yolov8n.pt选的是 nano 版本,参数量最小,适合小数据集快速验证;如果你后面要部署到边缘设备,nano 也是首选。epochs=100配合patience=20,意思是如果 20 轮验证指标没提升就提前停,避免过拟合。imgsz=640是默认输入尺寸,314 张图里如果绝缘子目标本身很小,可以试 1280,但显存占用会翻倍。batch=16在 8GB 显存上跑 640 尺寸基本稳,如果 OOM 就降到 8。lr0=0.01是初始学习率,小数据集上我一般会降到 0.005 甚至 0.001,因为样本少、梯度噪声大,学习率太高容易在损失面上跳来跳去。
训练开始后,终端会输出每一轮的 box_loss、cls_loss、dfl_loss 和 mAP50。box_loss 管的是边界框回归,cls_loss 管分类,dfl_loss 是分布焦点损失,YOLOv8 用来细化边界。前 10 轮 loss 下降快是正常的,如果 30 轮之后 mAP50 还在 0.1 以下,大概率是标签有问题或者学习率太大。
3.3 训练过程监控与指标解读:mAP50 到多少算能用
训练跑起来之后,insulator_runs/exp1/目录下会生成results.csv和若干曲线图。我习惯用 pandas 快速看一眼关键指标的变化趋势:
import pandas as pd df = pd.read_csv("insulator_runs/exp1/results.csv") df.columns = df.columns.str.strip() # 看最后 10 轮的指标 print(df[["epoch", "train/box_loss", "train/cls_loss", "metrics/mAP50(B)", "metrics/mAP50-95(B)"]].tail(10)) # 找 mAP50 最高的那一轮 best = df.loc[df["metrics/mAP50(B)"].idxmax()] print(f"最佳 epoch: {int(best['epoch'])}, mAP50: {best['metrics/mAP50(B)']:.4f}")mAP50是 IoU 阈值 0.5 时的平均精度,mAP50-95是 IoU 从 0.5 到 0.95 每隔 0.05 取一次的平均值,后者更严格。314 张图、3 个类别的场景下,mAP50 能到 0.6~0.75 就算正常发挥,mAP50-95 通常在 0.35~0.5 之间。如果 mAP50 高但 mAP50-95 很低,说明框的位置不够准,可以考虑加更多标注数据或者调 anchor(YOLOv8 是无锚框的,但可以通过增加 DFL 的回归范围来改善)。
注意:验证集指标好看不代表实际巡检场景能用。如果验证集里全是晴天顺光图,模型到了阴天逆光环境大概率翻车。有条件的话,留几张完全不同光照条件的图做最终测试。
4. 推理、验证与边缘部署前的检查清单
训练完拿到 best.pt 只是第一步,真正要落地还得过推理验证和部署适配两关。这一章讲怎么用训练好的模型跑单张图和批量图,怎么调置信度门限,以及导出到边缘设备前必须确认的几个参数。
4.1 单张与批量推理:置信度门限怎么调
YOLOv8 的推理接口很直接:
from ultralytics import YOLO model = YOLO("insulator_runs/exp1/weights/best.pt") # 单张推理 results = model.predict( source="test_images/img_0962_92.jpg", conf=0.25, iou=0.45, imgsz=640, save=True, project="inference_out", name="single" ) # 批量推理整个目录 results = model.predict( source="test_images/", conf=0.25, iou=0.45, imgsz=640, save=True, project="inference_out", name="batch" )conf=0.25是置信度门限,低于这个值的检测框会被丢掉。绝缘子缺陷检测里这个值很关键:调高到 0.5 以上,漏检会明显增加,尤其是小目标缺陷;调到 0.1 以下,误检会爆炸,正常绝缘子上的纹理可能被当成裂纹。我一般先在验证集上跑一组 conf 从 0.1 到 0.6 的对比,看 F1 曲线峰值在哪,再定最终值。iou=0.45是 NMS 的 IoU 阈值,控制重叠框的合并程度,密集目标场景可以适当调低到 0.3~0.4。
4.2 导出 ONNX 与边缘设备适配:输入尺寸和量化怎么选
如果你要把模型部署到 RK3588、树莓派或者 Atlas 这类边缘设备,通常需要先导出成 ONNX 格式:
yolo export \ model=insulator_runs/exp1/weights/best.pt \ format=onnx \ imgsz=640 \ opset=12 \ simplify=Trueopset=12是 ONNX 算子集版本,RK3588 的 NPU 工具链对 12 支持比较好。simplify=True会做一层图优化,去掉冗余算子。导出后你会得到一个.onnx文件,输入尺寸是 1x3x640x640。如果边缘设备算力有限,可以把imgsz降到 416 或 320,但小目标检测精度会掉,需要重新评估。
导出前建议做一次「一致性检查」:用同一张图分别跑 PyTorch 模型和 ONNX 模型,对比检测框数量和类别是否一致。如果 ONNX 输出和原模型差异大,大概率是 opset 版本或者算子不支持的问题。
import onnxruntime as ort import numpy as np from PIL import Image # 预处理 img = Image.open("test_images/img_0962_92.jpg").resize((640, 640)) input_data = np.array(img).transpose(2, 0, 1)[None].astype(np.float32) / 255.0 sess = ort.InferenceSession("best.onnx") outputs = sess.run(None, {sess.get_inputs()[0].name: input_data}) print(f"ONNX 输出形状: {[o.shape for o in outputs]}")这段代码只验证 ONNX 模型能不能正常推理、输出形状对不对。如果输出形状和预期不符,检查导出时的imgsz和opset参数。
提示:边缘部署前,务必在目标设备上跑一遍完整推理,别只在 PC 上验证 ONNX。NPU 的算子实现和 CUDA 有差异,PC 上正常的模型到了板子上可能出各种玄学问题。
5. 避坑与排查:314 张图训练时最容易翻车的五个地方
小数据集训练目标检测模型,翻车点比大数据集更集中。下面这五条是我自己在类似规模数据集上踩过的,按「现象 → 原因 → 解决」整理。
现象一:训练 loss 正常下降,但 mAP50 始终在 0.05 以下。原因:标签类别 id 和 data.yaml 里的 names 顺序对不上,模型学的是错位的分类映射。 解决:用awk '{print $1}' labels/*.txt | sort | uniq -c统计标签里的类别 id 分布,和 data.yaml 的 names 逐项核对。如果标签里只有 0 和 1,但 names 写了三个类,把多余的删掉。
现象二:验证集 mAP 很高,但拿新图推理时大量漏检。原因:训练集和验证集来自同一批次、同一光照条件,模型过拟合到了特定场景。 解决:按文件名前缀分组划分 train/val,确保验证集覆盖不同拍摄批次。如果数据里只有一种光照条件,做亮度、对比度、旋转增强来扩充。
现象三:训练到一半突然 OOM,或者 loss 变成 NaN。原因:学习率太大或者批次太大,梯度爆炸。 解决:把lr0从 0.01 降到 0.001,batch从 16 降到 8,加amp=True(YOLOv8 默认开启混合精度)。如果还 NaN,检查标签里有没有坐标值超出 0~1 范围的行。
现象四:推理时同一目标出现多个重叠框。原因:NMS 的iou阈值设太高,重叠框没被合并掉。 解决:把iou从默认 0.7 降到 0.45 甚至 0.3,观察框数量变化。如果降了之后目标被误合并,说明模型对相邻目标的区分能力不够,需要更多训练数据。
现象五:导出 ONNX 后推理结果和 PyTorch 不一致。原因:opset 版本不兼容或者simplify改变了计算图。 解决:先试opset=12且simplify=False,确认一致后再逐步开 simplify。如果仍然不一致,用 onnxruntime 逐层对比输出,定位是哪一层开始出现偏差。
6. 小样本下的数据增强与模型微调:把 314 张用出 1000 张的效果
314 张图训练 YOLO,最大的瓶颈不是模型结构,是样本多样性不够。我后来养成一个习惯:拿到任何小数据集,先跑一轮 baseline,看 mAP50 和各类别召回率,然后针对弱类别做定向增强,再跑第二轮对比。这个流程比盲目调参有效得多。
YOLOv8 内置了增强参数,可以在训练命令里直接开:
yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.005 \ hsv_h=0.015 \ hsv_s=0.7 \ hsv_v=0.4 \ degrees=10.0 \ translate=0.1 \ scale=0.5 \ fliplr=0.5 \ mosaic=1.0 \ mixup=0.1 \ project=insulator_runs \ name=exp2_aughsv_h、hsv_s、hsv_v控制色调、饱和度、亮度的随机扰动,绝缘子在不同光照下颜色差异大,这三个值可以适当开大。degrees=10.0做小角度旋转,translate=0.1和scale=0.5做平移和缩放,模拟不同拍摄距离。fliplr=0.5是水平翻转概率,绝缘子通常左右对称,翻转不会改变缺陷语义。mosaic=1.0是 YOLOv8 默认开启的马赛克增强,把四张图拼成一张,对小数据集特别有用,能显著增加目标出现的上下文多样性。mixup=0.1做图像混合,进一步扩充样本。
但增强不是越多越好。我做过一组对比:在 314 张图上,mosaic=1.0 + mixup=0.1 的组合比纯 mosaic 的 mAP50 高了约 3 个百分点,但 mixup 开到 0.3 之后反而掉了,因为混合后的图像语义太模糊,模型学不到清晰的特征。所以我的建议是:先开 mosaic,跑一轮看指标;如果弱类别召回率低,再加 mixup 和 HSV 扰动;每次只改一到两个参数,记录对比结果。
另一个容易被忽略的点是「冻结层微调」。如果你用预训练的 yolov8n.pt 做起点,前几轮可以冻结 backbone,只训练检测头,让模型先适应绝缘子数据的分布,再解冻全量微调。YOLOv8 里通过freeze参数控制:
# 前 20 轮冻结 backbone yolo detect train data=./data.yaml model=yolov8n.pt epochs=20 freeze=10 lr0=0.001 # 再解冻全量训练 yolo detect train data=./data.yaml model=insulator_runs/exp2_aug/weights/last.pt epochs=100 lr0=0.005freeze=10表示冻结前 10 层,具体层数按模型结构调。这个策略在小数据集上能防止 backbone 被少量样本带偏,尤其是当你用的预训练权重是在 COCO 这种通用数据集上训的时候。
最后说一个验证技巧:训练完成后,别只看 mAP 数字,把验证集里模型预测错的图挑出来,一张一张看。我一般会跑一段脚本,把 conf 低于 0.3 的检测框和漏检的 ground truth 画在一起,肉眼判断是标注问题还是模型问题。十次里有三次能发现标注框画偏了或者类别标错了,这种问题不修,调再多参数也是白搭。
从那以后我每次拿到新的小样本数据集,都强制走一遍「清点 → 分组划分 → baseline → 定向增强 → 错误可视化」这个流程,不再一上来就堆 epochs 和调学习率。希望帮到你。
本文还有配套的精品资源,点击获取