ARTICLE DETAIL

资讯详情

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

绝缘子缺陷目标检测数据集:VOC XML转YOLO格式全流程指南

绝缘子缺陷目标检测数据集:VOC XML转YOLO格式全流程指南 简介面向配电网输电线绝缘子缺陷检测的目标检测数据集专为电力行业缺陷识别、高校课程作业、毕业设计与算法竞赛场景打造。数据包含1240张无人机空中拍摄的线路巡检图片标签类别为“defect缺陷”图像覆盖不同线路走向、地形地貌、光照条件与拍摄距离分布均匀且重复度低能有效提升模型在真实巡检环境中的泛化性能。压缩包共3721个文件内含1240张JPG原图、1240个XML标注和1241个TXT标注分别对应VOC与YOLO两种主流格式XML保留目标框类别及坐标信息TXT为归一化后的YOLO格式均可直接接入YOLO系列、Faster R-CNN、SSD等检测框架包体约299.8MB用户可按需划分训练集与测试集后直接训练。所有标注均为人工框选目标定位精准、边界贴合缺陷区域对应算法拟合表现较好可大幅节省标注与格式转换时间。目前已有318人浏览学习适合需要高质量绝缘子缺陷数据以验证算法、开展配电网智能巡检项目的开发者与研究者。1. 配电网输电线绝缘子缺陷数据集1240张双格式标注图能帮你少走多少弯路“配电网输电线绝缘子缺陷1240张-含voc(XML)格式yolo(txt)格式标签.zip”这个标题说白了就是一个带完整标注的电力巡检缺陷数据集1240张现场拍摄的输电线绝缘子图像每张都同时提供VOC的XML标注和YOLO的txt标注。拿到手你不需要再爬塔拍照、不用再拿标注工具框缺陷解压之后直接就能进入目标检测流程。对三类人尤其有用一是刚接手电力视觉项目、急需真实缺陷样本的算法工程师二是做绝缘子缺陷检测毕业设计、不想从零标数据的学生三是想快速验证YOLO系列模型在电力场景下效果的技术选型者。这篇笔记就按“从XML结构→转YOLO格式→训练配置→避坑→验证”的顺序把这个数据集的落地路径完整拆开。2. 先从VOC的XML看标注真相字段结构、解析脚本与脏数据排查Pascal VOC格式的XML标注是目标检测数据集里最老牌也最直观的一种标注载体。它没有把标注信息塞进二进制文件而是用一棵清晰的XML树把图片名、图片尺寸、每个目标物体的类别和坐标都摊开写出来。绝缘子缺陷数据集用这种格式保存好处是你可以直接用文本编辑器打开XML看一眼标注内容坏处是字段一多解析时很容易遗漏或读错。我处理这类电力数据集时第一步从来不是急着转YOLO而是先写一个脚本把所有XML读一遍搞清楚里面到底有哪些缺陷类别、每个类别多少个框、坐标有没有超出图像边界。2.1 一个绝缘子缺陷XML里到底有什么folder、size、object与bndbox逐字段拆拿其中一张图举例XML的大致骨架是annotation folderJPEGImages/folder filenameimg_0042.jpg/filename pathD:/dataset/JPEGImages/img_0042.jpg/path source databaseUnknown/database /source size width1920/width height1080/height depth3/depth /size segmented0/segmented object nameinsulator_break/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin512/xmin ymin301/ymin xmax589/xmax ymax372/ymax /bndbox /object /annotation这段XML里真正训练时用得到的字段是四个filename对应图像文件名size里的width和height决定后续坐标归一化时的分母object下的name是这张图里某个缺陷的类别名bndbox里的xmin、ymin、xmax、ymax是这个缺陷框在原始像素坐标系下的位置。depth一般固定是3代表RGB三通道difficult标记样本是否难分YOLO默认不读它但转换时要决定是保留还是丢弃。注意path字段是标注机器生成时的绝对路径换机器后往往失效别用它来做路径拼接。如果这个绝缘子缺陷数据集的类别不止一种比如既有“破损/自爆”又有“污秽”那么同一张图里就会出现多个object节点每个节点记录一个框。这也就是说统计类别分布时不能用“有没有这个XML文件”来判断而得遍历所有object节点的name字段去计数。这也是新手最容易忽略的一点一张图可能同时存在正常绝缘子和缺陷绝缘子两个框类别不同但它们都在同一个XML文件里。2.2 用Python批量解析XML标注最小脚本与字段缺失判断解析这种XML我推荐用Python标准库xml.etree.ElementTree不需要额外装依赖。下面这个脚本是我拿到新数据集后的必跑项功能是遍历Annotations目录把每张图的文件名、类别、坐标全部打印出来并统计类别数量import os import xml.etree.ElementTree as ET xml_dir Annotations class_counter {} total_boxes 0 for xml_name in sorted(os.listdir(xml_dir)): if not xml_name.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_name)) root tree.getroot() filename root.findtext(filename) size root.find(size) width int(size.findtext(width)) height int(size.findtext(height)) for obj in root.iter(object): name obj.findtext(name) box obj.find(bndbox) xmin int(float(box.findtext(xmin))) ymin int(float(box.findtext(ymin))) xmax int(float(box.findtext(xmax))) ymax int(float(box.findtext(ymax))) # 合法性校验框必须朝右朝下且不能超出图像范围 if xmin xmax or ymin ymax: print(f[error] {filename}: invalid box {xmin},{ymin},{xmax},{ymax}) continue if xmax width or ymax height or xmin 0 or ymin 0: print(f[warn] {filename}: box out of image) class_counter[name] class_counter.get(name, 0) 1 total_boxes 1 print(class distribution:, class_counter) print(total boxes:, total_boxes)这里的findtext是ElementTree里比较省事的取文本方法比find().text更稳遇到缺字段时不会抛异常而是返回None。int(float(...))这个写法是为了兼容XML里出现“512.0”这种带小数的坐标值。校验逻辑里xmin xmax说明这个框的左右顶点写反了应该报error然后继续跑而不是中断整个脚本坐标超出图像尺寸的框在训练时会被YOLO自动裁剪但会引入定位偏差所以打warn出来提醒。跑完这个脚本你就知道这个数据集的类别分布、框数量和质量状况这决定了后面的类别映射表怎么定。2.3 解析XML时常见的数据脏点错位坐标与漏标的object解析阶段我实际踩过三个坑写出来给你当参考。第一个坑是xmin和ymin被填反有的标注工具早期版本会把矩形框的左上角和右下角用两个point节点表达转成XML时一旦顺序写反就会出现xmin比xmax还大的情况。上面脚本里的校验就是为了抓这种问题一旦发现别手工改XML直接重新生成标注更省事。第二个坑是object里的name字段存在中英文混写比如同一批数据里“insulator_break”和“破损”同时出现。这在转换阶段会造成类别ID错乱所以在解析阶段就把name统一映射成标准英文类别名。第三个坑是漏标的“空图”有些图片确实没有缺陷XML里连object节点都没有只有image size。这类图在转换时要么生成一个空的txt文件要么干脆剔除出数据集。如果你直接把这样的XML交给转换脚本而不做处理最后会出现标签文件比图片少的情况训练时YOLO会报找不到标签的警告。上述三个点看似不起眼但都会在后面的转换或训练阶段放大成比较难排查的问题所以我习惯在解析阶段就把它们全部拦下来。3. 把VOC的XML转成YOLO能直接训练的txt标签归一化原理、转换脚本与边界坑VOC的XML标注和YOLO的txt标注本质是同一个信息的两种不同坐标系表达。VOC存的是绝对像素坐标xmin和xmax的单位是像素YOLO存的是相对于图片宽高的归一化值并且用中心点坐标加框宽高来表达而不是左上角和右下角。这个差异决定了转换工作不是简单的格式改写而是要做一次坐标系换算。换算公式很固定中心点x坐标为(xminxmax)除以2再除以图片宽度中心点y坐标为(yminymax)除以2再除以图片高度框宽为(xmax-xmin)除以图片宽度框高为(ymax-ymin)除以图片高度。每个值都用浮点数表示通常保留6位小数。3.1 从(xmin, ymin, xmax, ymax)到(cx, cy, w, h)YOLO记住的两个换算关系YOLO的txt标签格式是“class_id cx cy w h”一行一个目标框。class_id是整数从0开始编号cx、cy、w、h都是0到1之间归一化后的浮点数。这里要特别提醒的是cx、cy是矩形框中心点的坐标不是左上角。很多新手在转换时容易把(xminxmax)/2当成cx、忽略再除以width结果就是坐标全部偏小一个量级画出来的框全都挤在图像左上角。我在落地项目里会把这个换算关系表贴在终端旁边原始量含义换算后xminxmax左边界加右边界除以2得到中心点x像素坐标xmax-xmin框的像素宽度直接作为w的分子中心点x像素坐标像素单位除以width得到归一化cx框的像素宽度像素单位除以width得到归一化w这个表看着简单但所有转换脚本的错误九成以上出在这四个除法上。尤其要注意cx和w的分母都是widthcy和h的分母都是height千万别把width和height弄混。YOLO之所以要求归一化是因为模型在训练时会做多尺度缩放输入图片会从原始尺寸resize到640或1280等固定尺寸只有标签也跟随缩放比例做归一化才能保证框的位置和尺寸在resize前后是同一个相对位置。3.2 转换脚本落地类别映射、空标注处理与单文件输出下面给出完整转换脚本可以直接复制到项目里用import os import xml.etree.ElementTree as ET # 类别映射表XML里的name - YOLO的class_id CLASS_MAP { insulator_break: 0, insulator_dirty: 1, insulator_normal: 2, } def convert_one(xml_path, txt_out_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(float(size.findtext(width))) height int(float(size.findtext(height))) lines [] for obj in root.iter(object): name obj.findtext(name) if name not in CLASS_MAP: print(f[skip] unknown class {name} in {xml_path}) continue cls_id CLASS_MAP[name] box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) # 坐标换算成YOLO格式clip到[0,1]防止越界 cx ((xmin xmax) / 2) / width cy ((ymin ymax) / 2) / height w (xmax - xmin) / width h (ymax - ymin) / height cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) # 即使没有object也生成空txt避免YOLO训练时找不到标签 with open(txt_out_path, w) as f: f.write(\n.join(lines) \n) xml_dir Annotations txt_dir labels os.makedirs(txt_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue base os.path.splitext(xml_name)[0] convert_one( os.path.join(xml_dir, xml_name), os.path.join(txt_dir, base .txt) )这里的CLASS_MAP是整个转换的灵魂它决定了XML里的缺陷类别名对应到YOLO里的哪个数字ID。如果你的数据集里含有“正常绝缘子”这个类别也要一起放进映射表否则训练时模型会把正常绝缘子当成背景。clip操作是为了防止坐标计算四舍五入后出现小于0或大于1的边界值这类越界值在YOLO训练中可能触发NaN loss。最后那个“即使没有object也生成空txt”的动作是很多人容易漏掉的细节YOLO在训练时对每张图片都会去找同名txt找不到会打WARNING并且在验证阶段这些图片会被直接忽略影响数据集的有效规模。3.3 转换后的验证用OpenCV把YOLO标签画回去转换完成之后不要急着开训练先做一步可视化验证把txt里的数值画回原图看框的位置是否和真实缺陷位置吻合。我一般用OpenCV写一个几十行的检测脚本随机抽十几张图每张图画出所有框并打印类别名import cv2 CLASS_NAMES [insulator_break, insulator_dirty, insulator_normal] def draw_txt(img_path, txt_path): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) x1 max(0, min(x1, w - 1)) y1 max(0, min(y1, h - 1)) x2 max(0, min(x2, w - 1)) y2 max(0, min(y2, h - 1)) color (0, 0, 255) if cls_id 0 else (0, 255, 0) if cls_id 1 else (255, 0, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, CLASS_NAMES[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return img # 抽样验证前10张 for i in range(10): img_path fimages/img_{i:04d}.jpg txt_path flabels/img_{i:04d}.txt vis draw_txt(img_path, txt_path) cv2.imshow(verify, vis) cv2.waitKey(0) cv2.destroyAllWindows()这段代码的要点在坐标还原部分它把归一化的cx、cy、w、h乘以图像的原始宽高再换算回像素坐标系所以画框前必须拿到图片的真实shape而不是用XML里记录的size。XML的size如果跟实际图片尺寸不一致这一步就能立刻暴露问题。颜色区分只是调试便利类别多的话可以改用类别名到颜色的字典。另外建议你在抽样时避开全黑或过曝的图片这类图在野外巡检数据里占比不低人眼都不易看清缺陷画框验证意义不大。3.4 转换阶段翻车最多的四个边界情况转换脚本看着简单但实际跑数据时边界情况能把脚本逼疯。第一种是多个object嵌套在同一张图里有的XML用两个object并列有的标注工具会生成嵌套的object结构用root.iter(object)能同时兼容这两种情况而findall只能匹配直接的子节点。第二种是类别名带空格比如“insulator break”中间有空格这在txt里会变成两列YOLO解析时直接报错。转换时要把空格替换成下划线同时修改CLASS_MAP里的键名。第三种是图片是灰度图有些电力巡检相机输出的不是三通道XML的depth写1YOLO训练前需要把图片转成RGB三通道转换脚本阶段不处理但会在训练数据加载时报错。第四种是文件名重复来自多个采集批次的数据合在一起时同名图片可能出现在不同文件夹转换脚本会互相覆盖。解决方法是转换前统一重命名用批次前缀加序号组成新文件名。这四个边界情况我在真正项目里都遇到过前两个最容易让新手在论坛里发帖求助。4. 跑通YOLO训练数据划分、data.yaml与必调参数格式转换完成后接下来的标准动作是用YOLO框架把缺陷检测模型训起来。当前主流的做法是用Ultralytics YOLOv8系列它的命令行接口对第一次做电力数据集的人最友好一条命令就能启动训练。但命令简单不代表不用做功课数据划分、data.yaml、超参数这三块没配好训练出来的模型基本就是废的。我在配电网绝缘子缺陷项目上的经验是把70%到80%的时间花在数据准备和参数确认上真正训练反而是最省心的部分。4.1 数据划分train/val/test的文件名一致性与随机种子数据划分的原则是同一张图片的.jpg和.txt必须被划分到同一个集合里且划分后三者的文件名列表严格不重叠。最简单可靠的实现方式是这样import os import random import shutil random.seed(42) # 固定种子保证每次划分结果一致 image_dir images label_dir labels train_ratio, val_ratio 0.8, 0.15 all_imgs [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(all_imgs) n_train int(len(all_imgs) * train_ratio) n_val int(len(all_imgs) * val_ratio) train_imgs all_imgs[:n_train] val_imgs all_imgs[n_train:n_train n_val] test_imgs all_imgs[n_train n_val:] for split, imgs in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: os.makedirs(fdata/{split}, exist_okTrue) for img in imgs: shutil.copy(os.path.join(image_dir, img), fdata/{split}/{img}) txt os.path.splitext(img)[0] .txt if os.path.exists(os.path.join(label_dir, txt)): shutil.copy(os.path.join(label_dir, txt), fdata/{split}/{txt}) else: # 空标签文件也拷贝避免YOLO警告 open(fdata/{split}/{txt}, w).close()这个脚本把原图和数据标签放在同一个子目录是YOLO最省心的目录风格train目录下同时放着jpg和txtdata.yaml只要指向train和val两个目录即可。random.seed(42)这一行很关键它保证了重复运行划分脚本得到相同的结果方便复现训练。测试集比例不显式设置剩余的就是测试集。电力巡检场景里测试集应该优先选取来自不同线路、不同光照条件的图片但该数据集没有明确的拍摄批次信息所以只能随机划分。如果有条件按拍摄日期或线路编号分组划分是更严谨的做法能避免同一批图既出现在训练集又出现在测试集造成评估虚高。4.2 data.yaml怎么配类别名、路径与本地化坑YOLOv8的data.yaml是训练配置的总入口内容极其简单但经常出问题的恰恰是这些简单字段。一个标准的绝缘子缺陷data.yaml长这样train: data/train val: data/val nc: 3 names: 0: insulator_break 1: insulator_dirty 2: insulator_normal这里最容易翻车的点是train和val的路径。Ultralytics YOLOv8会把这个路径直接交给Python的open函数去读如果你的项目路径里有中文或者data.yaml用的是相对路径而你在别的目录下执行训练命令就会报数据集不存在的错误。我在项目里一律用绝对路径并且把图片目录的完整路径写进去。nc的值必须和names列表的长度一致多写一个少写一个都会在训练启动时报错。names的顺序和转换脚本里的CLASS_MAP顺序必须完全相同否则同一个类别在训练时和验证时的ID对不上混淆矩阵会乱掉。4.3 必调参数imgsz、batch、epochs、学习率与yolo环境配置YOLOv8训练命令的必调参数有这么几个imgsz、batch、epochs、lr0。imgsz是训练输入尺寸电力巡检图片多数是1920x1080或更大缺陷框本身尺寸小如果把图片缩到416小缺陷会丢失像素信息所以我至少用640显存够且内存充足时直接上1280。batch大小取决于显卡显存以不爆显存为前提尽量大我常用默认的batch16在V100这类卡上可以开到32。epochs在缺陷检测这种小数据集上我一般从100起步。lr0是初始学习率保持默认的0.01但如果你用的是预训练模型且数据集只有一千多张建议把lr0降到0.001防止迁移过来的权重被过大的学习率冲坏。至于yolo环境配置常见做法是用conda建一个Python 3.10的环境pip安装ultralytics包然后跑yolo命令验证安装是否成功这一步是最容易出环境问题的环节但它的排查方法也很简单用yolo predict跑一张测试图能出结果就说明环境基本通了。4.4 预训练模型选择yolov8n还是yolov8s以及yolo系列对比预训练模型的下载是训练前的一个前置动作。Ultralytics会自动下载yolov8n.pt这类COCO预训练权重。在1240张图这种规模的数据集上我的建议是优先选择yolov8n或yolov8s不要一上来就选yolov8l或yolov8x。理由有两个一是电力缺陷检测通常部署在边缘设备或无人机机载平台上对模型体积和推理速度敏感n和s更适合部署二是小模型在小数据集上不容易过拟合而且训练速度快方便你调参迭代。如果你有其他YOLO系列经验比如YOLOv5或YOLOv7其实也可以。YOLOv8相比v5在电力巡检场景下最大的变化是anchor-free机制不用再手工调anchor尺寸对绝缘子这种长宽比不固定的目标反而更省心。对比来看v8n在当前预训练模型下载便利度和社区支持程度上都是性价比最高的选择。4.5 训练过程的监控loss曲线、验证指标与中断恢复训练启动后我只看三样东西train/box_loss曲线、val/box_loss曲线、以及验证集上的mAP50。训练停止的时机很讲究不要死等epoch跑完。对于一千多张图的小数据集模型一般在50到80个epoch就收敛再往后train_loss还在下降但val_loss开始反弹这说明过拟合已经开始了。YOLO会自动保存best.pt和last.ptlast.pt是最后一个epoch的权重best.pt是验证指标最好的权重最终部署一定用best.pt。如果训练中途因为断电或OOM中断可以带上resumeTrue恢复这是yolo框架自带的后悔药前提是你没有改动数据集和参数配置。5. 避坑排查绝缘子缺陷标签转YOLO翻车的5个真实案例这一章的内容来自我在多个电力巡检数据集上反复踩坑的总结。前面讲原理和步骤时提到的坑这里给出能直接对号入座的排查路径。每条都按我实际遇到的现象、定位到的原因、以及最终的解决办法展开写。5.1 现象一loss正常下降但验证集mAP一直是0这个现象我见过不下三次。训练日志里train_loss曲线很漂亮从2降到0.5但每个epoch结束后验证集mAP50始终是0。原因几乎都出在数据上没有同步划分训练用的txt和验证用的txt混在一起或者转换脚本在生成标签时把类别ID写成了从1开始但模型默认nc类别的ID是0到nc-1。解法是把txt标签的类别ID打印出来确认范围是0到nc-1并且用可视化脚本验证验证集的图片和txt确实一一对应。mAP为0的另一个常见原因是图片是灰度图而模型要求三通道这会在数据加载时报错但有时候YOLO都静默处理了导致训练在一堆坏图上跑完指标自然全是0。5.2 现象二标签txt数量比图片少了几十张训练前检查文件数量发现labels目录下txt文件比images目录下的jpg文件少。这个现象的直接原因是XML里没有object的空标注文件在转换脚本中被continue跳过了。我在转换脚本里特意加了一句“即使没有object也生成空txt”就是为了堵住这个坑。如果你已经转换完了补救的方法是遍历所有图片名给缺失的txt手动创建空文件。另外也要排查XML文件名与图片名是否完全同名有些标注工具导出的XML文件名和原图文件名不一致也会造成同样的问题。这一步看似小事但YOLO训练时对缺失标签的图片会直接忽略间接降低数据集规模对一千多张的缺陷检测数据影响尤其明显。5.3 现象三可视化验证时框全部偏到左上角画框验证时框的尺寸和位置跟真实缺陷完全对不上全部挤在图像的左上角。造成这个现象的原因有两个一是XML里的width和height是缩放后的尺寸而图片本身是原始分辨率二是转换脚本里忘了除以width和height直接把像素坐标当成归一化坐标写进txt。第一个原因更隐蔽因为XML里的尺寸字段通常是标注工具读取当时的图片分辨率如果标注前对图片做过压缩这个尺寸就跟训练时加载的图片不一致。解决方法是转换前统一核对用cv2.imread读取图片实际shape以实际shape而非XML的size做分母。我在转换脚本里会加一段校验代码当XML的size和实际shape不一致时打印warn这样能在转换阶段就发现数据的不一致。5.4 现象四训练到一半loss变成nan然后所有指标失效训练进行到二三十个epochtrain_loss突然变成nan然后指标一路崩掉。常见原因是标签值出现NaN或无穷大来源于坐标换算时某张图像的width或height为0或者某张图本身是损坏的文件读取失败。我在转换脚本里做了clip到[0,1]的处理但clip只能兜底不能解决根本问题。排查时先检查XML里有没有width或height字段为0的标注再检查图片文件是否能被cv2.imread正常解码。还有一种比较少见的触发原因是batch内部有图片尺寸极端差异导致BN层的统计量爆炸出现训练中BN崩溃的现象。解法是把这个batch的imgsz统一或者换用更稳健的归一化方式直接重启训练但更推荐从数据端排查把异常图片剔除干净再训。5.5 现象五混淆矩阵总合不唯一对角线也偏低训练结束打印混淆矩阵发现总合不是100%对角线数值也偏低。这通常是类别不均衡和背景类别处理不当共同导致的现象。1240张图的数据集里如果“绝缘子正常”类别占了80%以上而“破损”只有5%那么即使模型准确率很高混淆矩阵对角线也不会好看。另一个方向是YOLO默认会把背景也计入混淆矩阵特别是在置信度阈值设置得比较低时很多背景框会被判定为某个类别导致总合看起来不唯一。我在这个阶段会分别看每个类别的precision和recall找到“被漏检最多”的类别然后回到数据层面决定是补样本调整类别权重还是提高该类的置信度阈值。混淆矩阵总合不唯一这件事本身不是bug但要结合类别分布去解读才算验收完成。6. 进阶验证技巧用混淆矩阵验收绝缘子缺陷检测模型别只看mAPmAP是一个平均指标它把十个置信度阈值下的结果揉在一起能说明模型整体好不好但说不清楚“哪一类被漏了、哪一类被误检了”。在配电网绝缘子缺陷场景里漏检一个自爆绝缘子的代价远高于误检三个正常绝缘子。所以我迭代到模型候选阶段后不会只看验证集的mAP而是把混淆矩阵拉出来逐类分析漏检和误检。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datadata.yaml, splitval, plotsTrue) print(metrics.confusion_matrix.matrix) # 单独看每个类别的召回率 for i, name in enumerate([insulator_break, insulator_dirty, insulator_normal]): tp metrics.confusion_matrix.matrix[i, i] total metrics.confusion_matrix.matrix[i, :].sum() recall tp / total if total 0 else 0 print(f{name} recall: {recall:.3f})这段代码的原理是混淆矩阵的第i行代表真实类别i的所有样本总和第i列代表模型预测为类别i的样本数对角线[i, i]是正确预测数所以第i行的对角线值除以第i行所有值的和就是该类的recall。我通常把几个类别的recall打印出来后重点关注缺陷类别的recall这个值决定了模型在实际巡检中的漏检率。如果“破损”类别的recall不到0.7我会回到数据层面检查缺陷样本的多样性是否足够比如光照方向、角度、拍摄距离是否覆盖了现场情况而不是急着调超参数。最后说一个我自己的习惯每轮训练结束我会把best.pt、data.yaml、训练日志和混淆矩阵图放在同一个以日期命名的目录下备注本次实验改了哪些参数。翻车不可怕怕的是不知道上一版为什么好、这一版为什么差。做电力巡检模型更是这样缺陷样本本身就难采集每次训练都要留好后悔药。希望帮到你。本文还有配套的精品资源点击获取
返回列表