☰
YOLO宠物识别实战:4300张猫狗数据集构建与训练调优全流程
2026/9/30 13:41:47 网站建设 项目流程

最近整理了一套猫狗检测数据集,一共4300张标注好的图片,专门用来做YOLO宠物识别训练。我把整个从数据整理、标注清洗、模型训练到评估落地的过程完整走了一遍,这里面的门道比想象中多得多。本期就围绕这套YOLO数据集,把构建思路、训练参数、踩坑记录和评估方法一次说清楚,给准备自己搞目标检测数据集的读者一个可直接参考的完整方案。

先说一个很多人容易忽略的事实:4300张图对于二分类检测任务来说,属于"够用但不算宽裕"的规模。它不像COCO那种几十万张的大规模数据集,但也不像那种几百张的Demo级数据。这个量级下,预训练权重、数据增强策略和超参数选择会比数据量本身更影响最终精度。所以我把重点放在:如何用合理的方法把这4300张图的潜力压榨到极限。

1. 数据集从哪来:4300张图的构成与标注规范

1.1 图像来源与筛选标准

这套数据集我按三个渠道整合:公开图库爬取约占60%、开源数据集中筛选约占25%、自行拍摄补充约占15%。重点不是来源多样,而是清晰度和目标占比的筛选。原图分辨率低于640×640的直接淘汰,因为YOLO默认输入尺寸一般是640,强行resize小图会让猫狗特征严重失真。

目标在画面中的占比也是硬指标。我设定了一个筛选阈值:标注框面积占整图面积的比例低于1.5%的样本剔除。原因很简单,猫狗这类非刚性目标,如果目标太小,下采样到640分辨率时可能只剩十几个像素宽,别说检测了,人眼都费劲。有统计显示,小目标(面积占比小于1%)在YOLO系列模型上的AP值普遍比中大型目标低20%以上,所以与其让模型在小目标上反复挣扎,不如一开始就把样本质量门槛提高。

还有一类必须淘汰的是"多目标密集"图。比如一张图里挤了七八只猫,虽然真实场景确实存在,但对二分类检测训练来说,密集小目标会让正样本的尺寸分布失衡,反而干扰模型对常规单猫/单狗场景的特征学习。我把这类图单独归到扩展包里,后续做难例挖掘时再用,不混入主训练集。

1.2 标注流程:矩形框还是多边形

这套数据集用的是YOLO格式的矩形标注框(xywh归一化坐标),标注工具用的LabelImg。这里有一条关键经验:宁可框大一圈,也不要框太紧。

很多新手标注猫狗时喜欢沿着身体轮廓勒得很紧,把耳朵尖、尾巴尖刚好卡在框边上。这种标注方式对检测头来说其实不友好。因为目标检测的训练本质是让预测框去回归标注框,而猫狗的耳朵、尾巴是高频形变区域,框太紧意味着"真值框"剧烈变化,模型很难收敛。实测下来,我把标注规范定为:在目标主体(头+躯干)外扩2%到3%的边距,将尾巴和耳朵尽量包含,但不硬包全部尖端。这样训练出的预测框稳定性明显提升。

另外标注时我把"遮挡超过50%的个体"直接设置为ignore,不在该图上标注该目标。这样避免了边界模糊的样本给损失函数带去噪声。虽然这套数据集是单类别的简化版(猫、狗各算一类),但好习惯要从简单数据集开始养成。

1.3 类别平衡与数据划分

先说类别构成:猫图2300张左右,狗图2000张左右,比例接近1.15:1。这个不平衡幅度很小,基本不影响训练。真正的重头在划分策略上。

我做了严格的三级划分:训练集3700张、验证集400张、测试集200张,比例大约是85:9:6。这里强烈建议不要用常见的80/10/10,因为你的验证集用途不只是调参,还要承担早停判断、学习率衰减触发等功能。400张验证集对二分类检测来说,统计波动已经足够了。

在划分时特别注意了同源去重:从同一视频流截帧得到的图片,必须全部放进同一个集合,不能一部分在训练集、一部分在测试集。否则模型在评估时等于"见过"测试内容的相邻帧,mAP会虚高几个点。这种问题在公开数据集上很常见,自己构建时务必规避。

2. 用YOLO训练自己的宠物检测模型:从配置到跑通

2.1 版本选型:为什么不用最新而选成熟的

这个话题几乎每次都会被问到。我直接说结论:这套数据集我选用的是YOLOv5(6.0版本之后的代码结构),而不是YOLOv8或者更新的版本,也不是论文版的YOLOv6/YOLOv7。

理由有三条。第一,YOLOv5的社区生态最成熟,从预训练权重、第三方部署工具链到各种答疑帖,遇到问题几乎都能搜到现成方案。第二,YOLOv8在检测头结构上引入了Anchor-Free和DFL(Distribution Focal Loss)的变体,理论精度更高,但对中小数据集的拟合稳定性不如YOLOv5的Anchor-Based方案直观。第三,我的后续部署目标是移动端和边缘设备,YOLOv5的ONNX/OpenVINO转换路径非常成熟,而新版本在OP算子兼容上偶有坑。

如果你非要问"是不是YOLOv8一定更好",我的回答是:在4300张这个规模下,模型结构带来的精度增益小于训练技巧带来的增益。先把数据处理和训练调参搞扎实,比纠结用哪个版本重要得多。

2.2 环境准备和目录组织

训练环境我用的是一张单卡RTX 3060 12GB显存,PyTorch 1.12 + CUDA 11.6 + Python 3.8。这个配置跑YOLOv5s非常从容,跑YOLOv5m也能勉强扛住。

数据集目录结构按YOLO惯例组织:

datasets/ └── catdog/ ├── images/ │ ├── train/ # 3700张 │ ├── val/ # 400张 │ └── test/ # 200张 ├── labels/ │ ├── train/ # 3700个.txt │ ├── val/ # 400个.txt │ └── test/ # 200个.txt └── data.yaml

每个txt文件对应一张同名图片,内容格式是一行一个目标:

class_id x_center y_center width height

所有值除以图片宽高做了归一化,取值0到1之间。类别ID映射:0代表cat,1代表dog。

2.3 data.yaml与模型配置文件的改写

data.yaml是YOLO训练的重要入口,我用的配置如下:

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

这里有一个细节:path参数建议写绝对路径,不要写相对路径。虽然YOLO文档说支持相对路径,但实际使用中如果训练脚本的工作目录变了,相对路径会直接报错,而且错误信息并不直观,排查起来浪费时间。

模型结构文件用的是yolov5s.yaml的简版,只改了nc:

nc: 2 depth_multiple: 0.33 width_multiple: 0.50

这个改动意味着模型从80类的COCO检测器变为2类的宠物检测器,最后一层卷积输出通道从255变为(4+1+2)×3=21。

2.4 预训练权重的作用

这一步是整个训练策略的核心。我选择的预训练权重不是别人随意训练的某个backbone,而是COCO数据集上train好的YOLOv5s标准权重。

为什么必须用COCO预训练而不是ImageNet分类预训练?因为YOLOv5的COCO权重自带完整的检测头参数,包括anchor尺寸的先验知识。而ImageNet分类预训练只有backbone部分,检测头要从头随机初始化。对于4300张小数据集来说,检测头随机初始化意味着前期收敛非常慢,而且容易掉进局部最优。

官方COCO预训练权重的下载地址在GitHub仓库的release页面,文件名是yolov5s.pt。下载后用如下命令启动训练:

python train.py \ --data /path/to/datasets/catdog/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --cache ram \ --project /path/to/runs \ --name catdog_v1

--cache ram表示把图片一次性加载到内存,这对中小数据集是巨大的速度提升。4300张640分辨率的图大约占用4到5GB内存,完全可接受。如果不加这个参数,每轮epoch都要反复读磁盘,训练时间至少慢3倍。

2.5 训练过程中的监控指标

训练开始后,不要只盯着终端输出的进度条。我习惯同时开启两个监控:一是tensorboard,二是直接观察runs/exp*/目录下的训练曲线图。

重点看三组曲线:

  • train/box_loss、train/cls_loss、train/obj_loss是否平稳下降;
  • val/box_loss、val/cls_loss、val/obj_loss是否在某个点开始反弹;
  • metrics/mAP_0.5和metrics/mAP_0.5:0.95是否持续上升后进入平台期。

如果val_loss在某个epoch后持续上升而train_loss还在下降,那就是典型的过拟合信号。此时早停机制一般会自动触发。但如果val_loss在训练前10个epoch就直线飙升,通常是学习率设置过大或数据有问题,要立即停下排查。

3. 训练过程中最常翻车的几个点:损失爆炸、BN崩溃与样本不均衡

3.1 损失函数曲线不正常时的排查顺序

YOLO的损失由三部分构成:边界框回归损失(box_loss)、分类损失(cls_loss)、置信度损失(obj_loss)。如果只看总loss下降但mAP不动,大概率是哪一项loss失衡。

我这次训练第一轮就遇到一个典型情况:box_loss和cls_loss都在降,但obj_loss在70多个epoch后开始小幅回升。查了代码后发现是我的数据增强参数设置的mosaic=1.0,也就是每张训练图都做了mosaic拼接。mosaic增强确实能大幅提升小目标检测能力,但也带来了副作用:拼接后的图片中,物体边缘常常被截断,导致中心点落在拼接缝附近的目标其obj_loss标定混乱。

解决方法是把mosaic概率从1.0降到0.5,并且只在训练前半程启用,后半程关闭。这个技巧在很多竞赛方案中都有提到。调整后obj_loss曲线变得平滑,最终mAP@0.5提升了约2个点。

3.2 BN崩溃的成因与干预

如果你训练YOLO时看到loss突然变成NaN,或者准确率陡然归零,不要慌,这基本是BN(Batch Normalization)层崩溃。

BN层在YOLO中是对特征图做归一化的关键结构。崩溃的直接原因通常是某个batch内出现极端激活值,导致方差计算变为0或负数,进而梯度爆炸。触发这个问题的常见诱因:学习率过大(尤其是使用warmup阶段结束时)、batch size过小(低于8)、数据类型溢出。

我的处理方案分两步。第一步,把batch size从16调整到24(3060的12GB显存用YOLOv5s可以承受),增大batch内统计量的稳定性。第二步,在训练前50个epoch使用0.01的学习率,配合warmup 3个epoch,不要一上去就是0.1这种激进值。做了这两步后再没出现过NaN。

3.3 猫狗不平衡与难例样本的显式处理

虽然4300张里猫狗比例不算失调,但在实际测试中发现了一个更隐蔽的问题:猫的样本里"黑猫"和"橘猫"占比偏高,而"白猫"占比偏低。狗这边则是"柯基"和"金毛"这类常见犬种占据了半壁江山。这种品种偏向会导致模型对少见品种的泛化能力明显不足。

我针对这个问题做了两步显式处理。第一,在数据增强阶段增加了一个基于hsv空间的随机色调扰动,把饱和度变化范围从默认的0.5扩大到0.7。这样能逼迫模型学会"颜色不决定类别"。第二,单独挑出60张白猫和30张罕见犬种图片,复制后做水平翻转、随机裁剪、高斯噪声三种增强,变成约180张补充样本塞回训练集。这属于最原始的"过采样+增强"手段,简单但极其有效。处理后测试集上白猫的AP提升了约8个点。

4. 模型效果评估:混淆矩阵、单类别AP与阈值调整

4.1 混淆矩阵到底怎么读

训练结束后,YOLO会在验证集上自动生成混淆矩阵confusion_matrix.png。这个图很多人只看一眼对角线就完了,其实信息量很大。

矩阵的行代表真实标签,列代表预测标签。对二分类检测来说,重点关注两个位置:

  • 真实"猫"被预测为"背景"的比例——这是漏检率;
  • 真实"狗"被预测为"猫"的比例——这是类别混淆。

我这次训练的结果显示,混淆矩阵中狗被误判为猫的比例约为5%,猫被误判为狗的比例约为2%。这个不对称性很有意思:狗的品种外观差异远大于猫,有些小型犬(比如马尔济斯、博美)在特征空间上确实与猫的距离更近。如果这个比例超过10%,就需要检查标注框是否张冠李戴,而不是盲目调模型。

4.2 单类别AP:比总mAP更重要的细节

训练日志里最终显示的mAP@0.5:0.95往往是两个类别取平均的结果。但只看这个平均值会掩盖很多问题。

我这次训练的最终mAP@0.5:0.95是0.87,看起来还行。但拆开看单类AP:

类别AP@0.5AP@0.5:0.95
cat0.940.89
dog0.920.85
整体0.930.87

狗的指标比猫低了约4个点。原因是狗的训练样本里运动模糊的图比猫多,而且狗的品种差异大导致类内方差高。知道这个差异后,我做的针对性操作是:在后续迭代中,给狗的图片额外增加了少许运动模糊增强,让模型见过更多"动态中的狗"。这个操作不久后就体现在AP上有了1个点的提升。

4.3 置信度阈值:改前先测,没有通解

YOLO输出的每个检测框都带一个置信度分数,但部署时的置信度阈值直接决定了精确率和召回率的平衡。

这句话值得反复读:置信度阈值提高,误检减少、漏检增加;阈值降低,漏检减少、误检增加。没有"最好"的阈值,只有"最适合你场景"的阈值。

我测试了三个阈值下的表现:

置信度阈值精确率召回率场景建议
0.25(默认)0.900.95通用场景
0.50.960.88误检代价高的场景
0.10.820.98漏检代价高的场景

如果你做的宠物自动喂食器、猫咪门禁这类场景,漏一次意味着"该触发时没触发",建议用0.25;如果你做的是宠物数量统计、安防告警这类误报会很烦人的场景,建议调到0.45到0.5之间。我在实际部署时选择了0.4,原因是测试集上有几张高难度的"猫趴着睡在狗窝里"的图,0.25时会出现重复误检框,0.4时干净很多。

5. 从这套宠物数据集展开:小数据集复用的进阶操作

5.1 数据扩充的底线:增广不等于造数据

很多教程教你用各种增强技术把图片"变多",比如旋转、裁剪、MixUp、CutMix、Mosaic等。我的观点是:数据增强是降低过拟合的手段,不是增加信息量的手段。它不能让模型学习到全新的特征,只是让已有特征在几何、光度、遮挡等维度上更鲁棒。

对4300张这个规模,我的增强组合是固定的三板斧:Mosaic(0.5概率前50轮开启)、随机仿射变换(平移范围0.2、缩放范围0.5)、HSV扰动(hue 0.015、sat 0.7、val 0.4)。这个组合不追求花哨,关键是每个增强项都经过消融验证。我踩过的坑是:不加选择的堆积增强操作(比如同时开MixUp和CutMix),会让模型在验证集上表现不错,但一到真实部署环境就"发飘"——因为训练分布被人为扭曲得太厉害了。

5.2 预训练模型换脑:从宠物检测到其他领域的小数据集迁移

完成猫狗检测后,同一套流程可以快速迁移到任意二分类检测场景,比如工业缺陷检测中的"有瑕疵/无瑕疵"、安防场景的"行人/车辆"。迁移时有一个关键诀窍:不要冻结backbone,要连同检测头一起微调。

初学者很容易被"迁移学习应该冻结前面层"的说法误导。那套逻辑适用于ImageNet分类任务迁移到医学影像这类分布差异较大的场景。但对YOLO来说,数据集的domain gap往往没那么大,冻结backbone会导致早期的低级特征(边缘、纹理)无法适配新数据集,反而限制精度上限。我实测过:冻结backbone训练100轮,mAP@0.5是0.88;不冻结从头微调100轮,mAP@0.5是0.93。结论很清楚——在自己数据量不太小时(超过2000张),让模型全参数参与训练收益更高。

5.3 从检测到实例分割的路径

这套数据集如果未来要升级成"区分图中多只宠物个体"的精细识别,可以把标注从矩形框升级为多边形分割掩码,然后用YOLOv8-seg或Mask R-CNN重新训练。

不过我不建议在数据量不足的情况下盲目上实例分割。分割任务对边界标注质量极其敏感,4300张图全部重新标注多边形的工作量巨大,而且你的标注一致性很难保证。更现实的路线是:先用检测模型跑出框,再把每个框裁出来单独做细粒度分类(比如区分品种)。这样等于把一个大问题拆成两个小问题,每个小问题都更容易用少量数据达到较好效果。

5.4 部署层面的细节:导出ONNX与INT8量化的实测结论

最后分享一下部署环节的实测体验。YOLOv5s训练完成后,导出ONNX非常简单:

python export.py --weights runs/train/catdog_v1/weights/best.pt \ --img 640 \ --batch 1 \ --include onnx \ --dynamic

导出后我在CPU(i7-1165G7)上分别测了FP32和INT8量化模型的推理延迟。FP32模式单帧约180ms,INT8量化后约95ms,几乎减半。但这里有一个隐藏的坑:INT8量化需要校准数据集,不是直接转就行的。

我用验证集中的100张图做校准数据,对量化模型重新评估后,发现mAP@0.5下降了约1.5个点(从0.93降到了0.915)。这个损失对宠物识别来说完全可以接受,因为宠物场景对延迟的敏感度远高于对那1.5个点的精度敏感度。如果你部署的设备性能更弱,这个精度损失换来的速度提升是值得的。


最后再分享一个小技巧:训练过程的随机种子很折腾人,同一个数据集同样的参数,跑两次最终精度可能差1到2个点。如果你发现你的结果比网上别人的结果低一点点,先别急着改结构,把训练脚本里的seed固定住,默认用0即可。很多所谓的"改进"其实只是随机波动,固定种子后你才能真正评估你的改动是否有效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询