
简介本资源为老鼠检测数据集面向从事目标检测算法训练与验证的开发者、学生及研究人员可用于智能家居、农业监控、生物实验等场景下的鼠类识别模型训练。数据集同时提供Pascal VOC与YOLO两种标注格式包含jpg图片及一一对应的xml、txt标注文件标注类别为mouse共14321个矩形框使用labelImg工具完成标注标注规则统一规范。压缩包为7z格式共2000个文件其中1999个xml标注文件与1个说明用txt文件整体约187.89MB需注意图片与标注文件需按同名规则配对使用。该数据集含增强图片目测超过一半为增强样本可提升模型泛化能力。目前已有809人学习下载适合需要快速获取鼠类检测训练数据、验证检测流程或进行迁移学习实验的读者参考使用。1. 老鼠检测数据集实战4107 张 VOCYOLO 双格式到底能跑出什么做鼠类目标检测的同行大概都有过这种经历想训一个老鼠检测模型翻遍公开数据集要么是 ImageNet 那种分类数据没法直接拿来做检测要么是只有几十张图的玩具集训出来的模型一换场景就崩。这份 4107 张的老鼠检测数据集恰好卡在一个能用的量级上——单类别 mouse14321 个标注框同时给了 Pascal VOC 的 xml 和 YOLO 的 txt 两套标注labelImg 打的矩形框。它解决的核心问题就一个让你不用从零标数据直接进入训练和调参环节。适合谁做鼠情监测、实验室动物行为分析、粮仓/餐饮后厨鼠患识别的工程师以及想拿一个真实单类数据集练手 YOLO 全流程的人。需要提前说清楚的是这份数据有增强目测超过一半是增强图这意味着它的分布和纯实拍集不一样后面会专门讲这个坑。2. 双格式标注拆解VOC 的 xml 和 YOLO 的 txt 到底差在哪2.1 两种格式的坐标体系与目录结构先把这个数据集的文件构成说透。解压后你会看到三类文件jpg 图片 4107 张、同名 xml 4107 个、同名 txt 4107 个。xml 是 Pascal VOC 格式txt 是 YOLO 格式一一对应。VOC 的 xml 里一个目标对应一个object节点坐标是绝对像素值记在bndbox的 xmin/ymin/xmax/ymax 里类别名写在name里这份数据里全是 mouse。YOLO 的 txt 则是每行一个目标格式是class_id x_center y_center width height四个坐标全部是归一化到 0~1 的相对值class_id 从 0 开始这里只有 0 一个类。这两种格式的差异不只是写法而是整个训练链路的分叉点。VOC 格式通用性强很多老框架和评测脚本吃 xmlYOLO 格式是 ultralytics 系列直接读的省去转换步骤。数据集同时给两套等于把转换这道工序替你做了但你要清楚它们是不是严格对齐——正常情况下一张图里目标数应该一致xml 里 object 个数等于 txt 行数。我一般拿到双格式数据集第一件事就是抽几张核对别默认它们一定同步。2.2 用脚本核对 xml 与 txt 是否一一对应下面这段脚本干的事遍历所有 xml统计每张图的 object 数再读同名 txt 的行数比对是否相等同时把类别名收集出来确认只有 mouse。这是上手任何双格式数据集前的标准动作。import os import glob import xml.etree.ElementTree as ET img_dir ./images # jpg 所在目录 xml_dir ./annotations # xml 所在目录 txt_dir ./labels # yolo txt 所在目录 mismatch [] classes set() total_boxes 0 for xml_path in glob.glob(os.path.join(xml_dir, *.xml)): stem os.path.splitext(os.path.basename(xml_path))[0] tree ET.parse(xml_path) root tree.getroot() objs root.findall(object) xml_count len(objs) for obj in objs: classes.add(obj.find(name).text.strip()) txt_path os.path.join(txt_dir, stem .txt) if not os.path.exists(txt_path): mismatch.append((stem, txt missing)) continue with open(txt_path) as f: lines [l for l in f.readlines() if l.strip()] txt_count len(lines) if xml_count ! txt_count: mismatch.append((stem, xml_count, txt_count)) total_boxes xml_count print(类别集合:, classes) print(总框数:, total_boxes) print(不一致数量:, len(mismatch)) for m in mismatch[:10]: print(m)逻辑说明ET.parse解析 xmlfindall(object)拿到所有目标节点obj.find(name).text取类别名。txt 按非空行计数。参数上三个目录路径按你实际解压结构改这份数据如果图片和标注混在一个文件夹就把 img_dir 那行去掉xml 和 txt 同目录处理。跑完重点看两个数类别集合应该只有{mouse}总框数应该接近 14321。如果总框数对不上说明有 xml 解析异常或 txt 缺行得逐条查 mismatch 列表。这一步花两分钟能省掉后面训练时 loss 不收敛却找不到原因的几小时。2.3 从 VOC 转 YOLO 的坐标换算万一你要自己转虽然这份数据已经给了 txt但你要做数据增强、筛选子集或者换标注工具时还是得会自己转。VOC 的绝对坐标转 YOLO 归一化坐标公式是x_center (xmin xmax) / 2 / img_wy_center (ymin ymax) / 2 / img_hwidth (xmax - xmin) / img_wheight (ymax - ymin) / img_h。图片宽高要从 xml 的size节点读别用 PIL 再开一次图省 IO。import os import glob import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h 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 bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 归一化并裁剪到 [0,1]防止标注越界 xc min(max((xmin xmax) / 2 / w, 0), 1) yc min(max((ymin ymax) / 2 / h, 0), 1) bw min(max((xmax - xmin) / w, 0), 1) bh min(max((ymax - ymin) / h, 0), 1) 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)) class_map {mouse: 0} for xml_path in glob.glob(./annotations/*.xml): stem os.path.splitext(os.path.basename(xml_path))[0] voc_to_yolo(xml_path, f./labels/{stem}.txt, class_map)逻辑说明class_map把类别名映射到 id单类就是{mouse: 0}。归一化后我加了min(max(...,0),1)裁剪因为标注里偶尔会出现 xmax 超出图片宽度的脏数据不裁的话 YOLO 训练会报坐标越界警告。保留 6 位小数是 ultralytics 的常见精度够用。参数上如果你有多个类别class_map 按字母序或自定义序排好训练时的 data.yaml 里 names 顺序必须和这里一致否则类别全错位。3. 用这份数据训 YOLOv8从 data.yaml 到第一次收敛3.1 目录组织与 data.yaml 写法ultralytics 对目录结构有约定最省事的组织方式是 images 和 labels 平级各自再分 train/val。这份数据没有官方划分你得自己切。常见做法是 8:2 或 9:14107 张按 8:2 就是约 3285 训练、822 验证。切分时注意增强图和原图要打散别让增强图全落进验证集否则验证指标虚高。dataset/ ├── images/ │ ├── train/ # 约 3285 张 jpg │ └── val/ # 约 822 张 jpg └── labels/ ├── train/ # 对应 txt └── val/data.yaml 内容path: ./dataset train: images/train val: images/val nc: 1 names: [mouse]逻辑说明path是数据集根目录train/val是相对 path 的路径ultralytics 会自动把images替换成labels去找同名 txt所以目录名必须严格是 images 和 labels。nc是类别数这里 1。names顺序必须和 txt 里的 class_id 对应单类无所谓多类时错一个全盘皆输。参数上如果你把 val 也当测试集用可以再加一个test:字段但这份数据量不大我一般只切 train/val验证集兼做测试。3.2 训练命令与关键超参环境上ultralytics 装好、CUDA 可用就能开跑。下面这条命令是我在单卡 8G 显存上跑这份数据的常用配置。yolo detect train \ data./dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ workers4 \ project./runs \ namemouse_v8n逻辑说明modelyolov8n.pt用 nano 版起步单类小目标够用显存紧张就它。imgsz640是默认输入尺寸老鼠在图中占比小的话可以试 960但显存翻倍。batch16在 8G 卡上 640 尺寸基本能撑住爆显存就降到 8。lr00.01是 SGD 的初始学习率ultralytics 默认会自适应但单类数据集我习惯手动压到 0.01 避免早期震荡。patience20是早停20 轮验证指标不升就停省时间。workers4是数据加载线程按 CPU 核数调太高反而抢资源。跑起来后重点盯三个输出box_loss 是否稳定下降、mAP50 在第几轮开始抬头、有没有出现corrupt image或ignoring corrupt警告。这份数据有增强图如果增强做得糙比如过度裁剪导致目标只剩半个loss 会抖这时候别急着调参先抽样看几张增强图长什么样。3.3 验证与推理确认模型真的学到了老鼠训完在验证集上跑一次看 mAP50 和 mAP50-95。单类数据集 mAP50 上 0.8 算正常低于 0.6 就得查数据。推理单张图yolo detect predict \ model./runs/mouse_v8n/weights/best.pt \ source./test_imgs \ conf0.25 \ saveTrue逻辑说明conf0.25是置信度门限低于它的框不输出。老鼠检测里这个值很关键——调高到 0.5 漏检增多调到 0.1 误检增多。我一般先用 0.25 看整体再针对具体场景微调。saveTrue把带框结果存下来方便肉眼核对。参数上如果部署到边缘设备还要考虑iou阈值做 NMS默认 0.7密集鼠群场景可以降到 0.5 减少重叠框。4. 避坑与排查这份增强数据集最容易翻车的五个地方4.1 验证集里混进增强图指标虚高现象验证 mAP 冲到 0.95一上真实场景就崩。原因切分时没打散增强图和它的原图分别落进了 train 和 val模型等于在验证集上见了近亲。解决切分前先按原图分组同一原图及其所有增强变体必须整体进 train 或整体进 val。如果数据里没有原图-增强图的对应关系就按文件名前缀或相似度聚类后再切。4.2 增强过度导致目标残缺loss 不收敛现象训练前几十轮 box_loss 剧烈震荡mAP 长期趴地。原因这份数据目测过半是增强图如果增强包含大幅随机裁剪老鼠可能只剩一条尾巴或半个身子标注框还按原图算框和实际目标对不上。解决抽样可视化一批增强图用脚本把 txt 框画回图上肉眼确认框是否贴合。残缺严重的样本直接剔除或者对增强图降采样使用。4.3 坐标越界引发训练警告甚至报错现象训练日志刷WARNING: corrupt image/label或坐标超出 [0,1]。原因VOC 转 YOLO 时没做裁剪xmax 超过图片宽度归一化后大于 1。解决转换脚本里加min(max(...,0),1)裁剪见 2.3 节或者训练前跑一遍校验脚本把所有越界框修正或删除。别忽略这个警告越界框会让对应样本的 loss 计算异常。4.4 类别名大小写或空格不一致现象data.yaml 里写mouse但 xml 里是Mouse或mouse带尾空格导致类别匹配失败或框数为 0。原因labelImg 手工标注时输入不规范。解决用 2.2 节的脚本先收集类别集合strip()后确认唯一。如果发现多种写法统一清洗 xml 和 txt 后再训练。单类数据集这个问题隐蔽因为 nc1 时错位不一定报错但框可能被静默丢弃。4.5 图片与标注文件名不同步现象训练时提示找不到 label或某些图被跳过。原因jpg 和 txt 的 stem 不一致比如图片叫firc_mouse_754.jpgtxt 叫firc_mouse_754_1.txt。解决写个脚本比对 images 和 labels 目录的文件名集合差集就是问题文件。这份数据文件名规律是firc_mouse_数字正常应该严格对应但解压或复制过程中可能出岔子训练前核对一遍。5. 把 4107 张用出 8000 张的效果子集筛选与难例挖掘数据量不算大单类 14321 框平均每张 3.5 个目标属于中等密度。想让模型更稳与其盲目加 epoch不如把数据用精。我一般会做两件事一是按框的尺寸分布筛子集二是训完一轮后做难例挖掘。先看尺寸分布。老鼠检测里目标尺度差异大近景大、远景小。用下面脚本统计每个框的相对面积画个直方图你就能知道数据偏向哪种尺度。import glob import numpy as np areas [] for txt in glob.glob(./labels/train/*.txt): with open(txt) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue _, _, _, w, h map(float, parts) areas.append(w * h) # 相对面积 areas np.array(areas) print(框数:, len(areas)) print(面积分位:, np.percentile(areas, [10, 50, 90])) print(小目标(0.01)占比:, (areas 0.01).mean())逻辑说明YOLO 的 w、h 是归一化宽高乘积就是相对面积。np.percentile看 10/50/90 分位如果 90 分位还很小说明数据以小目标为主训练时imgsz要往上提或者用 P2 小目标检测头。areas 0.01占比高的话640 输入下这些小目标可能只剩几个像素得考虑切图训练。难例挖掘的做法用训好的 best.pt 在训练集上推理一遍把置信度低但实际有框的图挑出来这些就是模型还没学好的难例。常见做法是设conf0.1跑推理对比预测框和真值框的 IoUIoU 低于 0.3 的样本单独存一个列表下一轮训练时对这些样本过采样或者人工检查是不是标注有问题。这一步能把 mAP 再往上推几个点比单纯加数据划算。最后说个习惯。这份数据带增强我每次用之前都会强制走一遍抽样可视化——随机抽 20 张把 txt 框画回原图肉眼过一遍。有次就是靠这一步发现某批增强图的框整体偏移了十几个像素要是直接开训loss 曲线看着正常模型却学了个偏移的框。从那以后我每次拿到带增强的数据集都先画图确认再谈训练。希望帮到你。本文还有配套的精品资源点击获取