ARTICLE DETAIL

资讯详情

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

小样本单类别目标检测实战:VOC+YOLO双格式蜈蚣数据集训练全流程

小样本单类别目标检测实战:VOC+YOLO双格式蜈蚣数据集训练全流程 简介这份数据集面向计算机视觉方向的学习者与开发者尤其是需要开展蜈蚣目标检测训练、验证模型效果或进行小样本实验的人群。资源同时提供Pascal VOC与YOLO两套标注格式省去格式转换环节可直接接入主流检测框架。压缩包共713个文件包含237张jpg原图、237个VOC格式xml标注文件、237个YOLO格式txt标注文件另有少量说明类txt整体约68.14MB目录结构清晰便于按格式分别取用。标注统一采用labelImg工具以矩形框方式完成仅含Centipede一个类别累计标注框255个标注结果准确合理。目前已有96人学习下载可作为单类别检测任务的入门练手素材也可用于验证数据增强、迁移学习等策略在小规模数据集上的表现。需注意该数据集不对训练所得模型或权重文件的精度作任何保证使用者应结合自身需求评估与调优。1. 蜈蚣数据集 VOCYOLO 双格式237 张 1 类别到底能训出什么手上只有 237 张图、1 个类别很多人第一反应是「这点数据能干嘛」。我拿到的这份蜈蚣数据集就是典型的小样本单类目标检测场景VOC 格式的 XML 标注加一份已经转好的 YOLO 格式 txt类别只有centipede一个。它解决的不是「通用检测」问题而是让你在半小时内跑通一条从数据校验到训练再到推理的完整链路验证自己的环境、标注流程和训练脚本是否真的能跑。适合两类人一是刚接触 YOLO 目标检测、想找一个干净小数据集练手的新手二是手上有一批类似爬虫、昆虫、零件图像想先拿小样本验证方案再放量的工程师。237 张不算多但单类别、双格式、标注统一反而比那些几万张多类别数据集更适合做「流程验证」。下面我按实际落地顺序拆开讲。2. 先看清数据VOC 与 YOLO 两套标注的差异和校验方法2.1 VOC 的 XML 和 YOLO 的 txt 到底差在哪VOC 格式每张图对应一个同名 XML核心字段是size里的宽高和object里的name与bndbox坐标是绝对像素值左上角xmin,ymin、右下角xmax,ymax。YOLO 格式每张图对应一个同名 txt每行class_id x_center y_center width height全部是归一化到 0~1 的相对值且中心点坐标。两者不是简单缩放关系转换时最容易翻车的就是「先算宽高还是先算中心」以及「有没有做边界裁剪」。单类别时class_id恒为 0这反而降低了出错概率。但要注意VOC 里可能出现difficult1的难样本YOLO 格式没有这个字段转换时要么丢弃要么保留得自己定策略。我一般会保留因为 237 张本来就少丢掉难样本会让模型在遮挡场景下更差。2.2 用脚本做一次全量校验别信「已经转好了」拿到双格式数据集第一件事不是训练是校验两套标注是否一一对应、坐标是否越界。下面这段脚本同时检查 XML 和 txt 的配对情况与坐标合法性。import os import xml.etree.ElementTree as ET IMG_DIR images XML_DIR annotations/voc TXT_DIR annotations/yolo CLASSES [centipede] def check_voc(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) issues [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: issues.append(f未知类别 {name}) box obj.find(bndbox) xmin int(float(box.find(xmin).text)) ymin int(float(box.find(ymin).text)) xmax int(float(box.find(xmax).text)) ymax int(float(box.find(ymax).text)) # 越界检查VOC 允许轻微越界但超过 2 像素就要警惕 if xmin -2 or ymin -2 or xmax w 2 or ymax h 2: issues.append(f越界框 {xmin},{ymin},{xmax},{ymax} 图像 {w}x{h}) if xmax xmin or ymax ymin: issues.append(f非法框 {xmin},{ymin},{xmax},{ymax}) return issues def check_yolo(txt_path): issues [] with open(txt_path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: issues.append(f第{i}行字段数 {len(parts)}) continue cid, x, y, bw, bh parts vals [float(x), float(y), float(bw), float(bh)] if any(v 0 or v 1 for v in vals): issues.append(f第{i}行归一化越界 {vals}) if float(bw) 0 or float(bh) 0: issues.append(f第{i}行宽高非正) return issues imgs {os.path.splitext(f)[0] for f in os.listdir(IMG_DIR)} xmls {os.path.splitext(f)[0] for f in os.listdir(XML_DIR)} txts {os.path.splitext(f)[0] for f in os.listdir(TXT_DIR)} print(图片无XML:, imgs - xmls) print(XML无图片:, xmls - imgs) print(图片无TXT:, imgs - txts) print(TXT无图片:, txts - imgs) for stem in sorted(imgs xmls): errs check_voc(os.path.join(XML_DIR, stem .xml)) if errs: print(stem, VOC问题:, errs) for stem in sorted(imgs txts): errs check_yolo(os.path.join(TXT_DIR, stem .txt)) if errs: print(stem, YOLO问题:, errs)逻辑说明先做集合差集找出缺失配对再逐文件检查坐标。VOC 越界阈值给到 2 像素是因为标注工具偶尔会贴边YOLO 归一化值必须落在 0~1宽高必须为正。参数上CLASSES要和你的data.yaml里names完全一致大小写敏感。跑完如果输出为空说明数据干净可以进入下一步如果有越界框优先修 XML 再重新转换不要直接改 txt否则两套格式会不一致。2.3 划分训练集验证集时别用随机种子糊弄237 张按 8:2 划分训练 189 张、验证 48 张。小数据集最怕验证集里全是相似背景导致指标虚高。我一般会先按图像亮度或拍摄角度粗分再在每类里随机抽保证验证集覆盖不同场景。下面这段划分脚本会把图片和两套标注一起搬到对应目录。python - PY import os, random, shutil random.seed(42) img_dir images voc_dir annotations/voc yolo_dir annotations/yolo stems sorted(os.path.splitext(f)[0] for f in os.listdir(img_dir)) random.shuffle(stems) split int(len(stems) * 0.8) for subset, names in [(train, stems[:split]), (val, stems[split:])]: for sub in [images, voc, yolo]: os.makedirs(fdataset/{subset}/{sub}, exist_okTrue) for s in names: shutil.copy(f{img_dir}/{s}.jpg, fdataset/{subset}/images/{s}.jpg) shutil.copy(f{voc_dir}/{s}.xml, fdataset/{subset}/voc/{s}.xml) shutil.copy(f{yolo_dir}/{s}.txt, fdataset/{subset}/yolo/{s}.txt) print(train, split, val, len(stems) - split) PY逻辑说明固定seed42保证可复现先 shuffle 再切分。参数0.8是训练比例小数据集不建议低于 0.75否则训练样本太少。搬完后dataset/train/images和dataset/train/yolo就是 YOLO 训练要用的结构VOC 那份留着做交叉验证或后续换框架用。3. 从 VOC 转 YOLO转换脚本、类别映射和三个边界坑3.1 转换的核心公式和类别映射表VOC 转 YOLO 的公式不复杂但顺序错了就全废x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height w_norm (xmax - xmin) / width h_norm (ymax - ymin) / height类别映射必须显式维护一张表不能靠enumerate临时生成否则换数据集时类别顺序一变模型学到的全是错的。单类别时表只有一行但习惯要养好。VOC 类别名YOLO class_idcentipede03.2 可直接复用的转换脚本import os import xml.etree.ElementTree as ET VOC_DIR dataset/train/voc OUT_DIR dataset/train/yolo CLASS_MAP {centipede: 0} def convert(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASS_MAP: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 裁剪到图像范围内避免归一化后越界 xmin, ymin max(0, xmin), max(0, ymin) xmax, ymax min(w, xmax), min(h, ymax) if xmax xmin or ymax ymin: continue xc (xmin xmax) / 2 / w yc (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{CLASS_MAP[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) os.makedirs(OUT_DIR, exist_okTrue) for f in os.listdir(VOC_DIR): if f.endswith(.xml): convert(os.path.join(VOC_DIR, f), os.path.join(OUT_DIR, f.replace(.xml, .txt))) print(done)逻辑说明先裁剪再计算避免负坐标或超出图像。:.6f保留 6 位小数YOLO 官方脚本也是这个精度够用且不会引入明显误差。CLASS_MAP必须和data.yaml的names顺序一致。如果某张图所有目标都被裁掉会生成空 txt训练时该图会被当成负样本小数据集里建议直接删掉这张图而不是留空文件。3.3 三个边界坑贴边、极小框、空标注贴边框在 VOC 里很常见转换后x_center可能刚好等于w_norm/2归一化后仍在 0~1但训练时数据增强的随机裁剪会把框裁没。我的做法是转换后统计所有框的宽高分布宽或高小于图像 1% 的直接丢弃237 张里通常也就丢几个。极小框是另一个坑。蜈蚣身体细长标注时容易框得很窄转成 YOLO 后w_norm可能只有 0.01。YOLO 默认 anchor 对这种极端长宽比不友好训练时正样本匹配会很少。解决办法是在data.yaml里不要改 anchor而是把输入尺寸从 640 提到 960让细长目标有更多像素。空标注就是上面说的空 txt。YOLO 训练时会把没有目标的图当背景负样本单类别小数据集里背景图太多会让模型偏向「什么都别检」。校验阶段发现空 txt 直接删图删标注别犹豫。4. 用这份数据跑通 YOLO 训练环境、配置和必调参数4.1 环境配置和 data.yaml 写法环境用 conda 建一个干净环境Python 3.10 加 PyTorch 2.xYOLOv8 直接 pip 装。不要混用 yolov5 和 yolov8 的依赖血泪经验是 ultralytics 版本冲突会让训练脚本报一些看不懂的错。conda create -n centipede python3.10 -y conda activate centipede pip install ultralytics yolo checksdata.yaml是训练入口路径写绝对路径最稳相对路径在不同工作目录下容易翻车。path: /abs/path/dataset train: train/images val: val/images nc: 1 names: 0: centipede参数说明nc是类别数单类别写 1names的键必须从 0 开始连续。train和val指向图片目录YOLO 会自动去找同名 txt所以 txt 必须和图片同目录或按约定放在labels目录。我一般把 txt 放在images同级新建的labels目录避免和图片混在一起。4.2 训练命令和四个必调参数yolo detect train \ data/abs/path/dataset/data.yaml \ modelyolov8n.pt \ epochs200 \ imgsz960 \ batch8 \ lr00.001 \ patience50 \ projectruns/centipede \ nameexp1参数逐个说modelyolov8n.pt用 nano 版237 张数据量小大模型直接过拟合epochs200配合patience50早停防止无效训练imgsz960是前面说的细长目标对策显存不够就降到 640 但指标会掉batch8在 8G 显存上比较稳太小会让 BN 统计不准lr00.001是默认值小数据集可以降到 0.0005 更稳。训练日志里重点看mAP50和mAP50-95单类别时mAP50上 0.8 算正常上不去先查标注而不是调参。4.3 推理和置信度门限怎么定训练完用yolo detect predict跑验证集重点调conf和iou。yolo detect predict \ modelruns/centipede/exp1/weights/best.pt \ sourcedataset/val/images \ conf0.25 \ iou0.5 \ saveTrueconf0.25是默认门限蜈蚣这种细长目标建议从 0.15 开始试看漏检和误检的平衡点。iou0.5控制 NMS 合并阈值目标密集时调低到 0.4 能减少漏检。验证时不要只看指标把预测图打开肉眼过一遍小数据集上指标和实际观感经常不一致这就是玄学的地方。5. 避坑与排查小样本单类别训练最容易翻的 5 个坑现象一训练 loss 一直不降mAP 接近 0。原因通常是data.yaml里names和 txt 里的class_id对不上或者 txt 路径没被正确识别。解决先跑一遍第 2 章的校验脚本确认 txt 存在且 class_id 为 0再检查data.yaml的path是否是绝对路径。现象二验证集 mAP 很高但实际推理全是误检。原因是验证集和训练集背景太相似模型学到了背景捷径。解决重新划分验证集按亮度或角度分层抽样或者把验证集比例提到 0.25宁可训练少一点也要验证可信。现象三细长目标大量漏检。原因是默认 anchor 和输入尺寸不适合极端长宽比。解决把imgsz提到 960 或 1280同时把conf降到 0.15 观察召回变化。如果还不行考虑在标注阶段把蜈蚣身体分成两段标注但单类别下这会改变任务定义慎用。现象四训练到一半显存爆了。原因是batch太大或imgsz太高。解决先把batch降到 4再考虑降imgsz。YOLOv8 的batch-1自动模式在小显存上不一定靠谱手动设更稳。现象五转完 YOLO 格式后图片和标注数量对不上。原因是转换脚本遇到空标注或非法框时跳过了但图片还在。解决转换后做一次集合比对缺 txt 的图片要么补标要么删图不要留着让训练脚本自己猜。6. 把 237 张用到极致数据增强、交叉验证和指标复核小数据集的进阶玩法不是换更大的模型而是把现有数据榨干。我一般会做三件事增强、交叉验证、指标复核。数据增强方面YOLOv8 内置了 mosaic、mixup、HSV 抖动和随机翻转。237 张单类别我通常把mosaic开到 1.0mixup开到 0.1degrees开到 10translate开到 0.1。注意flipud对蜈蚣这种有方向性的目标要慎用上下翻转可能让标注语义变得不自然fliplr一般没问题。增强参数写在训练命令里即可不用改配置文件。yolo detect train \ data/abs/path/dataset/data.yaml \ modelyolov8n.pt \ epochs300 \ imgsz960 \ batch8 \ mosaic1.0 \ mixup0.1 \ degrees10 \ translate0.1 \ fliplr0.5 \ flipud0.0 \ patience60交叉验证是 237 张数据最值得投入的一步。把数据分成 5 折每折轮流做验证最后看 5 次 mAP 的均值和方差。方差大说明数据分布不均均值才是可信指标。5 折训练时间大概是单次的 5 倍但小数据集单次很快总成本可接受。跑完把 5 个best.pt在同一个独立测试集上推理取置信度平均或做 WBF 融合通常能比单模型涨 2~5 个点。指标复核容易被忽略。YOLO 输出的mAP50是 IoU 0.5 下的平均值单类别时就是该类别的 AP。但 AP 计算依赖排序小数据集里一两个样本的置信度波动就能让 AP 跳变。我的习惯是导出验证集的预测结果按置信度排序人工看前 20 个和后 20 个确认没有系统性错误。如果发现某类背景反复误检就把这些图加进训练集哪怕只加 10 张效果也比调参明显。最后说个我自己的习惯每次训完模型我都会把best.pt和当时的data.yaml、训练命令一起存进一个带日期的文件夹命名比如20250101_centipede_960。小数据集实验迭代快不记录的话两周后就忘了哪个模型对应哪套参数后悔药没处买。237 张能跑出可用模型但真正决定成败的是你有没有把每一步的输入输出管清楚。希望帮到你。本文还有配套的精品资源点击获取
返回列表