简介:这是一份面向目标检测任务的老鼠图像数据集,共包含约1100张已标注图片,采用YOLO标注格式,类别仅“老鼠”一类,适合需要训练老鼠检测模型、开展YOLO系列改进实验或进行迁移学习的研究者与开发者。资源包共2000个文件,其中920张jpg图片与1078个xml标注文件构成数据主体,另有1个可视化python脚本和1个json配置文件,整体约171.6MB。数据已做好训练集与测试集划分,使用者运行show脚本即可直观检查每张图片的标注框是否符合预期,省去自行整理与格式转换的步骤。目前已有91人学习下载,可直接用于目标检测入门实践、算法精度对比,以及实验室监测、智慧仓储等需要自动识别老鼠的场景。若需做YOLOv5改进验证,也可参考作者主页的配套实战内容进行延伸。
1. 老鼠图像检测:为什么说你的 1100 张标注图还差一道工序
做动物行为分析、药物毒理实验或者仓储鼠害监测的团队,大概率都卡在同一个环节:数据已经标好了,YOLO 格式的 txt 文件安安静静躺在 labels 目录里,但训练出来的模型要么把老鼠和背景糊在一起,要么出现了大量误检。拿到一批约 1100 张、已经标注好的老鼠图像数据集,是不是直接扔进 YOLOv8 就能跑出理想效果?答案是否定的。1100 张这个量级很微妙——比学术基准数据集小一个数量级,但比纯手工采集强很多,恰好是“迁移学习能救、硬train from scratch 必翻车”的分界线。
我见过不少翻车案例:有人拿 1100 张直接训 300 轮,损失函数曲线看似收敛了,实际验证集 mAP50 只有 0.6 出头;还有人不做任何数据检查,训练到一半才发现 label 里有空标签文件,损失直接 NaN。本文就顺着“YOLO 标注格式解析 → 数据验证与划分 → 训练参数调优 → 小目标检测优化”这条链路,把 1100 张老鼠数据集从标注文件变成可交付的检测模型,并重点处理老鼠这种“小目标+背景杂乱+形态多变”的特殊场景。
2. YOLO 标注格式底层解析:txt 文件里的五个数字怎么读
很多人用 LabelImg 或者 X-AnyLabeling 标注完就完事了,对 labels 目录下的 txt 文件没有完整认识。老鼠数据集既然声明是 YOLO 格式,那必须先理解它的物理存储结构。
2.1 标签文件的原子结构
YOLO 格式的每个 txt 文件与一张图片一一对应,文件名相同、扩展名不同。比如IMG_0231.jpg对应IMG_0231.txt,内容长这样:
0 0.328125 0.53125 0.151367 0.328125 0 0.712891 0.223632 0.113281 0.189453每一行代表一个标注框,共 5 个数字:class_id x_center y_center width height,注意这里的前 4 个几何值全部是相对原图宽高的归一化结果。比如第一行里的0.328125等于 bbox 中心的像素 x 坐标除以图像宽度,不是像素值。类别 ID 从 0 开始计数,如果 cats 目录下定义了names: ['mouse'],那么第 0 类就是老鼠;如果数据集同时标了 rat 和小鼠两种,['mouse', 'rat']中 0 是老鼠、1 是大鼠。
动手解析这类文件,我一般用 Python 的os和numpy直接扫描:
import os import numpy as np def parse_yolo_label(txt_path, img_width, img_height): boxes = [] with open(txt_path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) != 5: print(f"跳过异常行: {txt_path} -> {line.strip()}") continue cls_id = int(parts[0]) x_center, y_center, w, h = map(float, parts[1:]) # 还原像素坐标 x1 = (x_center - w / 2) * img_width y1 = (y_center - h / 2) * img_height x2 = (x_center + w / 2) * img_width y2 = (y_center + h / 2) * img_height boxes.append((cls_id, x1, y1, x2, y2)) return np.array(boxes, dtype=object)逻辑说明:上面的代码先逐行拆分出 5 个字段,然后做一步“归一化转像素坐标”的换算。为什么要还原像素坐标?因为训练时的损失计算用的是归一化坐标,但画框、评估、计算 IoU 时需要像素级坐标,这步换算不能省。第一行字段0代表类别索引,后面四个浮点数为归一化中心点和宽高。如果某行不足 5 个字段说明标注软件导出时截断了,这类脏数据应在训练前剔除。
2.2 反常识:YOLO 坐标只能用归一化值吗
严格来说,YOLOv5 之后官方推荐归一化坐标,但不是因为归一化本身有多高级,而是为了适配多分辨率输入。如果你的老鼠图像尺寸不一致——比如一部分是 640x480 监控截图,另一部分是 1920x1080 科研相机图像——归一化标签可以无缝执行 resize 而无需重新标注。反常识的点在于:很多人以为 YOLO 训练器会自动做 resize,所以标注尺寸无所谓,但训练器改的是输入张量,你的归一化标签乘以新尺寸后,框的比例关系必须与真实目标一致,如果原图被不等比例压缩,那只归一化坐标也无法拯救形变。
1100 张老鼠数据里经常混着不同来源的图像,我处理这种数据集时第一个动作是统计尺寸分布:
from PIL import Image import glob shape_counter = {} for img_path in glob.glob('images/*.jpg'): shape = Image.open(img_path).size shape_counter[shape] = shape_counter.get(shape, 0) + 1 for shape, cnt in sorted(shape_counter.items(), key=lambda x: -x[1]): print(shape, cnt)逻辑说明:统计每一种(width, height)出现了多少次,一眼就能看出数据是否存在多分辨率混用。如果主要尺寸集中在一种,训练时统一 resize 到 640x640,几何失真可以忽略;如果尺寸分布散,最好按最长边等比缩放加填充,或者用 YOLOv8 的rect模式保持宽高比。参数说明:rect=True会让训练器按照批次自动选择最接近的宽高比,减少图像形变带来的 bbox 漂移。
2.3 标注格式匹配数据集的真伪校验
拿到“约 1100 张数据”,不要轻信手机号。/images 目录有 1100 张 jpg,/labels 目录是不是也有整整 1100 个 txt?缺失一个标签文件,就意味着这张图在训练时被静默跳过或报错。先跑一次硬核对:
img_files = set(os.path.splitext(os.path.basename(p))[0] for p in glob.glob('images/*.jpg')) label_files = set(os.path.splitext(os.path.basename(p))[0] for p in glob.glob('labels/*.txt')) print(f"无标签文件 {len(img_files - label_files)} 张", img_files - label_files) print(f"无对应图片 {len(label_files - img_files)} 个", label_files - img_files)逻辑说明:用集合做差集,找出只有图没有标签、或只有标签没有图的文件名。另外还要检查 txt 是否为空文件,空文件在 YOLO 训练中的表现是“这张图不参与正样本匹配”,相当于背景样本,多了会让模型偏向于输出低置信度。检查脚本:for lp in label_files: if os.path.getsize(os.path.join('labels', lp + '.txt')) == 0: print(lp)。
一套流程走完,你才真正拿到了“可被 YOLO 消费”的数据。接下来才是划分、训练和调优。
3. 1100 张老鼠数据集的 train/val 划分与样本验证
上一章解决了“标签对不对、能不能读”的问题。这一章解决“怎么划分、划完要不要动”的问题。
3.1 手动 stratify 划分:按目标数量分桶采样
1100 张图如果用train_test_split(test_size=0.2)纯随机切,极有可能把包含大量密集老鼠的样本都砸进训练集,验证集全是没有老鼠的空场景——然后 val mAP 虚高。更可靠的做法是按图像中标注框数量分桶,再做分层采样:
from sklearn.model_selection import train_test_split import numpy as np import glob, os img_paths = sorted(glob.glob('images/*.jpg')) density_bins = [] for p in img_paths: label_path = p.replace('images', 'labels').replace('.jpg', '.txt') bbox_count = 0 if os.path.exists(label_path): with open(label_path) as f: bbox_count = sum(1 for line in f if len(line.strip().split()) == 5) if bbox_count <= 1: density_bins.append('single') elif bbox_count <= 4: density_bins.append('sparse') elif bbox_count <= 10: density_bins.append('dense') else: density_bins.append('crowd') train_idx, val_idx = train_test_split( np.arange(len(img_paths)), test_size=0.2, random_state=42, stratify=density_bins ) print(f"train: {len(train_idx)}, val: {len(val_idx)}")逻辑说明:stratify=density_bins让训练集和验证集里“单目标图、稀疏图、密集图”的占比与全数据集保持一致。对于老鼠检测这种目标尺度变化大的任务,validation 必须包含拥挤场景,否则模型在小目标上的表现没有参考意义。random_state 固定为 42,保证每次划分结果一致,便于后续对比实验。划分完成后写一个data.yaml:
path: /absolute/path/to/mouse_dataset train: images/train val: images/val names: 0: mouse参数说明:path建议写绝对路径,规避 YOLO 在不同运行目录下相对路径解析失败的问题。如果数据集里只有一类老鼠,names段只留0: mouse;如果有大鼠小鼠区分,顺序与训练时 txt 标签里的 class_id 严格对应。
3.2 划分后的框尺寸分布审计
划分完不等于就绪。老鼠目标在全图占比通常很小,10 米开外的监控画面里一只老鼠可能只有 32x32 像素。这种小目标在 YOLO 的 640x640 输入下,对应到下采样后的特征图上,可能只覆盖 1~2 个网格,极易漏检。我一般用脚本统计 bbox 尺寸分布:
from PIL import Image import pandas as pd widths, heights, areas = [], [], [] for img_path in train_img_paths: img = Image.open(img_path) w, h = img.size label_path = img_path.replace('images/train', 'labels').replace('.jpg', '.txt') with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue _, xc, yc, bw, bh = map(float, parts) widths.append(bw * w) heights.append(bh * h) areas.append(bw * bh * w * h) df = pd.DataFrame({'w': widths, 'h': heights, 'area': areas}) print(df.describe(percentiles=[.25, .5, .75, .9, .95])) print(f"面积小于 32x32 的框占比: {(df.area < 1024).mean():.2%}")逻辑说明:如果(df.area < 1024).mean()超过 20%,你的数据集中小目标占比很高,后面章节中的优化手段就不是可选项而是必选项。这一步的产出决定训练参数——小目标为主的数据集,imgsz不建议用 640 以下,anchor相关改进也要提前规划。
3.3 训练前可视化抽检:一副图 34 张画布
跑训练之前,把划分后的训练集随机抽 20 张,画上标注框,人眼过一遍。这个步骤能发现大多数自动化检查漏不掉的问题:标注框偏大/偏小、类别串了、旋转目标没包紧、漏标严重等等。用 OpenCV 画框:
import cv2 import random random.seed(7) sample = random.sample(train_img_paths, 20) for idx, path in enumerate(sample): img = cv2.imread(path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) label_path = path.replace('images/train', 'labels').replace('.jpg', '.txt') h, w = img.shape[:2] with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls_id, xc, yc, bw, bh = map(float, parts) x1 = int((xc - bw/2) * w) y1 = int((yc - bh/2) * h) x2 = int((xc + bw/2) * w) y2 = int((yc + bh/2) * h) color = (255, 0, 0) if cls_id == 0 else (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.imwrite(f'viz/{idx:03d}.jpg', img[:, :, ::-1])逻辑说明:这里用imwrite前把 RGB 又转回 BGR,避免保存后颜色通道错乱。验证目的不是欣赏标注质量,而是找“框不在目标上”“框住了两只老鼠”“某些目标明显漏标”的问题。这一步最多花 20 分钟,能把你从“模型训练完才发现数据有问题”的坑里拽出来——500 轮训完再去重新标注数据,付出的是天级时间成本。
4. YOLO 训练环境配置与关键参数调试:从模型结构到损失函数
数据准备好了,轮到 YOLO 本身。当前 yolo 版本已经迭代到了 v11,主流稳定版是 yolov8 和 yolov11。很多初学者选 YOLO 版本只看“最新”,但实际项目选型要看硬件条件、显存和检测精度需求。
4.1 环境安装与硬件选型
安装 YOLO 训练环境最顺滑的方式还是 Ultralytics 包:
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/source如果机器上有 NVIDIA 显卡,安装后执行python -c "import torch; print(torch.cuda.is_available())",输出 True 即可。如果没有独显,或者 AMD 显卡用户跑 yolo,只能走 CPU 推理或训练路线——不是不能用,1100 张图在 CPU 上训练 300 轮,yolov8n 大约十几个小时,yolov8m 可能要三十小时往上。AMD 显卡跑 yolo 社区有 ROCm 兼容方案,但坑非常多,新人不建议在环境上耗时间,老老实实用 CPU 配小模型先把流程跑通。
版本选择上,如果你的显卡显存 6GB 以下,用yolov8n或yolov11n;8~12GB 显存可以上yolov8s或yolov8m;超过 16GB 再考虑yolov8l。1100 张训练集撑不起 yolo-l 以上的模型,强行上大模型只会过拟合。
4.2 训练命令与参数逐项解析
我基于 1100 张老鼠数据集跑过的稳定训练命令是:
yolo detect train model=yolov8n.pt data=/path/to/mouse_dataset/data.yaml \ imgsz=640 epochs=150 batch=16 device=0 \ optimizer=AdamW lr0=0.001 weight_decay=0.0005 \ patience=30 cache=ram workers=4 \ pretrained=True close_mosaic=10 seed=42逻辑说明:model=yolov8n.pt表示加载 COCO 预训练权重,这一步至关重要。1100 张数据从零训练无法收敛到可用精度,预训练权重提供了底层特征提取能力——边缘、纹理、颜色分布等低级特征在 COCO 上已经学好了,你的数据只需要教会模型“老鼠长什么样”。pretrained=True与下载的 .pt 文件配合使用。
imgsz=640是输入分辨率,老鼠属于小目标,不建议低于 640;epochs=150不是越多越好,配合patience=30在验证集指标连续 30 轮不上升时自动早停;batch=16是单卡显存决定的,显存不够时报 CUDA OOM 就减半。close_mosaic=10表示训练最后 10 轮关闭 mosaic 增强——mosaic 是把四张图拼成一张,虽然能增强泛化性,但会改变目标尺度分布,直接导致小目标性能退化,最后几轮关掉让模型适应真实分布。
优化器选择AdamW而不是默认的 SGD,是因为 AdamW 在中小数据集上收敛更稳,不容易出现 loss 震荡,对学习率的敏感度也低一些。
4.3 loss 曲线怎么读
训练跑起来后,不要傻等。观察三个指标:
第一个是train/box_loss,如果它持续下降但val/box_loss在某个点反弹,说明过拟合开始,patience会自动停;第二个是metrics/mAP50(B),这个值最终应该超过 0.8 才算数据可用;第三个是metrics/mAP50-95(B),它比 mAP50 更严格,要求预测框与真实框 IoU 从 0.5 到 0.95 平均——小目标数据集里 mAP50-95 通常只有 mAP50 的六到七成,这是正常的。
如果训练结束 mAP50 不升反降,常见原因是数据划分不均匀或者标签坐标本身有系统性偏移,回第 3 章重新可视化。
4.4 预测阶段的后处理参数
训练完预测时,后处理参数直接影响检出率:
from ultralytics import YOLO model = YOLO('runs/detect/train/weights/best.pt') results = model.predict( source='test_images/', conf=0.25, iou=0.45, imgsz=640, max_det=300 )# 解析结果 for r in results: boxes = r.boxes.xyxy.cpu().numpy() scores = r.boxes.conf.cpu().numpy() cls_ids = r.boxes.cls.cpu().numpy() for box, score, cls_id in zip(boxes, scores, cls_ids): print(f"class={int(cls_id)} conf={score:.2f} box={box.astype(int)}")逻辑说明:conf=0.25是置信度阈值,低于这个分数直接丢弃。老鼠场景误检一般发生在阴影或杂物背景上,如果发现误检过多,把 conf 抬到 0.35~0.4;如果漏检多,降到 0.15 再配合后续 NMS。iou=0.45是 NMS 的 IoU 阈值,鼠群密集场景建议降到 0.3,否则两个贴近的老鼠框会被合并成一个。
5. 小目标老鼠检测的进阶优化:红外小目标思路与 mAP50-95 提升
老鼠数据是典型的小目标主导场景,监控画面里目标可能只占整体面积的 1% 以下。这一部分把小目标优化的常用手段整合成一套实战方案,按优先级排。
5.1 P2 检测层:给模型加一层高分辨率特征图
YOLOv8 的默认检测头从 P3 开始(8 倍下采样),对于 16x16 像素以下的目标,这个层级的特征图只有 2x2 的响应区域,信息量严重不足。常见做法是自定义 yaml 文件增加 P2 层(4 倍下采样):
# my_yolov8s_p2.yaml nc: 1 depth_multiple: 0.33 width_multiple: 0.50 backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, Conv, [128, 3, 2]] - [-1, 3, C2f, [128, True]] - [-1, 1, Conv, [256, 3, 2]] - [-1, 6, C2f, [256, True]] - [-1, 1, Conv, [512, 3, 2]] - [-1, 6, C2f, [512, True]] - [-1, 1, Conv, [1024, 3, 2]] - [-1, 3, C2f, [1024, True]] - [-1, 1, SPPF, [1024, 5]] head: - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 6], 1, Concat, [1]] - [-1, 3, C2f, [512]] - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 4], 1, Concat, [1]] - [-1, 3, C2f, [256]] - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 2], 1, Concat, [1]] - [-1, 3, C2f, [128]] - [ -1, 1, Detect, [nc, [128, 256, 512, 1024]]]配置导出后训练:
yolo detect train model=/path/to/my_yolov8s_p2.yaml \ pretrained=yolov8s.pt data=/path/to/mouse_dataset/data.yaml \ imgsz=640 epochs=150 batch=16 device=0逻辑说明:在原有 P3/P4/P5 基础上增加了 P2 层,Detect 头的输出变成 4 个尺度。代价是计算量增加约 20%,推理变慢一些,但 mAP50-95 通常能提升 2~5 个点,小目标收益尤其明显。注意nc: 1要与你的类别数严格一致。
5.2 判断小目标检测模型的评价参数
小目标任务的评价不能只看 mAP50,要看 mAP50-95 和不同尺寸目标的分离指标。Ultralytics 训练结果里没有直接按尺寸拆分 mAP,可以自己评估:
from ultralytics import YOLO model = YOLO('runs/detect/train/weights/best.pt') metrics = model.val(data='data.yaml', split='val') map50 = metrics.box.map50 map50_95 = metrics.box.map print(f"mAP50: {map50:.3f}, mAP50-95: {map50_95:.3f}") # 分尺寸统计 for i, metric in enumerate(metrics.box.maps_per_class): print(f"class {i}: {metric:.3f}")逻辑说明:训练日志里通常只输出 mAP50-95 的两种口径,但实际部署时更关心 32x32 以下的小目标到底检出多少。
5.3 数据增强策略调整:翻转、马赛克与尺度抖动
前述close_mosaic=10之外,YOLO 的增强策略通过degrees, translate, scale, fliplr等参数控制。老鼠检测场景下我建议参数设置如下:
degrees=0.0 translate=0.1 scale=0.5 fliplr=0.5 mosaic=1.0 mixup=0.0逻辑说明:老鼠没有方向性旋转问题,degrees=0避免模型学会识别倒着的老鼠,浪费学习能力。scale=0.5提供 0.5~1.5 倍的尺度抖动,对老鼠远近不同带来的尺寸变化有帮助。mixup默认关闭,这类数据增强在两个类别以上时效果更好,单类目标场景开 mixup 反而会制造模糊标签。如果发现光照变化大导致误检,可以去掉颜色增强相关的hsv_h/hsv_s/hsv_v默认值,改为hsv_h=0.015, hsv_s=0.5, hsv_v=0.3让模型对颜色变化更鲁棒。
5.4 推理端切片检测:放大后用小图扫一遍全图
监控摄像头拍到的画面是 1920x1080,整图缩到 640 输入,一只 40x40 像素的老鼠在输入里只剩 13x13 像素。一个替代方案是切片推理:把大图切成 640x640 的 tile,每块独立预测,最后合并结果。
import cv2 import numpy as np from ultralytics import YOLO model = YOLO('best.pt') def detect_tiled(img_path, tile_size=640, overlap=0.2): img = cv2.imread(img_path) h, w = img.shape[:2] step = int(tile_size * (1 - overlap)) all_boxes = [] for y in range(0, h, step): for x in range(0, w, step): tile = img[y:y+tile_size, x:x+tile_size] pad_h = tile_size - tile.shape[0] pad_w = tile_size - tile.shape[1] if pad_h > 0 or pad_w > 0: tile = cv2.copyMakeBorder(tile, 0, pad_h, 0, pad_w, cv2.BORDER_CONSTANT) result = model.predict(tile, conf=0.25, imgsz=640, verbose=False)[0] box = result.boxes.xyxy.cpu().numpy() for b in box: b[0] += x; b[1] += y; b[2] += x; b[3] += y all_boxes.append(b) return np.array(all_boxes) if all_boxes else np.empty((0, 4))逻辑说明:overlap=0.2防止目标正好被切片线切碎,因为切碎的目标在单个 tile 里只有一半,置信度很低。切片后每个 tile 独立推理,再把坐标加回原图偏移量。这种方案的检测速度约为整图推理的 4~6 倍,但小目标召回率通常明显提升,适合离线分析监控录像。参数调整方向:如果目标更小,把tile_size降到 512,如果目标较大,升到 768 以减少重复计算。
5.5 单类模型的置信度阈值策略
只有老鼠一个目标类别时,误检主要来自背景假阳性,漏检主要来自低置信度的小目标。这里有个平衡技巧:把 conf 设低到 0.1 推理,随后按面积过滤——小于某个面积阈值的检测框大概率是背景噪声,直接丢弃;大于阈值的保留。面积阈值可以用训练集 bbox 面积分布的 5% 分位数作为参考值。具体数值我在项目中常用 900 像素(30x30)作为下限,根据相机距离做调整。配合前面章节的划分、训练参数,这套流程能从 1100 张老鼠数据直接产出一个可交付的小目标检测模型。
本文还有配套的精品资源,点击获取