news 2026/9/30 1:17:16

自制YOLO猫狗检测数据集:标注、训练与评估实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
自制YOLO猫狗检测数据集:标注、训练与评估实战

1. 项目源起:为什么我需要一套猫狗检测数据集

我自己在做一个宠物识别类产品原型的时候,最头疼的并不是模型选型,而是数据集本身。YOLO这种目标检测算法对训练数据的要求有点“挑剔”,它既要原图也要带类别和坐标标注,而且标签格式必须统一成txt文本,每行对应一个目标框。市面上公开的猫狗相关数据大多是分类数据集,比如Stanford Dogs、Oxford-IIIT Pet,图片主体基本居中,背景相对干净,拿去训练分类模型没问题,但直接喂给YOLO做检测训练,模型学到的是“一张图等于一只猫”,而不是“图里这个坐标框里的东西是一只猫”。等模型部署到真正的摄像头画面里,猫出现在角落、部分被遮挡、和背景融为一体时,检测框就会开始乱跳甚至直接漏检。

所以我花了将近三周时间,从多个来源收集、筛选、清洗并手工标注了4300张猫狗图像,最终整理成了一套标准YOLO格式的宠物识别数据集。这个数据集中所有图像都有对应的txt标注文件,类别只保留猫和狗两类,坐标统一为归一化格式,训练集和验证集按9比1划分。如果你正准备用YOLOv5、YOLOv8或最新的一些改进版本训练猫狗检测模型,这一套数据可以直接拿来用,不用再踩我踩过的那些数据坑。

这个项目适合三类人:一是刚接触YOLO、想跑通第一个自定义检测模型的初学者;二是已经在做宠物相关的视觉应用,比如智能猫门、宠物喂食器、猫咪行为分析,需要一个干净数据集做基线实验的开发者;三是想研究检测模型在“类间外观接近、背景复杂”场景下如何表现的朋友。猫和狗其实在体型、颜色、姿态上都有很大差异,但幼猫幼犬、长毛品种、局部遮挡这些情况会给模型带来不少挑战,这套数据里我对这些边界情况做了额外补充。

有个数据上的细节我后来觉得非常重要:这套数据集不是简单地把网图打包,而是刻意模拟了真实落地场景的图像分布。室内外环境都有覆盖,光照条件从明亮的窗边到昏暗的走廊都有,图像里出现的目标数量从单只到三四只不等,目标大小从占画面三分之一的近景到只占画面十分之一的远景都有。之所以这么设计,是因为我在实际测试中发现,很多公开数据集在mAP指标上刷得很高,但一放到真实场景就“见光死”,原因就在于训练图像太“干净”,模型没有见过复杂的视觉噪声。

1.1 数据集能解决什么实际问题

  • 统一YOLO格式:训练集和验证集的图像文件、标注文件一一对应,放到data.yaml里就能直接训练。
  • 类别定义明确:猫、狗两类,标注严格按照左、上、右、下坐标映射到归一化的中心点坐标加宽高。
  • 场景覆盖贴近落地:单目标、多目标、小目标、遮挡目标都有涉及,模型训练后泛化性更接近真实需求。

提醒:别小看“数据干净”这四个字。我最初用爬虫方式收集了一批图片,结果里面混了漫画、玩偶、甚至衣领上印的卡通猫图案,标注的时候全部需要重新筛掉。这类噪声如果不清理,训练出来的模型会莫名其妙把卡通形象当成真实目标。

2. 数据集的内部结构与设计思路

这套4300张的数据集,从文件结构上看很简洁,但为了保证训练效果,我在内部构成上做了很多考究。

2.1 目录结构与标注格式

数据集根目录下分为images和labels两个大文件夹,每个文件夹里再按train和val细分。图像统一采用JPG格式,分辨率为640x640到1920x1080不等,训练时YOLO会自动做letterbox适配,不需要提前裁剪。labels目录下的每个txt文件与图像文件同名,例如一张名叫IMG_0042.jpg的图片,对应标注文件就是IMG_0042.txt。

txt文件里每行代表一个检测目标,格式如下:

class_id center_x center_y width height

其中class_id为0或1,0代表狗,1代表猫。四个坐标值都是归一化后的浮点数,范围在0到1之间,计算方式很简单:用目标的中心点x坐标除以图片宽度得到center_x,目标框宽度除以图片宽度得到width,高度同理。这种格式是YOLO系列训练时默认读取的格式,我见过很多人拿到VOC或COCO数据集后到处找转换脚本,其实核心就是做这一层归一化映射。

举个例子,如果一张宽800像素、高600像素的图片里,有一个目标框左上角坐标是(200, 150),右下角坐标是(400, 450),那么归一化后的标注就是:

  • center_x = (200 + 400) / 2 / 800 = 0.375
  • center_y = (150 + 450) / 2 / 600 = 0.5
  • width = (400 - 200) / 800 = 0.25
  • height = (450 - 150) / 600 = 0.5

对应txt文件内容为:0 0.375 0.5 0.25 0.5。

2.2 场景分布与类别平衡

为了不让模型对某一种背景产生过拟合,我对图像来源做了统计。数据集中室内图像约占55%,室外约占45%。室内场景包括客厅、卧室、阳台、宠物医院诊室,室外场景包括小区的道路、公园草地、宠物乐园。很多猫狗检测项目训练时只有室内图像,结果模型跑到户外就频繁漏检,这是我在真实项目中吃过亏的地方,所以这套数据在收集阶段就有意识地控制背景多样性。

类别平衡上,我统计了一下:包含狗的图像约2100张,包含猫的图像约2250张,其中约300张图像里猫狗同时出现,一共标注了大约6100个目标框,平均每张图约1.4个目标。这里有意识保留了一批猫狗同框的照片,因为实际应用中经常出现宠物互动的画面,如果模型只会识别单独出现的猫或狗,那多宠物场景基本就废了。

2.3 图像筛选标准

在筛选图像时,我给自己定了三条硬性标准:第一,图像中必须有可辨识的完整或大部分可见的猫狗主体,只露一条尾巴或者只看到模糊背影的不要;第二,图像本身不能有严重压缩痕迹或者分辨率过低,小于480p的图直接排除;第三,同一张图如果被多次转载、在水印位置发生变化,用感知哈希算法去重,避免重复图像进入数据集。

关于去重,我多说一句。很多人训练时发现验证集的mAP特别高,但测试集上效果明显下降,第一反应是模型过拟合,实际上很可能是因为训练集和验证集之间有重复或高度相似的图像。我在整理数据时写了一个哈希去重脚本,对每张图生成一个64位的感知哈希值,把汉明距离小于等于5的图片全部找出来人工确认,这一步至少清掉了二十多组重复图片。

3. 标注流程全记录与实操细节

标注是整个数据集构建过程中最耗时的一环。我前前后后用了十天来标注和校对,中间试过好几种标注工具,最后沉淀出一套比较顺手的流程。

3.1 标注工具选型与经验

我用过三款工具:LabelImg、labelme和X-AnyLabeling。LabelImg是老牌工具,对YOLO格式支持很直接,打个勾就可以导出txt标注文件;labelme输出的是json格式,适合做实例分割标注,但拿来做检测框标注反而要多一步转换;X-AnyLabeling是我后期的主力工具,因为它支持加载YOLO预训练模型做自动预标注,然后再由人工修正。

个人建议:如果标注量在几百张以内,LabelImg足够;如果标注量到了几千张,强烈建议先用一个预训练好的目标检测模型(哪怕是通用的COCO模型)对图片做第一轮自动标注,然后打开标注工具逐张检查修正。这个过程我实测下来能把总耗时压缩40%左右,因为大部分图像里的猫狗都能被预标注模型准确框出来,人工只需要调整边缘框的位置,而不是从零开始画框。

3.2 标注过程中的三个重点难点

第一个难点是小目标标注。很多图像里的猫狗只占画面的一小块区域,比如躲在沙发角落的猫、远处草坪上奔跑的狗。这类目标的框如果标注不准确,训练时YOLO很难学到有效特征。我的经验是:小目标的框宁可稍微紧一点,也不要为了把耳朵尾巴都包进去而扩得太大,背景噪声进框太多反而会让模型学到无关特征。

第二个难点是遮挡目标。只露出半个身子的猫、趴在毯子下面只露出头的狗,这类遮挡样本要单独标注,并且不要试图用一个很大的框把被遮挡的和遮挡物都框进去,只需要把可见部分框出来。我在标注时发现,如果人被框进目标里,相当于给模型注入了错误监督信息,后期模型的误检率会显著上升。

第三个难点是猫狗同框时候的边界划分。有些长毛品种的猫狗在颜色和纹理上很接近,尤其是幼年个体,框子稍微偏一点就可能落到另一类目标上。我处理这类图像时会同时打开原图的分辨率视图,放大到200%以上逐边缘检查,宁可多花十秒钟,也不要留下一个错误标签。

4. YOLO模型选型与训练实操

数据集准备好之后,就是模型训练环节。我用这套数据集同时跑过YOLOv5s、YOLOv8s和YOLOv8n,最终在项目里选型的是YOLOv8s,原因是它在算力占用和检测精度之间比较平衡,单张RTX 3060显卡就能在合理时间内完成训练。

4.1 模型版本对比与选型理由

虽然知乎、公众号上经常能看到各种“YOLO新版本杀疯了”的标题,但我自己的经验是:选模型要看你手头的算力和部署目标。

YOLOv8n的模型体积最小,只有3.2MB左右,推理速度极快,适合部署到树莓派、手机端或者低功耗摄像头设备,但精度在复杂背景下的表现一般,尤其是在小目标上,漏检率比YOLOv8s高不少。YOLOv5s虽然版本相对旧,但生态最成熟,部署到TensorRT、OpenVINO的文档和踩坑案例最多,适合需要快速落地的项目。YOLOv8s精度和速度的平衡最好,C2f结构带来更好的特征提取能力,这也是我最后选它的主要原因。

如果你在做一个研究性质的对比实验,我建议把YOLOv8s作为基线模型,然后再去对比一些改进方案,比如给Neck部分加注意力机制、换用某种更加高效的检测头结构。检测头这块多说一句,Efficient Head是最近很多改进工作的重点方向,它的核心思路是让不同尺度的特征在检测头里更充分地融合,对小目标的检测收益比较明显。这套数据集里有一个专门的子集是包含小目标的,跑改进模型时可以直接用这个子集做对比。

4.2 训练配置与命令

训练前需要先写好data.yaml文件,内容大概是:

path: /path/to/dataset train: images/train val: images/val nc: 2 names: ['dog', 'cat']

然后执行训练命令。我用YOLOv8训练时的核心参数如下:

yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=120 \ imgsz=640 \ batch=16 \ lr0=0.01 \ optimizer=AdamW \ seed=42

这里有个值得说道的点:为什么imgsz选640而不是更大?我试过用960分辨率训练,mAP确实有提升,特别是小目标上的召回率涨了大概1.8个百分点,但训练时间差不多增加了一倍,推理速度也明显下降。对猫狗检测这种目标本身不算特别小、实时性要求又比较高的应用来说,640是比较务实的选择。如果你做的是其他小目标检测场景,再把分辨率往上调也不迟。

4.3 训练过程监控与超参数调整

训练过程中我主要盯着三个指标:box_loss、cls_loss和dfl_loss。正常训练时,这三个损失都会在前期快速下降,然后进入平缓期。在epoch 80左右,我发现cls_loss出现了异常反弹,反复查了才发现是数据增强中的mosaic增强导致标签在拼接图边缘被切断,模型在中期开始学习到了不稳定的特征。解决方法是把mosaic概率从1.0降到0.5,并且在最后十轮训练中关闭mosaic增强,这才让损失曲线恢复平稳。

关于数据增强的另一条经验:上下翻转(flipud)在你训练宠物识别时要慎重。YOLO默认设置里flipud的概率是0,fliplr是0.5,这其实是符合直觉的——猫狗在水平方向上翻转不影响语义,但上下翻转会让模型学到“宠物倒立”这种不真实样本,如果部署环境中摄像头不存在俯仰翻转,建议保持默认,不要开flipud。我曾在一次测试中把Augment里所有增强项都拉满跑了个实验,结果训练集loss虽然降到很低,验证集mAP却掉了两个点,典型的增强过猛导致分布漂移。

5. 评估体系:mAP、混淆矩阵与真实场景验证

训练完成后,不能只盯着训练日志里的数字,完整的评估体系要包括定量指标和定性测试两部分。

5.1 定量指标解读

我跑完YOLOv8s之后,在验证集上得到的一组关键指标如下:

模型PRmAP50mAP50-95
YOLOv8s0.8850.8620.9120.684
YOLOv8n0.8470.8330.8910.635

很多人只看mAP50,觉得0.91已经不错了,但我更关注mAP50-95这个指标,因为它会以不同IoU阈值(从0.5到0.95,步长0.05)对模型做综合评价,更能反映检测框位置准确度。0.684对于一个两类的数据集来说,说明还有不少目标的预测框位置不够精准,尤其在小目标和遮挡目标上。

5.2 混淆矩阵里的信息

混淆矩阵是训练结束时自动生成的,YOLOv8会在 runs/detect/train 目录下输出confusion_matrix.png。我仔细看了这张图,发现除了dog和cat两个类别外,有一小部分目标被分到了background这一类,也就是模型在图上画了框,但实际上那个区域里没有猫狗。这些误检大多出现在光照极低、目标边缘模糊的图像里。

有一个值得注意的现象是:cat被误检为dog的比例比dog被误检为cat的比例略高。原因是数据集里有一些幼年萨摩耶和白色的布偶猫,它们都是白色长毛、圆脸大眼,在低分辨率下确实容易混淆。我在标注时已经尽量保证边界清晰,但模型还是学到了一些容易混淆的特征。针对这种情况,我给训练集额外补充了几十张白色长毛犬和白色长毛猫的近距离特写,单独又跑了一轮微调,混淆情况明显缓解。

5.3 真实场景验证比指标更重要

我始终觉得,验证一个猫狗检测模型是否合格,不能只看验证集mAP,更要去真实场景里测。我把训练好的YOLOv8s导出为ONNX格式,然后在一台只有CPU的Jetson Nano上分别测试了三段事先录好的视频:一段是白天小区里遛狗的画面,一段是室内猫从沙发后面走出来的画面,一段是夜晚光线昏暗时宠物在门边的画面。

结果很有意思。白天室外场景几乎零漏检,室内半遮挡场景的检出率也不错,但夜晚低光照场景下,模型依然能找到目标,只是框的抖动非常明显。后来我在预处理里加了自适应直方图均衡化,相当于在推理前对图像做了一次对比度增强,框的稳定性马上提升了。这个改动很小,但对于实际应用的视觉体验影响非常大。

6. 常见问题与排查技巧实录

整个项目做下来,踩过的坑确实不少。我把做得比较多的排查过程整理成一份速查表,方便你照着自查。

6.1 典型问题与对应解法

现象可能原因排查与解决办法
训练时loss为NaN或BN层崩溃学习率过高或数据中存在极端像素值降低初始学习率到0.001,检查图像是否有纯白/纯黑损坏文件
验证集mAP高但真实场景很差训练集与验证集分布不一致,缺失背景多样性增加场景扩充,不要只靠网图训练
某一类目标几乎检测不到类别样本数不均衡用数据增强、重复采样或补充收集该类图像
检测框不断抖动模型对低光照或运动模糊敏感推理前加图像增强预处理,用视频流做时序平滑
训练速度越来越慢数据加载瓶颈打开Dataloader的workers参数,图像全部转为JPEG编码统一格式
损失曲线后期反弹数据增强过猛关闭mosaic或降低增强概率,后期用微调方式训练

6.2 几个印象深刻的Bug

第一个让我印象特别深刻的坑是标签错位。有一批图像的标注文件是我手动批量改文件名时生成的,结果有一小部分图片和txt对不上——图片是狗,标签是猫。训练过程中损失曲线表现正常,但混淆矩阵里dog和cat之间多了一大块交叉区域。后来我写了一个脚本,随机抽了50张图,把标注框画回到原图上人工核对,立刻发现了问题。从此我再也不相信手动批量重命名,全部改用脚本生成同名文件。

第二个坑来自数据泄漏。我在统计验证集和训练集时,发现mAP50高到接近1.0,以为是模型神了,后面才排查到问题出在一小批图像从不同来源下载但内容完全相同,感知哈希去重只在初次筛选时跑过一遍,处理新增图像时忘了再跑一次。后面我把去重脚本集成到了数据处理流程的末尾,确保每次版本更新都重新做一遍去重。

第三个坑是标注框过小。YOLO训练时如果你把过小的目标框(比如宽度或高度小于图像尺寸的5%)直接喂进去,模型预设在特征图上对应的感受野会有问题,非常容易产生大量漏检。我的做法是在训练前跑一次统计脚本,把所有宽高占比过小的标注框筛出来,逐张判断是否属于真实小目标,如果属于,我会在训练配置里把anchor或者检测层的小目标分支调优;如果不属于,就删除这个无效框。

6.3 独家排查技巧

如果你不确定训练数据有没有问题,我教你一个懒人但有效的排查法:训练完成后,直接把验证集图像连同预测结果画框输出,保存到一张大图上,人眼快速扫一遍。这个操作比看任何指标都有用。YOLOv8在predict时会把结果存到runs/detect文件夹,你只需要把save=True打开,然后花十分钟翻一下预测图,模型哪里不行一目了然。

7. 数据集扩展与后续使用建议

这套数据集解决了“从无到有”的问题,但你如果想把模型做得更稳,有几个方向值得继续投入。

7.1 类别细分的扩展空间

当前数据集的类别只有dog和cat两类。实际业务里,用户通常还想知道“这是什么品种”或者“这是谁家的猫”。你可以在这套检测数据集的基础上,把每个检测框裁切出来,再做一个品种分类模型,形成“检测+分类”的两阶段方案。我实际试过,对检测框做IoU裁剪后,只用一个轻量级图像分类模型就能达到可用的品种识别效果,比直接训练一个多类别检测模型省力得多。

7.2 融入主动学习和数据闭环

如果是长期运营的线上系统,建议把每天的误检结果、漏检结果保存下来,每周人工审核一次,把确有价值的新样本补充进训练集。这种主动学习的数据闭环一旦跑起来,模型的泛化能力会持续提升,而不是训练完就固定在一个水平上。我自己早期做的时候没有这套机制,后来发现模型上线两周后遇到了一批新的场景(比如宠物店玻璃橱窗里的猫),漏检率明显升高,才意识到数据更新必须持续性。

7.3 与多模态、Transformer方向结合

如果你关注学术前沿,可以在猫狗检测任务上尝试两种进阶方向。一是把YOLO和Transformer模块结合,比如在Backbone后面加一个轻量级Transformer模块做全局特征建模,能帮助模型更准确地捕捉那些被遮挡目标的全局上下文信息。二是做“检测+跨模态检索”,比如根据文字描述去找对应的宠物个体,这对数据集结构会有更高要求,需要补充每个目标的属性标注。这套数据集虽不能直接满足多模态任务需求,但它作为基础的检测底座,能把目标区域准确裁出来,为后续的多模态对齐省去不少麻烦。

我在实际使用中把训练好的模型部署到一台带摄像头的边缘设备上,全天候记录小区里宠物出没的情况,前两周运行下来,白天场景的检测置信度基本稳定在0.8以上,夜间会降到0.7左右,但都没有出现完全漏检的情况。对真实项目来说,这样的稳定性已经具备落地价值了。

最后补充一个实用小技巧:训练完模型后,建议把模型导出成多种推理格式,比如PyTorch的.pt、ONNX的.onnx和TensorRT的.engine。不同部署平台对格式的兼容性不一样,我已经吃过“只在PyTorch下测试好用,一到嵌入式设备才发现不支持”的亏了。提前导出验证一遍,能让你的模型从实验室到生产环境的路顺畅很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 1:17:05

Linux Shell脚本入门:从创建到运行,Bash实战详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:17:02

深入理解CNN参数共享:从卷积核到图像分类的核心原理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:16:28

Playwright+Pytest实战:打造稳定高效的Web UI自动化测试方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:16:24

微信H5调用支付宝支付的合规实现方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:16:05

Win10重装不是一键的事:UEFI/BIOS/PE底层原理与实战排错

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/30 1:15:09

多源Transformer信贷评分:融合流水与文本的多源风控方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华