尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

老鼠图像目标检测:1100张YOLO标注数据集与yolov5训练实战

老鼠图像目标检测:1100张YOLO标注数据集与yolov5训练实战 简介一套面向目标检测任务的老鼠图像标注数据集约1100张图片类别仅老鼠一种采用YOLO标注格式整理并配有classes文件说明类别定义。适合训练YOLOv5等检测模型、验证数据增强策略、对比注意力机制改进效果也适用于鼠害监测、实验室动物行为分析等场景。压缩包共2000个文件1078个XML标签文件用于保存标注信息920张JPG原图供训练与测试另有1个Python可视化脚本和1个JSON说明文件整体约171.6MB。数据集已做训练集、测试集划分运行show脚本即可快速渲染标注框便于检查标签质量与评估预测结果整体围绕单类别检测任务组织结构清晰。目前已有93人学习下载。需要老鼠检测基准数据或复现YOLO改进实验的开发者可直接基于这套数据开展模型训练、指标评估和算法调优节省自行采集与标注的时间。1. 老鼠图像目标检测数据1100张YOLO标注图能不能直接开练做鼠类行为识别、实验室动物计数、鼠害监控这类活儿最卡人的往往不是模型而是数据。这份老鼠图像目标检测数据是约1100张已标注图片类别就一个老鼠标注格式是YOLO的txt方式也就是每张jpg配一个同名txt里面写归一化后的检测框坐标。它已经做了训练集、测试集划分还带一个show脚本用来可视化标注效果。对想快速验证yolo检测流程、或者拿单类别数据跑yolov5改进实验的人来说这是个能省下几天整理时间的起点。它适合两类人一类是没碰过目标检测标注的初学者另一类是想做算法改进但不想花力气清洗数据的从业者。拿到手先别急着训练把标注和目录结构核对清楚后面的坑会少很多。2. 标注格式与目录结构从一行txt还原出检测框的边界规则在把图片丢给任何网络之前先把目录和标注文件读懂。YOLO格式数据集的常见组织方式是images目录放图片、labels目录放同名txt、classes.txt放类别名列表有时还有train.txt和val.txt这种按行记录图片路径的清单。这份老鼠数据里图片文件就是mouse3148.jpg这类命名同名txt紧随其后这决定了后续所有脚本的匹配逻辑。如果你拿到的压缩包不是这种结构先手动整理成这个结构再继续往下走否则后面每跑一个脚本都会在路径上翻车。2.1 数据目录里都有什么jpg、txt与清单文件先打开目录看一眼最常见是这个结构mouse_data/ ├── images/ │ ├── mouse3148.jpg │ ├── mouse3066.jpg │ └── ... ├── labels/ │ ├── mouse3148.txt │ ├── mouse3066.txt │ └── ... ├── classes.txt ├── train.txt └── val.txtimages目录下放原始图片labels目录下放同名txt标注文件一个txt对应一张图。classes.txt用来记录类名每行一个类名行号就是类别编号。train.txt和val.txt是划分清单按行记录图片的路径训练时yolov5会按这个清单去加载数据。拿到数据后第一件事不是开训练而是做一致性检查。图片总数约1100张labels下同名txt的数量应该与jpg完全一致。不一致通常意味着两种情况有图片没标注或者有标注的图片不在images里。这两种情况都会在训练时暴露成奇怪的报错或者静默漏检。我一般会直接在命令行里数一遍文件数确认数量对得上再继续。2.2 classes.txt与单类别编号为什么类别必须从0开始YOLO标注txt里的第一列是类别编号它从0开始计数。这份数据只有老鼠一个类别所以classes.txt里大概率只有一行内容比如mouse对应的编号就是0。这一点特别容易栽跟头。如果自己重新生成classes.txt时漏了这一行或者在其它预处理脚本里把类别编号写成1后续所有训练脚本都会按错位的索引去读标签。比如txt里写的是0但classes.txt里第一行是空行、第二行才是mouse那模型会把空行当作第一个类别把mouse当成第二个类别。单类别时还好顶多是类别名显示不对一旦你后续想往数据里加第二个类别这种错位会直接导致训练标签全部弄混。我的习惯是拿到数据先数一下类别文件行数再找一张有标注的图确认txt里的编号小于等于这个行数减一。对单类别场景任何txt里的第一列不为0都说明中间处理过程出过错要停下来排查。2.3 一行标注的还原逻辑class x_center y_center width height一条标注通常长这样0 0.5125 0.4385 0.1820 0.2450五个数字从左到右分别是类别编号、中心点x归一化坐标、中心点y归一化坐标、框宽、框高。后面四个值是相对于原图宽高的比例值域在0到1之间。所谓还原就是把它们再乘回原图尺寸像素坐标x等于x_center乘图宽y等于y_center乘图高框宽和框高同理。以一张1280乘720的图为例上面这条标注换算后中心点在(656, 316)像素处框宽约233像素高约176像素左上角约在(539, 228)右下角约在(772, 404)。参数含义可以对照下面这张表理解字段位含义取值范围示例值第1列类别编号0到类别数减10第2列中心点x归一化值0到10.5125第3列中心点y归一化值0到10.4385第4列框宽度归一化值0到10.1820第5列框高度归一化值0到10.2450这里有两个常见的误读。一是以为txt里存的是左上角和右下角坐标但YOLO格式存的是中心点加宽高二是以为里面存的是像素值其实不是是归一化比值。如果强行把txt里的值当绝对像素画上去框子会小得几乎看不见全都挤在图像左上角那一小块区域里。这两个方向我都在早期项目里翻过车所以拿到任何YOLO标注数据第一件事永远是找一张图亲手算一遍还原过程确认理解正确再写脚本。YOLO选择这种表示法是有原因的。检测模型输出时天然就是中心点加宽高的形式预测框匹配anchor时直接算中心距离和宽高差就行归一化坐标与图像原始分辨率无关同一份标注在640分辨率和1280分辨率下都不用改训练做数据增强时对中心点和宽高做平移缩放也更直观。不要轻易把它转成左上角右下角的XYXY格式除非你后续使用的框架确实要求那种格式否则转一圈再转回来还容易引入精度损失。3. 可视化验证用show脚本把1100张标注画回原图标注文件本质上是数字数字对不对单靠眼睛看是看不出来的。训练前我强烈建议先把所有标注可视化一遍把每张图的检测框画回原图上人眼过一遍。这一步能发现的风险包括框没有对准目标、框尺寸明显不对、某些图漏标了、某些图标了超出边界的框。数据集自带的show脚本通常就是干这件事的但如果你拿到的是原始压缩包下面这份按常见方式实现的脚本可以直接用。3.1 show脚本的标准流程读图、解析txt、画框、存盘把下面内容保存为show.py和images、labels目录放在同一级直接运行。import os import cv2 img_dir images label_dir labels out_dir vis os.makedirs(out_dir, exist_okTrue) for img_name in os.listdir(img_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue stem os.path.splitext(img_name)[0] img_path os.path.join(img_dir, img_name) label_path os.path.join(label_dir, stem .txt) img cv2.imread(img_path) if img is None: print(f图片读取失败: {img_path}) continue if not os.path.exists(label_path): print(f缺少标注: {label_path}) continue h, w img.shape[:2] for line in open(label_path, r, encodingutf-8): parts line.strip().split() if len(parts) 5: continue cls int(parts[0]) xc, yc, bw, bh map(float, parts[1:5]) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) x1, y1 max(0, x1), max(0, y1) x2, y2 min(w - 1, x2), min(h - 1, y2) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, fmouse({cls}), (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(os.path.join(out_dir, img_name), img) print(可视化完成结果在 vis 目录下)这段代码的核心逻辑不复杂。遍历images目录下所有jpg用文件名去labels目录找同名txt读入每一行标注把归一化坐标还原成像素坐标用OpenCV画矩形框再把结果写到vis目录。注意几个关键点cv2.imread返回None表示图片本身损坏或者路径有中文这类文件要单独记录下来读取txt时我加了utf-8编码声明防止个别文件保存为带BOM的格式导致首行解析出错画框前做了越界裁剪防止标注框超出图像边界时OpenCV画图报错。3.2 坐标还原公式与越界检查归一化值乘以宽高才是像素这段代码里最容易出错的其实是还原公式的写法。x1等于中心点x减去框宽一半再乘图像宽度x2等于中心点x加上框宽一半再乘图像宽度。很多人会写成直接用xc乘w再去加减bw这样算出来的框比实际大一倍。另外要注意乘法顺序先算宽度一半加减后再乘宽高比先乘再加减更不容易丢失精度。越界检查这一步经常被忽略。标注框出现在图像边界附近时归一化坐标可能出现负值或者宽度大于1的情况。在可视化脚本里我做了min和max裁剪画出来的框看着是正常的。但训练时如果保留这种越界框数据增强模块做随机平移、缩放后越界会进一步加剧轻则影响loss计算重则产生NaN梯度。所以脚本里还应该在解析阶段做一次统计把每一行标注的原始值是否超出0到1范围记录打印出来而不是等到训练时才被框架报警告。3.3 我给show脚本增加的统计输出标注密度和框面积分布只看单张图很难判断整个数据集的标注质量所以我通常在画框脚本末尾追加一段统计逻辑。统计每个txt的框数量、每张图的框面积占整图面积的比例以及越界标注的行数。判断依据很简单老鼠目标一般占图像比例不大如果发现某张图的框面积占整图比例超过60%要么是这张图目标特别近要么是标注明显画大了如果大量图片的框面积占比都在5%以下说明老鼠在图中偏小训练时就要考虑提高输入分辨率。import numpy as np total_boxes 0 no_label_imgs [] small_ratio_imgs [] for img_name in os.listdir(images): stem os.path.splitext(img_name)[0] label_path os.path.join(labels, stem .txt) if not os.path.exists(label_path): no_label_imgs.append(img_name) continue with open(label_path, r, encodingutf-8) as f: boxes [line.strip().split() for line in f if len(line.strip()) 0] total_boxes len(boxes) for parts in boxes: w_norm float(parts[3]) h_norm float(parts[4]) if w_norm * h_norm 0.01: small_ratio_imgs.append(img_name) print(f总框数: {total_boxes}) print(f无标注图片数: {len(no_label_imgs)}) print(f小目标图片数: {len(small_ratio_imgs)})框面积用归一化宽高相乘得到结果就是相对于原图面积的占比。这个统计值对后续选imgsz和anchor很有参考价值比如大量框面积占比在1%以下640分辨率的输入可能让单个目标只有十几个像素检测效果基本不会好。这种统计在1100张图上跑一遍只要几秒比训练跑十几个小时再发现结果不对要划算得多。4. 从数据集到训练data.yaml与首次启动yolov5的完整配置可视化确认标注没问题后就可以开始配置训练了。这一步的目标是把数据集组织成yolov5能直接消费的形式核心动作是写data.yaml、确认划分方法、选择合适的训练参数。很多人卡在准备数据这一步几天就是因为搞不清yaml里该填什么、train和val该指向文件还是目录。4.1 确认已有划分而不是重复划分train.txt和val.txt先读一遍这份数据已经做了训练集、测试集划分所以第一步是确认划分是否合理而不是自己重新写一套split脚本。先看train.txt和val.txt各有多少行wc -l train.txt val.txt把两个数值相加正常应接近总图片数约1100。如果两者相加小于1000说明有一部分图片既不在训练集也不在测试集那部分图在训练时永远看不到在验证时也不会出现纯属浪费如果相加大于1100说明有图片同时出现在两个清单里会造成数据泄漏验证结果虚高。这两种情况都需要重新生成划分清单。如果数量对得上就不要动划分直接使用原始清单训练。原因很简单用原始划分得到的结果才能和同类实验对比自己重划分后指标会因随机采样产生波动别人不好复现。4.2 写data.yamlnc1与路径写法的两个细节yolov5训练时需要知道数据在哪、有几个类别、类名是什么。新建一个data/mouse.yaml文件内容如下# 老鼠单类别目标检测 train: /absolute/path/to/train.txt val: /absolute/path/to/val.txt nc: 1 names: [mouse]train和val可以指向txt清单也可以指向图片目录。yolov5的加载逻辑会自动判断是文件还是目录目录方式会自动扫描里面所有图片。但这里有两个值得注意的细节。第一路径最好写成绝对路径不要写相对路径因为训练脚本的工作目录可能与数据集目录不一致相对路径很容易解析失败第二尽量不要把数据集放在带中文或空格的路径下OpenCV和Python的文件操作在中文路径下表现不稳定这是跨平台项目里的老问题。4.3 首次训练参数建议img、batch、epoch与预训练权重从数据集和模型规模出发单类别1100张图用yolov5s起步就够了。按下面命令启动python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/mouse.yaml \ --cfg yolov5s.yaml \ --weights yolov5s.pt \ --name mouse_run1参数含义逐个说。--img是输入分辨率默认640。如果可视化阶段发现老鼠目标小可以提到768或1024但显存占用会按平方增长需要根据显卡实测调整。--batch是批大小16在多数12GB显存的显卡上可以跑yolov5s显存不足就降到8不要硬来。--epochs设为100单类别简单任务100轮足够看出趋势不用一上来跑300轮。--weights用yolov5s.pt官方预训练权重虽然老鼠与COCO类别相关性不高但预训练权重提供的底层特征提取能力对收敛速度帮助明显。先不用改anchor。yolov5会自动调用k-means重新计算anchor日志里会打印建议的anchor尺寸。单类别检测框形状相对统一自动算出来的anchor基本可用。改了yolov5s.yaml的nc后注意cfg文件里默认nc是80如果不改训练会在类别数上暴雷。打开yolov5s.yaml把nc改成1即可。4.4 训练后必看的结果文件results.png与best.pt训练结束位置在yolov5/runs/train/mouse_run1目录。先看weights目录下best.pt和last.ptbest.pt是对验证集指标最优的权重推理时用这个。再看results.png一屏展示loss曲线、mAP曲线、PR曲线能快速判断训练是否收敛。最后看confusion_matrix.png和labels.jpg前者展示混淆矩阵后者展示训练集标注框的位置分布。如果labels.jpg里框都集中在图片中心区域说明数据集的标注位置分布有偏需要检查拍摄和标注过程是否偏心。文件清单大致如下文件作用weights/best.pt验证集指标最优的权重weights/last.pt最后一个epoch的权重results.png训练曲线总览confusion_matrix.png混淆矩阵labels.jpg训练集标注框分布val_batch0_pred.jpg验证集预测可视化5. 常见问题与避坑单类别老鼠数据复现时的五个高频坑把这份数据集交给不同的人跑翻车的姿势几乎集中在几个固定位置。这一章把我在类似数据上踩过的坑整理出来每条按现象、原因、解决的顺序写方便你对照排查。5.1 现象一训练时loss正常下降但验证mAP0.5一直是0这可能是单类别数据最诡异的问题。训练损失在降曲线看着一切正常但验证指标一动不动。其实原因多半不在模型而在类别编号对不上。data.yaml里names的顺序与标注txt里的第一列不一致或者classes.txt文件头部有一个看不见的空行类别被整体后移了一位。yolov5解析names时会把空行也算作一个类别于是txt里写0对应到names里的空串真正的mouse变成了编号1。模型一直在尝试学习一个空类名验证当然什么都检测不到。解决方法是逐行核对。先用命令打印data.yaml内容和classes.txt内容确认第一类就是mouse。再用工具读取任意一个txt标注确认第一列是0而非1。如果txt第一列是0而names列表第一个是空串把空行删掉重新保存。5.2 现象二show脚本画框时框位置明显错位可视化时框画出来了但位置和老鼠完全对不上要么偏到图像角落要么框的大小与目标比例明显不符。这种问题几乎都出在坐标还原这一步。最常见的错误是把归一化值当成绝对像素值直接用比如0.5125被当作横坐标512像素来画实际应该用0.5125乘以图像宽度才能得到像素坐标。另一个错误是图像尺寸取错比如先对图片做了resize但坐标还原时用的还是原始宽高导致框错位。解决的唯一路径是回到公式。中心点像素x等于归一化x乘当前图像宽度中心点y乘当前图像高度。代码里必须从img.shape实时取值不要写死一个固定尺寸。如果脚本用了resize要确保解析坐标时用的是resize之后的尺寸。检查方法很简单把一张图和它对应的txt标注单独拎出来手算一遍坐标对照画框结果几分钟就能定位是公式错了还是尺寸错了。5.3 现象三训练一开始报No labels in images训练启动时日志出现UserWarning: No labels in image或者干脆报Dataset is empty第一反应应该是标签没有被成功扫描到。这个坑在数据从一台机器拷到另一台机器时尤其频繁。原因通常是train.txt和val.txt里记录的是旧机器的绝对路径到新机器后路径失效yolov5遍历清单时发现图片不存在自然也就加载不到对应标签。解决方式是检查清单内容。直接查看train.txt头几行看看里面的路径是否指向当前机器的真实位置。如果路径格式不对用文本编辑器批量替换前缀即可。另外还有一类情况是yaml的train字段指向了图片目录但目录里混入了一些没有对应txt的jpgyolov5会跳过这些图并打印警告。这种要写脚本把没有标注的图移出images目录而不是放着不管因为训练时那些图不会参与训练白白浪费数据。5.4 现象四训练中途loss变成NaN或bn running_mean出现NaN单类别数据训练时NaN出现的概率不高一旦出现基本是两类原因。一是学习率过大梯度更新直接溢出了浮点数表示范围二是数据增强在数据量偏少时生成了异常样本比如mosaic把多张图的框叠加后归一化坐标出现负值或大于1。在1100张图这种规模下mosaic增强虽然有效但确实存在小概率生成跨图像拼接后的越界组合导致回归loss异常。处理方式依次排查。先检查是不是学习率问题把yolov5默认的lr00.01调低到0.005重跑看前20个epoch是否正常如果还是NaN把mosaic增强关掉yolov5训练命令加参数--no-mosaic可以快速验证最后检查图片本身有没有损坏写一段循环读取脚本用cv2.imread逐个打开返回None的文件直接删除或重新导出。我一般会先把损坏图片这一步做掉因为它最容易被忽略而且影响最隐蔽。5.5 现象五验证集框用于计算IoU时出现负值或极大值自己写评估脚本时经常发现某些样本计算IoU得到负数或者概率值异常。这往往不是因为模型输出错了而是因为标注框本身越界。标注工具在标注时没有裁剪中心点接近图像边缘导致框的一部分落在图像外侧归一化坐标就可能有负值或大于1。可视化脚本由于做了clip肉眼看不出来但评估脚本不会自动clip于是IoU计算就出问题。解决方式有两步。第一步写越界检查脚本遍历labels目录所有txt逐行检查四个归一化值是否都在0到1内把所有越界行打印出来。第二步根据越界比例决定策略。如果越界框只有零星几个直接手动修正这些txt如果比例较高需要回到标注工具重新导出或者在训练脚本的数据加载部分统一做一次clip。不要直接在归一化值上硬clip因为clip后的宽高可能是负数要先判断框是否完全在图像内再做截断。6. 进阶技巧先读bad cases再决定要不要做数据增强单类别1100张数据训练效果不理想时大多数人第一反应是加数据增强、换更大的模型、堆更多的epoch。我现在的习惯是先看bad cases再决定动哪部分。6.1 训练完不要急着看mAP先看val_batch0_pred.jpgyolov5训练目录里生成的val_batch0_pred.jpg是验证集第一张预测可视化图。把它放大逐格看关注两种现象漏检的老鼠是什么样子的误检的框落在哪些区域。老鼠如果全身灰黑、和背景颜色接近漏检大概率是因为对比度不足如果漏检的都是体型很小的远距离目标说明输入分辨率不够如果误检框集中在鼠笼边缘或垫料褶皱处说明模型学到了纹理伪影而不是目标本体。这些信息比一个mAP数字更有指导性。6.2 根据坏样本选择增强参数而不是堆满所有增强yolov5默认的增强参数mosaic、hsv、flip都是开着的对大多数任务够用。但单类别老鼠数据有自己的特点老鼠形态单一、背景相对固定过度增强反而会让模型学到不存在的颜色和形状变化。我的做法是先关掉mixup数据量少时mixup的收益不稳定然后把hsv_v的值从默认的0.4调低到0.2因为老鼠颜色在可见光下不会大幅变化过强的亮度扰动会引入噪声如果漏检集中在小目标就把输入分辨率从640提到1024同时保持mosaic开启让模型在小目标上多看到一些组合样本。# 单类别小目标场景的增强调整参考 mosaic: 1.0 mixup: 0.0 hsv_h: 0.01 hsv_s: 0.5 hsv_v: 0.2调完参数重新训练再把新的val_batch0_pred.jpg和上一版并排放一起对比确认漏检确实减少了。从那以后我每次拿到数据集都强制走一遍固定流程先看类别文件再全量可视化标注确认图片与txt数量一致确认划分清单可用最后才启动训练。这个顺序帮我挡掉了大部分无意义的训练轮次。希望帮到你。本文还有配套的精品资源点击获取
返回列表