ARTICLE DETAIL

资讯详情

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

绝缘瓷瓶检测数据集:VOC/COCO/YOLO格式转换与YOLOv8训练实战

绝缘瓷瓶检测数据集:VOC/COCO/YOLO格式转换与YOLOv8训练实战 简介YOLO无人机航拍输电线路绝缘瓷瓶数据集面向计算机视觉与电力巡检方向的开发者、学生及算法工程师提供真实场景下高质量绝缘瓷瓶图像与标注可直接用于YOLO系列目标检测模型训练。压缩包内共1281个文件包含421张jpg原图、421个xmlVOC格式、428个txtYOLO格式标签以及1个jsonCOCO格式与yaml配置文件覆盖三种主流标注规范省去格式转换步骤另附3个python数据集划分脚本和6个html环境搭建、训练教程兼顾Linux与Windows平台。压缩包整体约941.93MB结构清晰便于按需取用。已有334人学习下载配套博文还展示了数据集详情适合需要快速获取电力巡线标注数据并入门YOLO训练的实践者。1. 绝缘瓷瓶检测数据集为什么说三种标签格式和划分脚本才是重点第一次用 YOLO 跑无人机航拍输电线路绝缘瓷瓶检测的时候我卡在最无聊的一步标注格式。手里是 VOC 的 XML模型要 YOLO 的 TXT中途还想着给 COCO 留一份三个格式之间的坐标换算和类别编号对齐全是运行时才暴露的坑。这份数据集一次性把三种标签给齐再配上划分脚本和训练教程相当于把「准备数据→划分样本→跑通训练」这条链路直接打通。适合正在做电力巡检、输电线路缺陷检测的工程师也适合准备拿 YOLO 练手的入门者。这篇文章就按格式、划分、训练、踩坑四个部分往下讲每个环节都会配上可以直接复现的命令。2. 读懂 VOC、COCO 和 YOLO 三种格式坐标系、存储结构和转换边界2.1 XML、JSON 和 TXT三种格式各自怎么描述一个瓷瓶VOC 格式是最「人类可读」的。每张图片对应一个同名 XML 文件根节点annotation下面filename写图片名size里记录 width、height 和 depth然后每个目标是一个object节点里面的name是类别名bndbox给四个值xmin、ymin、xmax、ymax都是像素坐标表示框的左上角和右下角。如果你用 labelImg 标注过导出 VOC 格式就是这套结构——代码里用 ElementTree 读 XML拿到object列表就拿到全部检测目标。COCO 格式则是一个汇总的 JSON 文件把所有图片和所有标注集中管理。结构分两层images数组里每个元素记录图片的id、file_name、width、heightannotations数组里每个元素记录这条框属于哪张图image_id、类别编号category_id、框坐标bbox——注意这里的bbox是[x, y, width, height]是左上角坐标加宽高不是两个点。另外还有area和segmentation字段segmentation在实例分割里才有值检测任务里通常只是边框对应的多边形。YOLO 格式最简单也最容易出错。每张图片对应一个同名 TXT 文件每行代表一个目标class_id、x_center、y_center、width、height五个数空格隔开。其中中心坐标和宽高都是相对图片宽高的归一化小数范围 0 到 1。这种设计的好处是模型前处理时不用管输入图片实际是多大网络输出和标签天然对齐坏处是你肉眼直接看 TXT 根本看不出框在哪一旦转换时坐标算错训练出来的模型会非常「自信地」输出错误位置。三种格式的本质区别可以归纳成三点坐标表达方式两点像素 vs 左上点加宽高像素 vs 中心点加宽高归一化、类别表达方式字符串 vs ID vs ID、文件组织方式一图一文件 vs 全量聚合 JSON vs 一图一文件。你只要把这三条主线理清剩下所有转换都是机械计算。2.2 格式转换的坐标换算与三个常踩的坑VOC 转 YOLO 的换算公式如下分母必须是实际图片的宽高x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / heightVOC 转 COCO 则直接换算为左上角坐标加宽高x xmin y ymin box_w xmax - xmin box_h ymax - yminCOCO 转 YOLO 时要把像素的左上角坐标和宽高换算成归一化的中心点x_center (x box_w / 2) / width y_center (y box_h / 2) / height box_w box_w / width box_h box_h / height公式本身不难难的是转换过程里的三个隐蔽坑。第一个坑是分辨率不一致。VOC 的 XML 里size字段标了图片宽高但实际图片可能被预处理缩放、旋转而 XML 没有跟着更新。归一化时如果直接拿 XML 里的size当分母而实际图片尺寸已经变了坐标整体偏移。我在这类数据上见过偏移超过 20 个像素的情况模型照样能训但框永远贴不紧瓷瓶边缘。正确做法是转换前用 Pillow 或 OpenCV 读一遍实际图片宽高。第二个坑是category_id不等于class_id。COCO 的categories列表里id可以是从 1 开始的任意整数而 YOLO 要求class_id从 0 开始连续递增。如果转换时不建映射表直接把category_id当class_id训练时所有类别的标签整体错一位。这种错误最阴险的地方在于 loss 曲线看起来完全正常只是训练出来所有预测框的类别都是错的。第三个坑是 VOC 的difficult标志。difficult1表示难以识别的样本转换到 YOLO 格式时一般直接丢弃。如果不做过滤这些困难样本会被当成普通正样本送进训练模型被迫去拟合那些标注本身就不确定的目标精度会掉一截。2.3 拿到数据集后的第一件事验证标签与图片是否对齐把 .rar 解压后的第一件事不是急着写 YAML而是检查三个格式的覆盖度。我一般用一小段 Python 把文件名集合的差集打出来看import os from pathlib import Path root Path(解压后的目录) images {p.stem for p in (root / images).glob(*) if p.suffix.lower() in (.jpg, .jpeg, .png)} voc {p.stem for p in (root / Annotations).glob(*.xml)} yolo {p.stem for p in (root / labels).glob(*.txt)} print(图片数:, len(images)) print(VOC标签数:, len(voc)) print(YOLO标签数:, len(yolo)) print(缺VOC标注的图片:, sorted(images - voc)[:10]) print(缺YOLO标注的图片:, sorted(images - yolo)[:10])这段代码假设目录结构是images/放图片、Annotations/放 VOC 的 XML、labels/放 YOLO 的 TXT。如果两个差值列表不为空说明部分图片没有对应标注。这类图片在训练时要挑出来不能直接留在 images 里否则 YOLO 在数据准备阶段要么报错要么把没标签的图当纯背景处理效果很怪。COCO 不是一图一文件不能按文件名比对但要检查 JSON 的完整性import json with open(annotations/instances.json, r, encodingutf-8) as f: coco json.load(f) print(images:, len(coco[images])) print(annotations:, len(coco[annotations])) print(categories:, coco[categories]) img_ids {img[id] for img in coco[images]} ann_img_ids {ann[image_id] for ann in coco[annotations]} print(有标注但不在images里的image_id:, sorted(ann_img_ids - img_ids)[:10])categories打印出来就是类别 ID 和类别名的映射后面写 YOLO 的data.yaml时直接参考。如果有标注但找不到对应图片记录说明 JSON 和实际图片不一致划分前要先修掉。我还习惯随机抽三张图做可视化把标签框直接画在原图上——这一步能发现坐标偏移、类别标反、框超出图片边界这类「数据本身没错但转出来错了」的问题。3. 用划分脚本切好训练集随机种子、文件联动与 COCO JSON 重建3.1 划分的三个核心逻辑随机种子、文件联动、COCO 的 JSON 重建划分数据看似简单写个 random 打乱就行但对带三种标签的数据集有讲究。第一随机种子必须固定。不固定种子每次跑出来的 train/val 分配都不一样实验对比就没有意义。你改一行代码重新跑val 集变了模型精度的变化就分不清是代码改进带来的还是数据划分变了带来的。所以脚本里固定random.seed(42)42 换成什么都行关键是固定下来。同样的种子和同样的数据跑出来的划分必须完全一致这是可复现实验的底线。第二划分要以图片为单位做文件联动。VOC 和 YOLO 的标签都是「一图一文件」切分时要把图片和对应的 XML、TXT 一起移动或复制不能只移图片。常见的错误是只移动了图片但 labels 目录里还留着全部 1000 个 txt训练时 YOLO 按图片名去找同名 txt此时目录对不上轻则空标签重则训练集和验证集混进了本不该出现的样本指标全部失真。第三COCO 格式没有独立文件全量标注都在一个 JSON 里。划分时要先把 train/val/test 各自的图片 id 筛出来再按 id 过滤annotations重新生成三个子集的 JSON。手工做这一步不现实必须脚本化。重建时categories字段要完整保留否则后面做可视化时类别名全部丢失只剩数字编号。3.2 划分脚本一次搞定三种格式假设 .rar 解压后目录结构是这样的数据根目录/ ├── images/ # 全部图片 jpg/png ├── Annotations/ # VOC 的 xml与图片同名 ├── labels/ # YOLO 的 txt与图片同名 └── annotations/ └── instances.json # COCO 全量标注下面这个脚本我一直在用覆盖图片、VOC 标签、YOLO 标签的同步复制以及 COCO JSON 按子集重建 数据划分脚本按图片名把三种标签同步切到 train/val/test 用法: python split_dataset.py --source 数据根目录 --output ./dataset \ --ratio 0.8 0.1 0.1 --seed 42 import os import random import shutil import argparse import json from pathlib import Path def parse_args(): parser argparse.ArgumentParser(description划分VOC/COCO/YOLO三种格式数据集) parser.add_argument(--source, typestr, requiredTrue, help解压后的数据根目录) parser.add_argument(--output, typestr, default./dataset, help输出目录) parser.add_argument(--ratio, typefloat, nargs3, default[0.8, 0.1, 0.1], helptrain/val/test比例按 train, val, test 顺序给) parser.add_argument(--seed, typeint, default42, help随机种子) return parser.parse_args() def split_list(items, ratios, seed): random.seed(seed) items sorted(items) random.shuffle(items) n len(items) n_train int(n * ratios[0]) n_val int(n * ratios[1]) return items[:n_train], items[n_train:n_train n_val], items[n_train n_val:] def rebuild_coco(coco_file, images, split_name, out_dir): 按图片文件列表重建COCO子集JSONimages 是文件名列表 with open(coco_file, r, encodingutf-8) as f: coco json.load(f) keep_ids set() split_images [] for img in coco[images]: if img[file_name] in images: split_images.append(img) keep_ids.add(img[id]) split_anns [a for a in coco[annotations] if a[image_id] in keep_ids] sub { info: coco.get(info, {}), licenses: coco.get(licenses, []), categories: coco[categories], images: split_images, annotations: split_anns, } sub_dir out_dir / split_name / annotations sub_dir.mkdir(parentsTrue, exist_okTrue) with open(sub_dir / finstances_{split_name}.json, w, encodingutf-8) as f: json.dump(sub, f, ensure_asciiFalse, indent1) def main(): args parse_args() src Path(args.source) out Path(args.output) img_dir src / images voc_dir src / Annotations yolo_dir src / labels coco_file src / annotations / instances.json all_images [p.name for p in img_dir.glob(*) if p.suffix.lower() in (.jpg, .jpeg, .png)] print(f共 {len(all_images)} 张图片) train_imgs, val_imgs, test_imgs split_list(all_images, args.ratio, args.seed) print(ftrain{len(train_imgs)} val{len(val_imgs)} test{len(test_imgs)}) for split_name, imgs in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: for img_name in imgs: dst_img_dir out / split_name / images dst_img_dir.mkdir(parentsTrue, exist_okTrue) shutil.copy2(img_dir / img_name, dst_img_dir / img_name) xml_name Path(img_name).stem .xml if (voc_dir / xml_name).exists(): dst_voc_dir out / split_name / Annotations dst_voc_dir.mkdir(parentsTrue, exist_okTrue) shutil.copy2(voc_dir / xml_name, dst_voc_dir / xml_name) txt_name Path(img_name).stem .txt if (yolo_dir / txt_name).exists(): dst_yolo_dir out / split_name / labels dst_yolo_dir.mkdir(parentsTrue, exist_okTrue) shutil.copy2(yolo_dir / txt_name, dst_yolo_dir / txt_name) if coco_file.exists(): for split_name, imgs in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: rebuild_coco(coco_file, imgs, split_name, out) for split_name, imgs in [(train, train_imgs), (val, val_imgs), (test, test_imgs)]: print(f{split_name}: {len(imgs)} 张图) if __name__ __main__: main()这个脚本的逻辑分四步走第 1 步收集images/目录下全部图片文件名第 2 步固定随机种子后打乱切分第 3 步对每个子集按图片名去Annotations/和labels/里查询同名文件并复制过去第 4 步对 COCO 的 JSON 按image_id筛选重建三个子集。用copy2而不是move是为了保留原始数据——划分之后如果发现比例不合适重新跑一次就行不用重新解压原文件相当于给数据划分留了一份后悔药。脚本对无标签的图片不会报错但最后汇总打印时你会看到数量差异这时需要回头处理源数据。参数方面--source指向解压出的根目录脚本自动认images、Annotations、labels、annotations/instances.json这套固定结构--ratio按 train、val、test 顺序给三个数数加起来不用等于 1脚本按比例切--seed默认 42复现实验时用同一个 seed 跑出来的划分完全一致。几千张以内的数据量这个脚本几十秒就结束慢的不是脚本而是后面训练。3.3 划分后的三个自检命令划分完不能直接开训先做三个检查。第一个检查数量比例确认子集划分符合预期for d in train val test; do echo $d images: $(ls dataset/$d/images | wc -l) echo $d yolo_labels: $(ls dataset/$d/labels | wc -l) echo $d voc_labels: $(ls dataset/$d/Annotations | wc -l) done如果某个子集的图片数和标签数对不上说明源数据里有图无标或有标无图的脏样本先解决再训练。第二个检查 COCO 子集的完整性import json for split in [train, val, test]: p fdataset/{split}/annotations/instances_{split}.json with open(p, encodingutf-8) as f: data json.load(f) print(split, images:, len(data[images]), anns:, len(data[annotations]))重点看有没有「有 annotation 但没有对应 image」的悬挂记录。这类记录会导致 COCO 数据加载器在运行时报 KeyError而且报错位置在框架内部排错很费劲。第三个检查类别分布用 YOLO 的 txt 统计每个类的行数for split in train val test; do echo $split cat dataset/$split/labels/*.txt 2/dev/null | awk {print $1} | sort | uniq -c done正常情况三个子集里每个类别的样本占比应该接近如果某个类在 test 里占比特别高、在 train 里几乎没有模型对这个类别基本是瞎的。这个检查用一条命令就能看清类别分布有没有失衡。4. 从环境到出图用 YOLOv8 跑通绝缘瓷瓶训练全流程4.1 环境配置conda 创建与 ultralytics 安装环境配置是很多人第一步就卡住的地方。用 Anaconda 建环境是常见做法命令就三条conda create -n yolo python3.10 -y conda activate yolo pip install ultralyticsPython 版本 3.8 到 3.12 都能跑 ultralytics我习惯用 3.10兼容性最稳。pip install ultralytics会把 torch、torchvision、opencv-python 等一系列依赖自动装上。装完先验证一下环境是否可用python -c import ultralytics; ultralytics.checks()如果你机器有 NVIDIA 显卡再跑一句看 GPU 是否被识别python -c import torch; print(torch.cuda.is_available())输出True说明 CUDA 版 torch 已就位训练可以用 GPU 加速输出False说明装的是 CPU 版 torch。CPU 版不是不能训只是同样的 100 轮GPU 可能两小时跑完CPU 要跑到第二天早上。如果你在 PyCharm 里建项目记得给项目解释器选同一个 conda 环境路径这样终端里能跑的训练命令在 IDE 里也能直接跑。4.2 数据 YAML路径、类别名和 class_id 的对应关系YOLOv8 训练前要先写一个数据配置文件指向前面划分好的目录# 绝缘瓷瓶检测 - 数据配置 path: /绝对路径/dataset # 数据根目录写绝对路径最省事 train: train/images # 相对 path 的训练图片目录 val: val/images # 相对 path 的验证图片目录 test: test/images # 相对 path 的测试图片目录没有可删掉 nc: 2 # 类别数 names: 0: normal_insulator # 正常瓷瓶名字要和 VOC/COCO 里一致 1: damaged_insulator # 破损/自爆瓷瓶path我建议写绝对路径。相对路径在换机器、换工作目录训练时经常出现「directory not found」的报错排查起来玄学感极重。names里的索引必须和 YOLO txt 里的class_id一致也就是上一章用awk统计出来的那个编号。如果你手里的数据集类别名和这个例子不同以解压后labels/里实际的文件内容和 COCO 的categories为准这个名字列表就是训练时标签和 loss 打印的对齐依据。nc可以省略YOLO 会根据names数量自动推断。训练前再确认一次类别编号合法性这批 txt 里class_id的最小值应为 0最大值应为nc-1。如果出现超出范围的值训练会在数据加载阶段直接报错不用等到跑起来才发现。4.3 训练命令与关键参数epochs、batch、imgsz 怎么选数据配置就绪后跑训练只需要一条命令yolo detect train \ datainsulator.yaml \ modelyolov8s.pt \ epochs100 \ batch16 \ imgsz640 \ patience30 \ projectruns \ nameinsulator_exp1modelyolov8s.pt会从官方权重地址自动下载 COCO 预训练权重第一次跑需要联网。如果你只是想快速验证流程通不通换成yolov8n.pt模型更小训练更快如果对精度要求高且显存充足换yolov8m.pt。三个模型大小从 n 到 m 递增最终 mAP 一般也递增但训练时间同步翻倍。epochs100是个合适的起点。跑完看 mAP 曲线如果第 100 轮还在缓慢上涨就继续加大到 150 或 200。batch16在 8G 显存下跑 yolov8s 大约够用中途报 OOM 就改成 8 或 4。拿不准显存占用时训练前看一眼nvidia-smi别等报错了才去改。imgsz640对航拍瓷瓶这类小目标检测优先保持 640。跑通 baseline 之后可以再试一次imgsz1280分辨率翻倍对小目标召回率通常有明显帮助但训练时间会翻几倍显存占用也翻倍。patience30表示验证集精度连续 30 轮没有提升就自动早停这个参数对不想死守完整 100 轮的人来说很省时间。训练过程中的输出重点看三块box_loss、cls_loss、dfl_loss 三条 loss 曲线的整体趋势每轮结束时打印的 precision、recall、mAP50、mAP50-95以及最后的权重文件。训练结束在runs/detect/insulator_exp1/weights/下会生成best.pt和last.ptbest.pt按验证集 mAP 择优保存后面所有推理和部署都用它。训练完后随手跑一次推理把测试图片过一遍yolo detect predict \ modelruns/detect/insulator_exp1/weights/best.pt \ sourcedataset/test/images \ conf0.25 \ save_txtTrue这一步能看到模型在没见过图片上的真实表现框有没有贴住瓷瓶边缘、有没有把杆塔构件当瓷瓶基本一目了然。5. 绝缘瓷瓶训练避坑清单五条真实的翻车记录5.1 图片有标签但训练时总报 no labels found现象data.yaml 的路径都检查过图片数量也能正常打印但训练刚开始就报错日志里提示 no labels found。这个问题在第一次跑这份数据集的人里出现频率最高。原因YOLO 找标签目录有一套默认规则。你用train: train/images指定了图片目录YOLO 就会自动去同级的train/labels找训练标签而不是去train/Annotations或随便什么自定义目录。如果你的标签目录命名或层级和默认规则不一致它就直接判定为没有标签。解决确保train/images的同级存在train/labels且 labels 里的 txt 与 images 里的图片同名。这也正是前面划分脚本把标签复制到split_name/labels而不是别的名字的原因——labels这个目录名是 YOLO 的默认约定不要为了整齐改叫label或labels_new。5.2 三种格式类别 ID 对不上模型把破损瓷瓶当正常瓷瓶现象训练 loss 正常验证集 mAP 也不差但打开预测图发现破损瓷瓶全被标成正常瓷瓶正常瓷瓶偶尔又被标成破损。原因从 VOC 转 COCO 时category_id可能从 1 开始而 YOLO 要求class_id从 0 开始。数据集在生成三种格式时如果某一处转换没有做减一映射类别标签就会集体错位。这种问题在 loss 曲线上看不出任何异常。解决拿一张破损瓷瓶的图片分别查看它的 XML、COCO JSON 里的category_id和 YOLO txt 里的class_id三者核对一遍import xml.etree.ElementTree as ET import json # 以样例文件 insulator_001 为例 xml_tree ET.parse(Annotations/insulator_001.xml) for obj in xml_tree.getroot().findall(object): print(VOC name:, obj.find(name).text) with open(labels/insulator_001.txt) as f: for line in f: print(YOLO line:, line.strip()) with open(annotations/instances.json, encodingutf-8) as f: coco json.load(f) for img in coco[images]: if img[file_name] insulator_001.jpg: for ann in coco[annotations]: if ann[image_id] img[id]: print(COCO category_id:, ann[category_id])如果 VOC 的 name 是damaged_insulatorCOCO 的category_id是 1而 YOLO 的class_id是 1那就错了——YOLO 里 0 应该是normal_insulator1 应该是damaged_insulator。修正时以 VOC 的name为基准重建 YOLO 的 txt 和 COCO 的category_id映射不要只改一个格式。5.3 小目标瓷瓶漏检严重mAP50 还可以但 mAP50-95 很难看现象瓷瓶在高空航拍图里只有一二十个像素模型要么直接漏检要么框显著偏大或偏小。mAP50 看着还行mAP50-95 却明显偏低。原因YOLO 在 640 分辨率输入下最深特征图的步长是 32一个 20 像素的小目标在特征图上只有不到一个像素点信息被压缩得所剩无几。小目标占比高的场景里默认配置对小目标不友好这是网络结构本身的约束。解决优先把imgsz升到 1280 试一次效果通常立竿见影。如果显存放不下用 SAHI 这类切片推理工具把大图切成 640 的小块分别检测再合并结果。更根本的做法是训练阶段就做切片数据增强把原始大图随机裁成小块再喂进模型。我一般先跑一个 640 的 baseline再看验证集里小目标的 AP 表现决定要不要上 1280。5.4 训练 loss 不降甚至 NaN现象loss 第一轮就是 nan或者前几轮正常后面突然变成 nan另一种是 loss 一直挂在某个数值附近不动怎么调都不降。原因标签坐标越界是最常见的元凶——归一化后 w 或 h 大于 1或中心点坐标落在图片外。其次是学习率过高超出优化器的稳定区间。batch 过小也会让 BatchNorm 的统计量抖动导致训练不稳定。解决先把所有 txt 做一轮范围校验过滤越界标签import glob for txt in glob.glob(dataset/train/labels/*.txt): with open(txt) as f: for line in f: parts line.split() cx, cy, w, h map(float, parts[1:]) if not (0 w 1 and 0 h 1 and 0 cx 1 and 0 cy 1): print(txt, line.strip())如果打印出越界行说明标签本身有问题修正后再训不要硬着头皮跑。如果没有越界且 loss 还是 nan把学习率从默认的 0.01 降到 0.001 试一次排除超参问题batch 尽量提到 16 以上再观察。顺序是先查标签再降学习率最后调 batch。5.5 测试集指标虚高部署到新航线上就翻车现象本地 val 和 test 的 mAP 都到 90% 以上模型换到一片新线路、新天气的无人机航拍数据上效果明显变差误检漏检都上来了。原因划分时没做序列去重。无人机巡检的原始数据是按航线连续拍的同一基杆塔的相邻十几帧画面高度相似。如果不按拍摄时间或位置分组这些近乎重复的帧会被同时塞进 train 和 val模型等于在背题验证指标自然虚高。解决划分前先按拍摄序列分组一组整体进 train 或进 val不是按单帧随机分。如果数据集文件名的前缀带有序列信息比如tower_001_frame_001这样的命名直接用序列前缀做分组依据再划分。这也是为什么划分脚本里先取图片列表、再按文件联动复制标签而不是按目录整体移动——分组逻辑可以替换文件联动逻辑是通用的。6. 模型训完先别急着部署用 conf 阈值和混淆矩阵做一轮复盘6.1 用 yolo val 分阈值复验训练完的best.pt在 val 上的指标是框架默认阈值下算出来的部署时不一定是最优工作点。我习惯把不同置信度阈值下的表现都跑一遍yolo detect val \ datainsulator.yaml \ modelruns/detect/insulator_exp1/weights/best.pt \ conf0.25然后把conf改成 0.4 再跑一次对比两次输出的 precision 和 recall。如果误检多提高 conf 能压住如果漏检多就要降低 conf。具体调到多少以你实际场景里误检和漏检哪个代价更大来决定。6.2 混淆矩阵与 edge 部署每次 val 跑完runs/detect/insulator_exp1/下会生成confusion_matrix.png。除了正常瓷瓶和破损瓷瓶两类的互相混淆重点看 background 那一格。如果背景被模型大量预测为目标说明误检率高部署时把 conf 往上提到 0.4 到 0.6误检能压下来但 recall 也会掉一点。边缘设备上部署检测模型时很多误检问题都不是模型没训好而是推理时置信度阈值设得太低我见过直接把 conf 设成默认 0.25 就上线结果误检框满天飞的案例。我最早做绝缘瓷瓶检测项目时为了让指标好看训练时把 conf 调得很低验证集里确实多检出了几个真目标但部署到现场后误检框多到没法看演示的时候当场翻车。后来我养成一个习惯模型训完只做两件事第一看混淆矩阵的 background 列第二用测试集把不同 conf 下的表现跑一遍选好工作点再固化到部署代码里。这两件事做完模型才敢挂出去。希望帮到你。本文还有配套的精品资源点击获取
返回列表