免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

YOLO老鼠检测实战:从数据集准备到训练调参部署

YOLO老鼠检测实战:从数据集准备到训练调参部署 简介这是一份面向目标检测场景的老鼠数据集包含四千余张标注图像适配YOLO系列主流算法如第五、第七、第八、第九、第十及第十一代等版本。数据已经完成训练集、验证集和测试集的划分标签信息完整下载后即可直接用于模型训练与效果验证。压缩包内文件总数约两千个以VOC格式的XML标签文件为主同时提供YOLO格式的TXT标签文件。其中YOLO格式记录类别索引以及归一化后的目标中心坐标、宽度和高度VOC格式则保存目标边界框和类别名称两种格式分别存放方便在不同训练框架间按需选用省去格式转换时间。资源包整体约一百九十五兆目录结构清晰既适合刚接触目标检测的初学者快速上手也能作为算法对比实验的现成数据支撑目前已有近两百人学习浏览适合需要老鼠识别、动物检测或YOLO系列算法实战练习的开发者参考使用。1. 老鼠检测为什么值得用 YOLO 算法从头训一版做鼠情监测、实验室动物行为分析或仓储防鼠巡检的人大概率都遇到过同一个需求通用目标检测模型认得出猫狗却认不出镜头里快速窜过的老鼠。原因不复杂老鼠在画面里占比小、移动快、毛色与环境接近公开权重里几乎没有这类样本。最直接的做法不是调 API而是拿一批带标签的老鼠图像用 YOLO 算法训一个专用检测器。标题里这种 4089 张图像带标签的老鼠数据集单类别训练够用单卡一轮时间可控。下面把数据检查、格式转换、训练参数、调优和验证的路径走一遍适合已经跑通 YOLO 示例、第一次做自定义数据的工程师跟着做。2. 老鼠数据集的目录结构拆解与标签格式核对无论是拿到标题里这种 4089 张带标签的老鼠压缩包还是同事交接的目录第一步都不是急着写训练脚本。先把目录结构、文件数量、标签格式核对一遍这一步省下的时间远比花的力气多。下面按最常见的 images/labels 双目录结构来讲。2.1 先核对图像与标签的对应关系再开始训练常见做法是解压后看到两个平级目录images 放图像labels 放同名 txt。txt 每行对应一个标注框格式是 class x_center y_center width height五个数都用图像宽高归一化过。我一般会先跑两条命令把缺配对样本直接揪出来# 统计图像和 txt 标签总数期望都是 4089 ls images/*.jpg | wc -l ls labels/*.txt | wc -l # 找出有图无标签或有标签无图的样本 comm -23 (ls images | sed s/\.jpg$// | sort) (ls labels | sed s/\.txt$// | sort)第一条命令确认总数和标题描述一致第二条用 comm 求差集-23 表示只输出第一列独有的行也就是缺标签的图像名。如果输出为空说明配对完整如果有内容先看数量再决定处理方式。缺十张以内直接补标就行缺几十张说明打包时丢了标注需要回源头核对。空标签和越界坐标是“带标签”数据集里最常见的暗病光看文件名完全发现不了直接训练也不会报错只会悄悄拉低指标。用一段 Python 把 labels 目录整体扫一遍把异常样本一次性列出来再决定是修是删import numpy as np from pathlib import Path label_dir Path(labels) bad [] for txt in label_dir.glob(*.txt): if txt.stat().st_size 0: bad.append((txt.name, empty)) continue arr np.loadtxt(txt) if arr.ndim 1: arr arr.reshape(1, -1) if arr.shape[1] ! 5: bad.append((txt.name, fcols{arr.shape[1]})) continue if np.isnan(arr).any() or arr[:, 1:].min() 0 or arr[:, 1:].max() 1: bad.append((txt.name, out-of-range)) print(f异常文件数: {len(bad)}) for name, reason in bad[:20]: print(f{name}: {reason})逻辑说明空文件直接标记np.loadtxt 解析后先 reshape 成二维兼容只有单目标的情况第 0 列是类别 id第 1 到 4 列是坐标统一检查是否落在 0 到 1 区间。越界框进训练后轻则拉偏回归 loss重则让增强阶段生成畸形样本所以这一步值得在训练前做掉。提示贴近图像边缘的框有时会被标注工具画出画布坐标略大于 1。这类框建议修正而不是直接删除因为删除后同一帧里其他目标不受影响但边缘目标就少了一个学习样本。2.2 VOC XML 或 COCO JSON 标签转成 YOLO 格式拿到的老鼠数据集不一定是 YOLO 原生格式常见还有 PASCAL VOC 的 XML 和 COCO 的 JSON。判断方法简单labels 目录里出现 .xml 文件或 annotations.json 就是。下面这段脚本把 VOC XML 转成 YOLO txtimport xml.etree.ElementTree as ET from pathlib import Path CLASS_NAMES [rat] # 顺序即类别编号训练时保持一致 def voc_to_yolo(xml_path: Path, out_dir: Path) - None: root ET.parse(xml_path).getroot() size root.find(size) w float(size.find(width).text) h float(size.find(height).text) lines [] for obj in root.findall(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) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_dir.mkdir(exist_okTrue) (out_dir / (xml_path.stem .txt)).write_text(\n.join(lines)) xml_dir Path(annotations) out_dir Path(labels) for xml_path in xml_dir.glob(*.xml): voc_to_yolo(xml_path, out_dir)转换逻辑其实就三步从 size 节点取原始宽高用于归一化从 bndbox 取 xmin/ymin/xmax/ymax 像素坐标换算成中心点加宽高的相对坐标。CLASS_NAMES 的顺序就是类别编号后续 data.yaml 里的 names 必须与它一致否则模型会把别的类别当成老鼠来学这种错误最难排查。各格式的对应关系如下源格式文件形态转换要点VOC XMLannotations/*.xmlsize 取宽高bndbox 取像素坐标COCO JSONannotations.json按 image_id 关联bbox 存的是左上角坐标YOLO txtlabels/*.txt已归一化核对后直接使用COCO 转换时有个固定坑bbox 数组里存的是左上角 x、y 和宽高不是中心点直接当中心点用会把框画到图像外转换后一定要用 2.1 节的越界检查再扫一遍。2.3 划分 train/val 时固定随机种子并同步移动标签训练集和验证集划分必须保证图像与标签同步移动散落式划分最容易出的问题就是验证集混入训练图指标虚高得离谱部署后立刻现原形。固定随机种子是让实验可复现的前提也能保证下次补数据时划分口径不变import random from pathlib import Path random.seed(42) img_dir Path(images) label_dir Path(labels) imgs sorted(img_dir.glob(*.jpg)) random.shuffle(imgs) split int(len(imgs) * 0.15) val_imgs, train_imgs imgs[:split], imgs[split:] print(f训练集 {len(train_imgs)} 张验证集 {len(val_imgs)} 张) for name, subset in [(train.txt, train_imgs), (val.txt, val_imgs)]: with open(name, w) as f: for p in subset: if (label_dir / (p.stem .txt)).exists(): f.write(str(p.resolve()) \n)说明只把图像绝对路径写进 txtYOLO 会按同名规则在 labels 目录找标签写绝对路径后换机器也不需要重新生成。val 比例取 0.1 到 0.2 都行4089 张按 0.15 切验证集约 613 张单类别的 mAP 统计已经够稳。如果数据发布方自带划分文件优先用官方的跨实验对比时口径才一致。3. 用 YOLOv8 在老鼠数据集上跑通训练的最小命令集如果你之前只跑过官方示例还没完整做过 yolov8 训练自己的数据集这一章就是最小可用路径装环境、写配置、起训练、断点续跑。选 YOLOv8 而不是其他分支是因为训练、导出、部署在同一套工具链里脏活最少。团队还在用 YOLOv5 的话流程基本一样入口从 yolo 命令换成 train.pyyaml 和标签都不用改。3.1 环境准备给 ultralytics 单独建虚拟环境训练环境最忌互相污染特别是机器上还跑着别的框架时版本冲突能把半天耗在 import 报错上。我一般用 venv 给每个项目建一个干净环境Python 版本选 3.9 以上装完依赖就不再去动它python -m venv .venv source .venv/bin/activate pip install ultralytics python -c import torch; print(torch.cuda.is_available(), torch.__version__)参数说明ultralytics 会连带安装 CPU 版 torch机器有 NVIDIA 显卡的话先装对应 CUDA 版本的 torch 再装 ultralytics否则后面训练全程跑 CPU。最后一行命令输出 (False, ...) 说明当前环境没有可用 GPU4089 张图虽然不多但纯 CPU 跑 100 轮也要大半天不建议硬等。3.2 编写 data.yaml路径写绝对类别数写正确YOLOv8 的训练入口只需要一个 yaml 描述数据位置和类别不需要自定义 dataloader。写错这个文件是训练失败的头号原因最常见的是路径对不上和 nc 数错# rat.yaml path: /home/user/rat_dataset # 数据集根目录建议用绝对路径 train: train.txt # 训练图像列表 val: val.txt # 验证图像列表 nc: 1 # 类别数量老鼠只有一类 names: [rat] # 类别名顺序与标签 id 对应参数说明path 是根目录train 和 val 可以是相对路径nc 必须等于标签文件里最大类别 id 加 1单类别数据所有 txt 第一列都应该是 0names 影响输出名称与日志可读性推理显示标签时缺了它直接报错。3.3 训练命令与四个必调参数4089 张单类别数据下面这条命令就是最小可用起点。第一次跑不建议堆参数先按默认逻辑把整条链路走通再根据验证集表现逐项调整。命令里只有 data、model 是必填其余都是可以动手改的候选参数yolo detect train \ datarat.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectrat_runs参数说明modelyolov8n.pt 表示加载官方预训练权重做微调n 是 nano 变体速度最快epochs100 对单类别 4k 级别数据足够一般 60 轮左右验证指标就持平了imgsz640 是精度和速度的平衡点batch16 在 12G 显存上没问题8G 显存降 batch 到 8 并把 workers 调小patience20 表示验证指标连续 20 轮不提升就早停。参数建议值什么时候改modelyolov8n.pt精度不够换 yolov8m.pt训练和推理都会变慢imgsz640小目标偏多时改 1280显存占用约翻倍batch16显存溢出就降 8同时降低 workerspatience20出结果优先设 10想充分训练设 30lr00.01换大模型时降到 0.005 以下batch 与 workers 的搭配也值得留意。workers 默认 8数据读取慢的机械硬盘上反而成为瓶颈我一般把 workers 设成 CPU 核数的一半同时确认 GPU 利用率能在 90% 以上。训练开始的几分钟里观察 GPU 利用率比盯 loss 更有用利用率上不去多半是数据管线的问题。3.4 中断恢复与 results.csv 的读法训练中断是常态特别是用公司共享机器跑的时候显存被占用、断电、系统重启都会打断训练。恢复方式很简单用上次保存的 last.pt 直接接着跑不需要重新起一轮yolo detect train datarat.yaml modelrat_runs/detect/train/weights/last.pt resumeTrue恢复后会自动沿用上次的超参数不需要重新传 data。训练过程中每隔几轮打开 runs 下的 results.csv重点看三列train/box_loss 应该持续下降val/box_loss 降到某一点开始反弹是过拟合信号metrics/mAP50(B) 是最终要交账的数字。第一次跑完单类别老鼠检测 mAP50 到 0.85 以上算建立了可用基线低于 0.7 就先查标签而不是查模型。results.csv 用表格工具就能打开训练完直接按列画曲线最快。我习惯把 train/box_loss 和 val/box_loss 叠在一张图里看两条曲线同步下降说明拟合正常train 降而 val 开始抬就是早停机制要接管的时候这时候回去看增强参数比加轮次更有用。4. 老鼠场景下 YOLO 训练的调参与四个典型坑原理层面的 yolo 算法讲解到处都是真正让工程师卡住的是脏数据和参数边界。老鼠检测的难点集中在目标小、背景杂、身体变化大。这一章把最常踩的四个坑拆开讲。4.1 先统计标注框尺寸分布再决定 imgsz 要不要改监控画面里老鼠经常只有几十像素高。imgsz640 训练时图像被缩放到 640小框在特征图上只剩几个激活点网络学不到有效特征。动手调参前先看框的面积分布import numpy as np from pathlib import Path areas [] for txt in Path(labels).glob(*.txt): arr np.loadtxt(txt) if arr.ndim 1: arr arr.reshape(1, -1) for row in arr: areas.append(row[3] * row[4]) areas np.array(areas) print(f标注框总数: {len(areas)}) print(f面积占比中位数: {np.median(areas):.4f}) print(f面积占比小于 0.01 的比例: {(areas 0.01).mean():.2%})面积占比是归一化 w 乘 h0.01 大致对应 640 分辨率下 64×64 像素的框再往下就是典型小目标区间。小框占比超过三成建议直接 imgsz1280或者结合 SAHI 做切片训练与推理。imgsz 提高后显存占用接近翻倍batch 要同步下调这是最容易忽略的连锁反应。4.2 背景相似物误检加负样本比调阈值更有效把电线、管道、阴影误检成老鼠是常见问题。只调 confidence 会把真样本一起压掉效果很差。常见做法是收集一批确定没有老鼠的图像写空标签混进训练集让网络学会输出“无目标”from pathlib import Path import shutil neg_dir Path(negative_images) # 现场收集的无老鼠图 img_dir Path(images) label_dir Path(labels) for i, p in enumerate(neg_dir.glob(*.jpg)): new_name fneg_{i:04d} shutil.copy(p, img_dir / f{new_name}.jpg) (label_dir / f{new_name}.txt).write_text()空标签文件不能省YOLO 按同名找标签找不到的文件会被直接跳过负样本等于没放。负样本占训练集 10% 到 20% 即可比例过高会让模型整体偏向不检测召回率急剧下降。加了负样本后验证集也要混少量负样本否则 mAP 数字虚高部署到现场才发现压不住误检。4.3 数据增强按生物目标重设别用默认值硬训YOLOv8 的默认增强面向通用目标老鼠场景要调整几个关键参数参数默认值建议值理由degrees0.05 以下老鼠不会倒立大角度旋转制造假样本hsv_h0.0150.01毛色是判别特征颜色扰动要克制translate0.10.2老鼠出没位置随机平移增强有效scale0.50.8距离不同导致目标大小差异大mosaic1.00.8小目标在多图拼接时容易被裁掉一半这些参数不适合命令行传建议训练脚本里覆盖from ultralytics import YOLO model YOLO(yolov8n.pt) model.train( datarat.yaml, epochs100, imgsz640, batch16, degrees5, hsv_h0.01, translate0.2, scale0.8, mosaic0.8, )说明mosaic 把四张图拼成一张训练小目标被裁掉一半的概率变大所以降到 0.8 是小目标场景的通用做法。如果训练数据本身就是固定机位监控截图视角单一translate 和 scale 的收益远大于旋转。单类别数据集改两个参数就能看出 mAP50 的差异不需要做 Grid Search。4.4 训练尾段关掉 mosaic让收敛落在真实分布上最后若干轮关闭 mosaic 和随机翻转模型在接近真实分布的数据上做收尾这是 YOLO 系列调参里的标准习惯直接对应到 yolo 算法改进实践里见效最快的一步model.train( datarat.yaml, epochs100, imgsz640, batch16, close_mosaic10, )close_mosaic10 表示最后 10 轮不再做 mosaic 增强。此时 loss 曲线会先跳升再回落属于正常现象。如果这阶段 mAP50 还在明显上涨说明前面的训练轮次不够可以直接加 epochs不必重新起一轮。5. 部署前的三个验证检查点与标签回归技巧模型训练完不等于能上线。现场环境和训练集分布往往有差异部署前用三个检查点把返工成本压下来。5.1 用 val 结果和预测图检查别只盯 mAP单类别模型没有混淆矩阵判断定位质量要看 mAP50-95 与 mAP50 的差值超过 0.15 说明框回归不够准优先查标签质量而不是换模型结构。yolo detect val datarat.yaml modelrat_runs/detect/train/weights/best.ptval 输出目录里的 val_batch0_pred.jpg 画了预测框和真实框的对比直接看比看曲线直观。重点检查两类错误漏检的是小目标还是遮挡目标误检集中在哪类背景。这两类错误的修法完全不同前者改 imgsz 和增强后者加负样本。把 val 预测图按置信度从低到高排序看一遍低分误检的样本往往就是负样本需要覆盖的场景记下来去采集对应背景比单纯调 NMS 阈值有效得多。5.2 导出 ONNX 后单独测推理耗时训练日志里的速度是批处理指标测的是整批并行的吞吐代表不了部署环境里单帧的延迟。上线前先在目标机器上导出再实测别信开发机的数字yolo export modelrat_runs/detect/train/weights/best.pt formatonnx imgsz640导出后用 onnxruntime 加载模型在部署机上拿 100 张现场图测单张耗时并取均值。单张 25ms 以内算中等水平够接实时监控超过 50ms 就考虑降 imgsz 或换更小的模型变体。实时图像场景下的耗时测试必须在目标硬件上做不同显卡和 CPU 的差异很大。5.3 用当前权重预标注新数据人工修正后增量训练数据要持续补手工标注又慢。我一般用当前最优权重去识别新采集的图像生成初版标注再人工修正框的位置和漏检。修正后的样本加入训练集下一轮迭代的标签质量提升速度远快于纯手工标注。操作上保持 data.yaml 不变新图加进 images同名 txt 加进 labels重新跑一遍第 2.3 节的划分脚本随机种子保持不变才能保证新老样本的划分比例可追踪。本文还有配套的精品资源点击获取
返回列表