ARTICLE DETAIL

资讯详情

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

夜间老鼠检测数据集:316张VOC+YOLO双格式小样本训练实战

夜间老鼠检测数据集:316张VOC+YOLO双格式小样本训练实战 简介本资源为夜间老鼠检测数据集面向从事目标检测算法训练与验证的开发者、学生及科研人员可用于夜间场景下的老鼠识别模型训练与效果评估。数据集采用Pascal VOC与YOLO双格式标注包含316张jpg图片及对应的316个xml和316个txt标注文件标注类别为laoshu共565个矩形框使用labelImg工具完成标注另附1个mp4视频文件供参考。压缩包为7z格式共951个文件以txt、xml、jpg和mp4为主整体约91.81MB目录结构清晰便于直接接入主流检测框架。目前已有312人学习下载。读者可获得完整标注数据、双格式标签及视频素材快速开展夜间老鼠检测实验节省数据采集与标注成本适合作为课程设计、毕业设计或算法验证的基础数据。1. 夜间老鼠检测数据集316 张 VOCYOLO 双格式到底能训出什么夜里十一点仓库管理员发来一段监控货架底部有东西在动但画面糊成一团人眼盯半天也分不清是老鼠还是光影。这种场景下通用 YOLO 模型基本是废的——COCO 里压根没有“老鼠”这个类夜间低照度、红外偏色、目标小而暗三个条件叠在一起模型要么漏检要么把纸箱角当目标。我拿到这个标题时第一反应就是316 张、1 类别、VOCYOLO 双格式、外加 1 个视频文件这套组合不是拿来刷榜的是拿来快速验证“夜间鼠患监测”这条链路能不能跑通的。它解决的核心问题很具体让你在半天内拥有一份能直接喂给 YOLOv8/v11 的夜间老鼠检测数据不用从零标注也不用纠结格式转换。适合谁做仓储、餐饮后厨、粮库、实验室动物房监控的算法同学以及想拿一个真实小样本练手“小目标夜间”调参的 YOLO 入门者。316 张确实少但小样本微调恰恰是边缘部署里最常见的起点——你不可能每个现场都攒几千张图。VOC 和 YOLO 两种格式同时给意味着你既能用 PyTorch 系训练也能直接丢进 LabelImg 复查标注省掉一次格式转换的玄学环节。视频文件那 1 个我一般拿它做推理演示和时序连续性验证比单帧图片更能看出模型在真实监控流里的抖动。2. 拆开这个 7zVOC 与 YOLO 双格式的目录结构与字段含义2.1 VOC 格式的 XML 里到底存了什么VOC 格式的核心是每张图对应一个 XML里面记录了图像尺寸、目标类别和边界框的绝对像素坐标。夜间老鼠检测只有 1 个类别所以 XML 里name字段会反复出现同一个词通常是mouse或rat具体以你解压后看到的为准。关键字段是bndbox下的xmin/ymin/xmax/ymax这四个值是左上角和右下角的像素坐标不是归一化的。很多人第一次转 YOLO 时翻车就是因为把 VOC 的绝对坐标直接当归一化坐标用了训练时 loss 直接炸成 NaN。annotation folderimages/folder filenamenight_mouse_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namemouse/name bndbox xmin845/xmin ymin612/ymin xmax903/xmax ymax658/ymax /bndbox /object /annotation上面这段 XML 里老鼠框只有 58×46 像素在 1920×1080 画面里属于典型小目标。YOLO 默认 640 输入下这个框会被缩放到约 19×15 像素已经接近 P3 特征图的感受野下限。所以训练时imgsz不建议低于 640最好上 960 或 1280否则召回率会很难看。2.2 YOLO 格式的 txt 与 data.yaml 怎么对应YOLO 格式每张图对应一个 txt每行是class_id x_center y_center width height全部归一化到 0~1。1 类别意味着 class_id 恒为 0。data.yaml 里要写清train、val路径和names列表。常见做法是把 316 张按 8:2 切分252 张训练、64 张验证如果视频文件要抽帧补充可以再切一部分做测试集。# data.yaml path: ./night_mouse_dataset train: images/train val: images/val nc: 1 names: 0: mouse这里nc: 1必须和 txt 里的 class_id 范围一致。我见过有人把nc写成 80 却只给 1 类标注训练不报错但模型永远学不会——因为输出层维度对不上梯度传不回去。另外path用相对路径时训练脚本的工作目录必须和 yaml 同级否则 Ultralytics 会找不到图。2.3 视频文件在验证环节的用法那个视频文件不是拿来训练的是拿来验证时序稳定性的。单帧检测再准如果连续帧里框忽大忽小、忽有忽无监控场景就没法用。我一般用 OpenCV 抽帧后跑推理统计“连续 5 帧内同一目标框 IoU 波动”和“漏检帧占比”。夜间红外视频常有亮度突变模型容易在某一帧集体翻车这时候看视频比看 mAP 更直观。import cv2 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) cap cv2.VideoCapture(night_mouse_clip.mp4) frame_id 0 while cap.isOpened(): ret, frame cap.read() if not ret: break # 每 5 帧抽一帧降低冗余 if frame_id % 5 0: results model(frame, imgsz960, conf0.35) for box in results[0].boxes: x1, y1, x2, y2 box.xyxy[0].tolist() cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.imshow(night_mouse, frame) if cv2.waitKey(1) 0xFF ord(q): break frame_id 1 cap.release() cv2.destroyAllWindows()这段代码里imgsz960是为了匹配小目标conf0.35比默认 0.25 高因为夜间误检多宁可漏一点也别让纸箱角频繁触发。抽帧间隔 5 是平衡速度和连续性的经验值实际部署可以按帧率调整。3. 从 7z 到可训练VOC 转 YOLO 的脚本与四个边界坑3.1 转换脚本绝对坐标归一化与类别映射拿到 VOC 的 XML 后第一步是遍历所有 XML读出宽高和 bbox再除以宽高得到归一化值。类别名到 id 的映射要固定1 类别直接映射为 0。下面脚本会同时生成 images 和 labels 目录并打印每张图的框数量方便你快速发现空标注或异常框。import os import xml.etree.ElementTree as ET from pathlib import Path VOC_DIR Path(VOCdevkit/VOC2007) OUT_IMG Path(night_mouse_dataset/images/all) OUT_LBL Path(night_mouse_dataset/labels/all) OUT_IMG.mkdir(parentsTrue, exist_okTrue) OUT_LBL.mkdir(parentsTrue, exist_okTrue) class_map {mouse: 0} # 按实际 XML 里的 name 修改 for xml_file in (VOC_DIR / Annotations).glob(*.xml): tree ET.parse(xml_file) 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 bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.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.0 / w yc (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_map[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if not lines: print(f空标注: {xml_file.name}) continue img_name xml_file.stem .jpg src_img VOC_DIR / JPEGImages / img_name if src_img.exists(): import shutil shutil.copy(src_img, OUT_IMG / img_name) (OUT_LBL / (xml_file.stem .txt)).write_text(\n.join(lines)) else: print(f缺图: {img_name})脚本里class_map必须和 XML 里的name完全一致大小写敏感。xmin/ymin裁剪到 0 是防止标注员手抖画出负坐标xmax/ymax裁剪到宽高是防止越界。空标注直接跳过并打印方便你回头补标或删图。3.2 坑一归一化时用了错误的宽高现象是训练 loss 从第一轮就 NaN或者框全部挤在图像左上角。原因通常是 XML 里的width和height读反了或者用了depth字段当宽高。解决方法是打印前 10 张图的宽高和 bbox肉眼核对一遍。夜间监控图常见 1920×1080 和 1280×720 两种如果宽高写反归一化后的 x 会超过 1YOLO 虽然不报错但学不到东西。3.3 坑二类别名多了空格或大小写不一致现象是转换后 labels 目录大量为空或者 class_id 全是 0 但模型只对部分图有响应。原因是 XML 里写的是Mouse、mouse带尾空格或rat而 class_map 里只有mouse。解决方法是先跑一遍统计脚本把所有name的原始值打印出来确认唯一值后再写映射。我一般会加.strip()并统一转小写。3.4 坑三图片和 XML 文件名不对应现象是转换脚本报“缺图”或者 labels 生成了但 images 目录是空的。原因是 VOC 里图片可能是.png而 XML 里写.jpg或者文件名有_001后缀差异。解决方法是先ls两个目录用 Python 的set求差集把不匹配的文件名列出来手动改。316 张量不大十分钟能核完。3.5 坑四训练集和验证集划分时随机种子没固定现象是每次跑训练 mAP 波动好几个点你以为模型不稳定其实是划分变了。解决方法是划分时固定random.seed(42)并且把划分结果写成 txt 文件训练脚本直接读 txt。这样下次复现时数据完全一致调参才有可比性。import random from pathlib import Path all_imgs sorted(Path(night_mouse_dataset/images/all).glob(*.jpg)) random.seed(42) random.shuffle(all_imgs) split int(len(all_imgs) * 0.8) train_imgs all_imgs[:split] val_imgs all_imgs[split:] Path(night_mouse_dataset/train.txt).write_text(\n.join(str(p) for p in train_imgs)) Path(night_mouse_dataset/val.txt).write_text(\n.join(str(p) for p in val_imgs))固定种子后每次实验的数据划分完全一致mAP 变化才能归因到模型和超参上。4. 316 张小样本夜间训练YOLOv8 参数怎么设才不翻车4.1 为什么选 YOLOv8n 而不是更大模型316 张图、1 类别数据量决定了你只能用轻量模型。YOLOv8n 参数量约 3.2M在 640 输入下单卡 1080p 能跑到 100 FPS 以上边缘盒子也能部署。用 YOLOv8x 只会过拟合验证集 loss 先降后升mAP 卡在 0.5 上不去。我一般先用 n 跑通链路确认数据没问题后再考虑换 s 或 m但小样本下提升非常有限。yolo detect train \ datanight_mouse_dataset/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz960 \ batch8 \ lr00.001 \ lrf0.01 \ warmup_epochs5 \ cos_lrTrue \ patience30 \ seed42 \ projectruns/night_mouse \ namev8n_960imgsz960是小目标的关键640 下很多老鼠框只剩十几个像素特征图根本抓不住。batch8是 8G 显存下的安全值显存够可以上 16。lr00.001比默认 0.01 小因为小样本微调不需要大学习率否则前期 loss 震荡。patience30是早停150 轮里如果 30 轮没提升就停省时间。cos_lrTrue让学习率余弦衰减比阶梯衰减更平滑。4.2 夜间低照度下的数据增强怎么开YOLO 默认增强里hsv_v调亮度、hsv_s调饱和度对夜间图很关键。但夜间红外图本身色彩信息少饱和度增强过头会引入伪影。我一般把hsv_v开到 0.5hsv_s降到 0.3hsv_h保持 0.015。另外mosaic1.0在小样本下能显著提升泛化但最后 20 轮建议关掉让模型适应真实单图分布。yolo detect train \ datanight_mouse_dataset/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz960 \ batch8 \ lr00.001 \ hsv_h0.015 \ hsv_s0.3 \ hsv_v0.5 \ mosaic1.0 \ close_mosaic20 \ seed42close_mosaic20表示最后 20 轮关闭 mosaic这是 Ultralytics 的内置参数不用自己写回调。夜间图如果偏绿或偏紫可以在数据加载时加一个灰度化概率但 316 张里如果全是红外灰度化反而丢信息建议先看视频文件确认色彩分布。4.3 验证阶段看哪些指标才不被 mAP 骗mAP50 在 1 类别小样本下容易虚高因为负样本少。我一般同时看precision、recall和mAP50-95。夜间场景 recall 比 precision 重要漏检一只老鼠可能意味着整批货被啃。如果 recall 低于 0.7优先降conf阈值到 0.2 再测或者把imgsz提到 1280。另外看混淆矩阵确认没有把背景类大量误判成 mouse。yolo detect val \ modelruns/night_mouse/v8n_960/weights/best.pt \ datanight_mouse_dataset/data.yaml \ imgsz960 \ conf0.2 \ iou0.5 \ plotsTrueconf0.2是验证时故意放低看模型在宽松阈值下的召回上限。plotsTrue会生成 PR 曲线和混淆矩阵存到 runs 目录下。如果 PR 曲线在 recall 0.6 之后断崖说明模型对某些夜间光照条件完全没学到需要补这类样本。5. 避坑与排查夜间老鼠检测最常见的 5 个翻车现场5.1 现象训练 loss 正常下降但验证 mAP 始终为 0原因验证集路径写错或者 val.txt 里的图片路径是绝对路径而训练脚本在另一台机器上跑。YOLO 找不到验证图时不会报错直接跳过验证mAP 显示 0。解决训练前用ls确认 val.txt 里每一行路径都能访问或者把 data.yaml 里的val改成绝对路径。5.2 现象模型把红外画面里的亮斑全部框成老鼠原因夜间红外图有反光点标注时没标但模型学到了“亮老鼠”的捷径。解决在数据增强里加随机亮度抖动和随机遮挡同时补标 20~30 张纯背景图作为负样本。YOLO 支持空 txt 作为负样本把背景图放进训练集即可。5.3 现象推理时框抖动严重视频里目标一闪一闪原因单帧检测阈值卡在边界相邻帧置信度在 0.3 附近波动。解决推理时加跟踪器比如 ByteTrack用轨迹连续性平滑框。或者简单点对连续 3 帧的框做 NMS 后再输出。Ultralytics 内置model.track()可以直接用。5.4 现象316 张训练集跑 300 轮后严重过拟合原因数据量太小模型把训练集背下来了。解决早停patience30必须开另外加weight_decay0.0005和dropoutYOLOv8 分类头有 dropout 参数。如果还过拟合冻结 backbone 前 10 层只训检测头。5.5 现象转成 TensorRT 后精度掉 10 个点原因TensorRT 默认 FP16 量化小目标在低精度下特征丢失。解决导出时用halfFalse保持 FP32或者用int8False先排除量化影响。如果必须 FP16把imgsz提到 1280 补偿精度损失。yolo export \ modelruns/night_mouse/v8n_960/weights/best.pt \ formatengine \ imgsz960 \ halfFalse \ device0halfFalse是夜间小目标部署的后悔药速度换精度边缘盒子如果算力够优先保召回。6. 把 316 张用到极致视频抽帧补标与难例回灌的闭环316 张静态图训出来的模型直接上监控视频大概率会漏。我的习惯是先用训练好的模型跑一遍那个视频文件把置信度在 0.1~0.35 之间的帧全部抽出来这些就是“难例”。然后人工筛一遍把真正有老鼠但没检出的帧补标再加进训练集。一轮回灌大概能补 30~50 张mAP 通常能涨 3~5 个点。这个闭环跑两轮316 张就能等效到 400 张以上的效果。import cv2 from ultralytics import YOLO from pathlib import Path model YOLO(runs/night_mouse/v8n_960/weights/best.pt) cap cv2.VideoCapture(night_mouse_clip.mp4) hard_dir Path(hard_examples) hard_dir.mkdir(exist_okTrue) frame_id 0 saved 0 while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, imgsz960, conf0.1, verboseFalse) boxes results[0].boxes # 有框但置信度低或者框数量异常都算难例 if len(boxes) 0: confs boxes.conf.tolist() if any(0.1 c 0.35 for c in confs) or len(boxes) 5: cv2.imwrite(str(hard_dir / fhard_{frame_id:05d}.jpg), frame) saved 1 frame_id 1 cap.release() print(f保存难例 {saved} 张)这段脚本把低置信度和框数量异常的帧都存下来框数量异常通常意味着模型在背景上乱触发。存完后用 LabelImg 过一遍只保留真有老鼠的帧补标后加入训练集。注意难例回灌后要重新划分训练验证集别把难例全塞进训练集导致验证集分布偏移。验证闭环是否有效不能只看 mAP。我一般会固定一段 200 帧的视频统计“漏检帧数”和“误检帧数”。回灌前漏检 40 帧、误检 25 帧回灌后如果漏检降到 20 帧以下、误检不涨说明补标方向对了。如果误检涨了说明难例里混入了太多背景帧需要重新筛。最后说个血泪经验夜间老鼠检测别追求单帧完美监控场景看的是连续 10 秒内能不能稳定触发告警。我一般把告警逻辑写成“连续 3 帧检出且框 IoU 大于 0.5 才上报”这样即使单帧有抖动也不会频繁误报。316 张数据不大但把视频闭环跑通足够你在真实仓库里做一轮 PoC 了。希望帮到你。本文还有配套的精品资源点击获取
返回列表