尧图精选

鼠类检测数据集:VOC与YOLO格式转换及YOLOv8训练实战

🕒 发布时间:2026/10/1 4:16:00 📁 来源:尧图网络
简介这是一份面向目标检测任务整理的鼠类数据集适合计算机视觉初学者、算法工程师及需要扩充训练样本的开发者。资源围绕“rat”这一类别进行标注共提供约587张jpg原图并同时给出VOC格式的xml标注与YOLO格式的txt标注两种格式一一对应便于直接接入常见检测框架。压缩包内含1762个文件主体为588个txt、587个xml、587个jpg图片大小在1-500KB之间整包约149.16MB解压后按图片、xml、txt三个文件夹分类存放目录结构清晰可快速定位到所需文件。标注环节使用labelImg完成并遵循准确框选边界、尽量覆盖全部目标、交叉一致性检查等规范降低了数据清洗门槛。目前已有146人学习下载适合用于模型训练、格式对比或标注流程参考是一份轻量且可直接落地的数据集。1. 鼠数据集 VOC 和 YOLO 格式587 张标注图能支撑一个检测项目吗从仓储防鼠、实验室动物行为分析到家庭宠物识别鼠类检测是目标检测里典型的小样本垂直场景很难找到公开标好的鼠图像集通常只能自己拍自己标。标题里这套鼠数据集用约 587 张真实图像同时提供 VOC 和 YOLO 两套目标标注等于提前把最耗时的一步做完了。它能解决的问题很直接当你准备用 YOLOv8 或 YOLO11 做鼠检测时数据准备环节被大幅压缩直接进入训练和调参。适合三类人刚入门目标检测、想拿小数据集跑通全流程的新手做鼠类预警或实验室监测的开发者想彻底搞懂 VOC 与 YOLO 标注映射关系的进阶者。接下来我按数据结构、训练复现、格式转换、避坑和验证的顺序拆开讲。2. 数据集结构拆解VOC 的 XML 和 YOLO 的 txt 分别在记什么同一批图像配两套标注文件刚接手的人常以为是冗余其实是两套完全不同的坐标系VOC 用绝对像素坐标给人看和改YOLO 用归一化比例坐标给训练框架直接吃。手里同时有两种格式时工作流会灵活很多——写增强脚本时用 XML 好调试直接训练时用 txt 少一步换算。2.1 VOC 标注长什么样目标框落在 XML 的哪个节点里VOC 格式每个图像对应一个同名 XML 文件目录结构一般长这样mouse_dataset_voc/ ├── JPEGImages/ │ ├── mouse_001.jpg │ ├── mouse_002.jpg │ └── ... ├── Annotations/ │ ├── mouse_001.xml │ ├── mouse_002.xml │ └── ... └── ImageSets/ └── Main/ ├── train.txt └── val.txtJPEGImages放原图Annotations放标注 XMLImageSets/Main里是划分好的训练和验证图像名列表。打开任一 XML 会有这些关键标签annotation folderJPEGImages/folder filenamemouse_001.jpg/filename size width1280/width height720/height depth3/depth /size !-- 目标一 -- object namemouse/name bndbox xmin80/xmin ymin120/ymin xmax310/xmax ymax240/ymax /bndbox /object !-- 目标二 -- object namemouse/name bndbox xmin420/xmin ymin90/ymin xmax551/xmax ymax180/ymax /bndbox /object /annotation逻辑说明size里的宽高决定后面所有坐标的解释方式它必须跟实际图片尺寸一致否则后续归一化全部偏移。object可以出现多次每个 object 代表一个鼠目标name是类别名单类别数据集里一般全是mouse。bndbox下四个值分别是左上角 x、左上角 y、右下角 x、右下角 y单位是像素并且是整数。这个顺序不是xmin, ymin, width, height写成四元组是[xmin, ymin, xmax, ymax]比 COCO 的[xmin, ymin, width, height]多一步换算。这个格式的好处是表意清晰任何目标检测常用标注工具打开都能直接改缺点是训练时还得再转一次坐标。因此装机量更大的反而是下一节这种写法。2.2 YOLO 标注长什么样一行一个归一化目标框YOLO 格式的文件组织要简单得多每个标注文件是跟图像同名的 txt放在 labels 目录里内容是一行一个目标0 0.152344 0.250000 0.179688 0.166667 0 0.378906 0.187500 0.102344 0.125000第一列是类别编号从 0 开始。后面四列分别是目标框中心点 x、中心点 y、框宽、框高全部除以图像宽高做了归一化因此取值范围约定在 0 到 1 之间。拿上一节那张 1280×720 的图举例第一只鼠的边框坐标是 xmin80, ymin120, xmax310, ymax240换算成中心点就是 (80310)/2195, (120240)/2180再除以宽高得到 x_center195/1280≈0.152344, y_center180/7200.25框宽 230/1280≈0.179688框高 120/720≈0.166667。这样坐标值跟图像分辨率解耦模型在任意输入尺寸下都能复用。需要留个心眼的是YOLO txt 文件里没有图像元信息。如果标注文件里出现负数或大于 1 的值说明原框越界或标注工具记录错误训练时虽然不会崩但会让 anchor 匹配乱套。这也是后面专门要做越界检查的原因。2.3 两种格式的思路对照人读的坐标系和机器读的坐标系把两套格式铺开对比核心差异有三处。第一VOC 表达的是绝对像素位置YOLO 表达的是相对图像比例第二VOC 给出的是左上和右下两个对角点YOLO 给出的是中心点和宽高第三VOC 里类别是字符串 nameYOLO 里类别是整数索引索引对应的类别全表在 data.yaml 的 names 字段里另行定义。换算公式其实就四个x_center (xmin xmax) / 2 / image_widthy_center (ymin ymax) / 2 / image_heightwidth (xmax - xmin) / image_widthheight (ymax - ymin) / image_height很多标注工具导出 VOC 是默认项导出 YOLO 却要手动勾选。如果你手头只有 VOC 标注又急着跑 YOLOv8就需要自己写或找现成的转换脚本。转换这件事看着简单实际有四个高频坑图像尺寸对不上、类别名大小写混用、文件名后缀不一致、越界框没处理。这些我在第 4 章单独展开。如果你用过 COCO 数据集会发现它的 json 里坐标是[xmin, ymin, width, height]和 VOC 的[xmin, ymin, xmax, ymax]只差一个减法。这也引出一个常见误操作把 VOC 转 YOLO 的脚本直接套在 COCO json 上出来的框宽高永远翻倍。换数据集格式之前先把源格式的坐标系定义查清楚再动手比试错更快。提示用标注工具导出时先确认“导出格式”里选的是 Pascal VOC 还是 YOLO两者保存目录和扩展名完全不同不要拿 txt 格式的 YOLO 标注去套 VOC 的目录结构。3. 用 YOLOv8 训练自己的鼠检测数据集目录、配置和最小命令数据集本身是双格式落地时最通用的一条路是直接用 YOLO 格式训练。下面这套流程是我在小样本数据集上跑过多次的标准做法从目录摆放到看到验证指标半小时内能走完。3.1 按框架约定摆目录images 与 labels 必须同名对应YOLOv8 的约定是图像目录与标注目录结构对称图片和 txt 文件名一一对应。如果你直接把 VOC 那套 JPEGImages/Annotations 目录拿给 YOLO 读一定会读到“No labels found”。正确组织方式如下mouse_dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── mouse_001.jpg │ │ ├── mouse_002.jpg │ │ └── ... │ ├── val/ │ │ ├── mouse_401.jpg │ │ └── ... │ └── test/ │ ├── mouse_541.jpg │ └── ... └── labels/ ├── train/ │ ├── mouse_001.txt │ └── ... ├── val/ │ ├── mouse_401.txt │ └── ... └── test/ └── ...我从 587 张里取 80% 做 train10% 做 val10% 做 test。划分时按文件名随机抽样再同时移动到 images 与 labels 两个目录不要只复制图片不复制标注。划分配对建议写成脚本别手动拖文件手一抖图片和 txt 对不上训练日志的 val 指标会非常难看。下面是带固定种子的划分脚本import random import shutil from pathlib import Path random.seed(42) src_img Path(voc/JPEGImages) src_lab Path(yolo/labels) base Path(mouse_dataset) for split, ratio in [(train, 0.8), (val, 0.1), (test, 0.1)]: imgs sorted(src_img.glob(*.jpg)) random.shuffle(imgs) count int(len(imgs) * ratio) for img in imgs[:count]: lab src_lab / (img.stem .txt) if not lab.exists(): continue dst_img base / images / split / img.name dst_img.parent.mkdir(parentsTrue, exist_okTrue) shutil.copy2(img, dst_img) dst_lab base / labels / split / (img.stem .txt) dst_lab.parent.mkdir(parentsTrue, exist_okTrue) shutil.copy2(lab, dst_lab)这段逻辑有两个关键设计一是用同一种子 shuffle保证重跑结果一致二是只挑标注存在的图像进训练集避免空标注图片把 val 指标带偏。参数 ratio 不用追求精确训练集足够大时划分误差对模型影响很小。3.2 写 data.yaml 并跑通训练命令参数不玄学看环境说话data.yaml 是 YOLO 读取数据集的入口路径字段写绝对路径避免框架拼接时产生歧义# data.yaml path: /home/user/mouse_dataset train: images/train val: images/val test: images/test names: 0: mouse然后执行训练cd /home/user yolo detect train datamouse_dataset/data.yaml \ modelyolov8n.pt epochs80 imgsz640 \ batch16 device0 workers4参数说明modelyolov8n.pt是 nano 规模预训练权重本地没有时会自动下载 YOLOv8 在 COCO 上的预训练模型用它做起点比从零初始化收敛快得多这一点在小数据集上几乎是必须的。imgsz640控制训练输入分辨率想检测更小号的鼠可以升到 1024但显存和训练时间会同步上涨。batch16在 16G 显存上能跑显存小就降到 8 或 4。workers4是读图线程数Windows 上建议设 0否则容易报 DataLoader worker 错误。device0指第一块 GPU纯 CPU 环境改成devicecpu能出结果但慢一些。如果你不想手动调YOLOv8 自带默认值也够epochs 默认 100imgsz 默认 640batch 默认 16。我第一次跑这种小数据集选过 yolov8s.pt精度提升有限训练时间却翻了快一倍后来固定在 nano 起步、不行再上 small。3.3 训练日志怎么读从预热到收敛哪些指标值得盯训练跑起来后终端刷新的日志分两部分。前半段是 loss 曲线后半段是每个 epoch 结束后的 box loss、cls loss、dfl loss。重点看的不是 loss 单点值而是 train 与 val 的 loss 有没有拉开过大的剪刀差。出现这种情况说明过拟合587 张的小数据集尤其敏感改进方法是补增强或减少 epochs。最终表格会输出类似这样ClassImagesInstancesBox(P)RmAP50mAP50-95all591020.9410.8870.9120.761这里 Images59 是 val 图像数Instances102 是这 59 张图里所有真实目标的总个数。Box(P) 是精确率R 是召回率mAP50 是 IoU 阈值 0.5 下的均值平均精度mAP50-95 是阈值从 0.5 到 0.95 取平均。前者衡量“框大体位置对不对”后者衡量“框贴得准不准”。小目标场景下常见 mAP50 高、mAP50-95 低这不用慌说明边界框误差主要来自精修而不是整体漏检。如果是部署做预警类需求我更看重 mAP50 和 R因为漏报警比框不准更致命。4. 从 VOC 转 YOLO 标注一个转换脚本和四个边界坑很多真实项目里别人给的标注文件可能只有 VOC 一种格式。能把 XML 稳定转成 YOLO txt以后处理数据集用于 YOLOv8 训练时都能直接用。下面是我一直保留的转换脚本以及四个反复踩到的边界问题。4.1 转换脚本把 XML 的绝对像素换算成归一化比例import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path: Path, class_names: list, out_dir: Path) - None: tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) b obj.find(bndbox) xmin float(b.find(xmin).text) ymin float(b.find(ymin).text) xmax float(b.find(xmax).text) ymax float(b.find(ymax).text) # 中心点与宽高全部归一化到 [0,1] x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 越界保护 x_center max(0.000001, min(0.999999, x_center)) y_center max(0.000001, min(0.999999, y_center)) width max(0.000001, min(0.999999, width)) height max(0.000001, min(0.999999, height)) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if lines: (out_dir / (xml_path.stem .txt)).write_text(\n.join(lines) \n) if __name__ __main__: class_names [mouse] xml_root Path(Annotations) out_dir Path(labels) out_dir.mkdir(exist_okTrue) for xml_file in xml_root.glob(*.xml): voc_to_yolo(xml_file, class_names, out_dir)逻辑与参数class_names 是类别列表顺序必须和训练 data.yaml 里的 names 完全一致这个列表的索引就是 YOLO 标注里的第一列。归一化时我保留 6 位小数足够单精度训练使用越界保护用 0.000001 而不是 0是因为 0 表示边框刚好贴边检测时会产生极端 anchor容易加剧训练发散。脚本只处理*.xml如果标注是*.XML大小写差异把 glob 写成glob(*.xml)的兄弟glob(*.[xX][mM][lL])或者先统一后缀。转换完成后肉眼验证一个样本对照原图把 txt 里的数值反算回像素坐标看框是否和老鼠对齐。这一步我每次换新数据集都会做能挡掉大部分标注工具版本差异造成的坐标漂移。4.2 坑一size 与实际图宽高不一致框整体漂移现象转换出的 txt 数值看起来正常训练也能跑但验证集里边界框普遍偏左上或偏右下尤其图像边缘的老鼠框错位明显。原因VOC XML 里的size是标注工具写图片时的快照。如果图片后来被外部脚本改过分辨率或原图是带 EXIF 旋转的手机照片工具记录的宽高就没跟上实际像素。解决转换前用 PIL 或 OpenCV 读取真实宽高不一致就按真实值覆盖from PIL import Image img_w, img_h Image.open(img_path).size # 实际尺寸优先如果你的图片本身带旋转信息建议统一转换成无 EXIF 的正向图再训练不然即使宽高对了目标框整体也会跟着转 90 度。4.3 坑二类别名大小写混用脚本直接索引越界现象脚本运行到一半抛ValueError: Mouse is not in list或者更隐蔽的class_names.index(name)返回错误索引导致一张图上有的鼠框类别是 0有的类别是 1。原因标注人员在不同批次里录入的类别名不一致比如mouse、Mouse、mice、鼠混在一批 XML 里。解决转换前先做一个统计把所有出现过的 name 值打出来names set() for xml_file in xml_root.glob(*.xml): root ET.parse(xml_file).getroot() for obj in root.iter(object): names.add(obj.find(name).text) print(names)看到结果后再写一个别名映射统一归到 class_names 里的标准名。千万不要在转换脚本里临时改名那只会让映射越来越乱。4.4 坑三XML 与图片不同名转换结果静默丢失一半现象转换脚本无报错但 labels 目录里只有 200 个 txt而 Annotations 目录里有 300 个 XML。原因VOC 文件命名时不只有mouse_001还有一批是Mouse-001或batch2_house.jpgXML 的 stem 和图片对不上。YOLO 训练时按图片文件名找标注找不到就跳过图照常训练但被当成背景。解决转换前做一个配对检查xml_stems {p.stem for p in xml_root.glob(*.xml)} img_stems {p.stem for p in img_root.glob(*.jpg)} print(fXML 有 {len(xml_stems)} 个图片有 {len(img_stems)} 个缺失 {len(xml_stems - img_stems)} 个)把不匹配的挑出来人工处理别让脚本替你做决定。静默丢弃的标注是数据集里最贵的沉淀丢了想补标得重新翻图。4.5 坑四越界框直接写进 txt训练时警告刷屏现象训练每轮都刷标签越界警告启动阶段还会在 dataset 检查里看到红字提示。原因老鼠身体有一部分在图像边缘标注工具没截断导致 xmax 大于图宽归一化后 x_center 超过 1甚至出现负值。解决既要写到脚本的越界保护里也要在数据检查阶段揪出来。我一般在转换完写一个校验命令把值在 [0,1] 之外的行单独列出来awk $20 || $21 || $30 || $31 {print FILENAME, $0} labels/*.txt这一条命令能快速定位所有越界标注。配合脚本内的截断逻辑训练时才能完全不报警。注意这里我选择的策略是截断而不是丢弃整条边界框因为一只鼠贴着图像边缘时框的大部分仍有效直接丢掉会让模型学不到边缘目标的形态。5. 避坑排查鼠检测训练里五个高频翻车现场拿 587 张这个量级的数据集训练框架层面的坑比算法层面的更磨人。下面五个问题在社区里几乎每天都能看到按现象、原因、解决三个步骤写清方便你直接对照排查。5.1 训练可以跑但 val mAP 一直是 0连 0.01 都上不去现象训练 loss 正常下降每个 epoch 结束后的验证表格却清一色是 0Box(P)、mAP50 全部为零。原因最常见的是 labels 目录里的 txt 文件名和 images 目录里的图片名没有一一对应。比如图片叫mouse_001.jpg标注却叫M001.txtYOLO 逐个匹配时找不到标注就把这张图当背景样本用。验证集里没有真值目标mAP 自然恒为 0。解决先停训练检查配对数量和文件名大小写。运行 4.4 里的配对脚本把不匹配名单清出来如果确认文件名一致再打开 labels 下的 txt确认第一行的类别编号是否落在 data.yaml 的 names 长度范围内。这个翻车现场最常见的诱因就是先前划分数据集时只复制了图片、没复制 txt。5.2 启动就报 No labels found但目录里明明有文件现象训练命令刚执行日志里出现WARNING No labels found in /path/to/train/labels随后训练自动终止或全程不更新。原因要么是目录路径写错要么是标注文件内容为空。我见过最多的一种翻车是 data.yaml 里train: images/train写成相对路径却忘了写最上层的path:字段框架用默认路径拼接自然找不到 labels。还有一个隐蔽原因是 txt 文件存在但 0 字节YOLO 一律认为它是空标注直接忽略。解决把path字段改成绝对路径并确保 images 和 labels 下同名的子目录名完全一致。空标注文件用find labels -name *.txt -empty找出来要么回 VOC 重转要么删掉对应的图片不要留隐患。5.3 训练到一半 loss 变成 NaNBN 层开始崩溃现象训练到十几个 epochloss 从 0.02 突然跳到 NaN后续不再恢复验证集指标也全部变 NaN。原因小数据集上学习率过高或增强过度是主力。YOLOv8 默认训练 100 epoch 时学习率会做 warmup 爬升如果你改了lr00.02这类激进参数很容易直接把 BN 层统计数据冲爆另外 mosaic 和 mixup 同时把增强拉满在小样本上也可能出现数值发散。这个现象网上常叫“yolo训练中bn崩溃”本质上不是网络结构坏是参数和数据的配比失衡。解决把lr0调回默认的 0.01或干脆不开任何自定义学习率参数mosaic如果已经设成 1.0降到 0.5 试一轮。更稳妥的做法是回归最简单的配置跑 20 个 epoch确认不 NaN 后再逐步加增强。5.4 验证 mAP50 到 0.9实际推理却漏检测现象val 曲线好看模型一上真实监控画面帧率正常但鼠目标大量漏掉尤其目标小到十几个像素的时候。原因小目标占比高时mAP50 会被大量中等目标拉高看不出小目标上的准确率。模型在 640 分辨率下一个 15×15 像素的老鼠只占不到 1% 面积特征图下采样后基本没信息。这是小数据集场景最容易出现的指标乐观偏差说玄学也好运气也罢本质是评测样本和部署样本的分布不一致。解决训练时把imgsz调到 768 或 1024推理时也要用相同分辨率否则训练与部署口径不一致指标会平白掉一截。如果你确定部署端是固定视频流可以按裁剪区域训练把画面切成多块让鼠目标在 640 里占据更大比例。5.5 导出 ONNX 后推理结果对不上预处理细节被忽略现象PyTorch 里模型检测框正常用 ONNX Runtime 跑同样的图结果全乱框的位置和置信度都偏差很大。原因ONNX 导出后YOLOv8 的推理逻辑是解耦的常见坑有三个没有做 letterbox 缩放、没有把 BGR 转 RGB、没有按训练时的预处理做归一化。这三步差一步结果就可能从“检测到”变成“一个都没有”。解决写成标准推理函数先 letterbox 到训练 imgsz再[..., ::-1]转 RGB最后除以 255.0不要假设 ONNX 会自动处理。验证时用yolo export modelbest.pt formatonnx导出的文件配合yolo predict modelbest.onnx sourcetest.jpg对比效果能省下不少自制推理脚本的调试时间。6. 模型怎么验证才放心PR 曲线、盲测和连续帧检测模型训完除了看一眼 val 指标我还会做三件事。第一导出 PR 曲线找 confidence 阈值。YOLOv8 训练结束会保存PR_curve.png看曲线的肘部位置。低于肘部阈值太低会有大量误检高于肘部漏检率上升。部署阶段我用肘部对应的值而不是默认的 0.25。命令先跑一次yolo detect predict modelbest.pt sourceval_images/ conf0.35 iou0.5 saveTrue这里的conf控制置信度门槛iou控制 NMS 去重时的重叠阈值。鼠检测场景里如果多只老鼠挤在一起iou可以降到 0.4否则相邻框会被 NMS 误杀。第二用 test 目录做盲测且 test 的划分要仿真实分布。不要从同一个视频抽帧里既取 train 又取 test那样模型等于“见过”测试帧指标虚高。正确做法是按采集时间段或场景分组比如白天建的 train晚上拍的做 test这样测出来才是真实泛化水平。第三连续帧验证。静态图片通过不代表视频流能用。我习惯拿一段 10 秒的监控视频每 5 帧抽 1 帧预测检查有没有出现单帧闪断上一帧检出下一帧什么都检不出后面又恢复。这类闪断常来自运动模糊和遮挡直接表现是 Recall 波动。处理办法不是调模型而是加一个轻量跟踪器靠前后帧关联把断检补上。我自己在鼠检测这类小样本项目上最大的习惯是先把数据隐患清干净再谈模型。587 张不算多但足够暴露你在标注转换、目录划分和指标解读上的所有粗糙习惯你把这三关都打通了换到任何大一点的数据集只会更顺。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →