ARTICLE DETAIL

资讯详情

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

基于YOLO的猫品种识别:2400张数据集标注、训练与踩坑记录

基于YOLO的猫品种识别:2400张数据集标注、训练与踩坑记录 去年十月朋友拿了一包冻干来找我说要给自家小程序加一个猫品种识别的功能。一开始我想得很简单直接拿现成的开源模型顶上省事。结果真要跑的时候傻了眼——光线稍微偏一点的室内照片、只露个后脑勺的猫、和猫长得很像的玩偶摆件分分钟把置信度干穿。最后逼得我老老实实整理了一套猫品种检测数据集总共 2400 张全部用 YOLO 格式重新标注拿来做宠物识别。这套流程走下来我对“数据集”这三个字的理解彻底变了所谓“2400 张”听上去只是个数字真正值钱的是每一张图背后的标注标准、类别分布和训练细节。这篇就纯粹记录一下我踩过的坑和可复现的做法给同样想用 YOLO 做宠物识别的人一个参考。1. 2400张图听起来不少但真正决定模型上线效果的其实是这些细节1.1 数据集的构成6个品种、每类400张最终落地的版本我一共放了 6 个品种分布在 2400 张图里每个品类尽量控制在 400 张上下。选品种的时候没有闭眼乱抓而是参考了真实场景里最容易出现的猫英短、美短、暹罗、布偶、缅因再加一个中华田园猫作为“兜底类别”。为什么一定要留一个“兜底类别”这是我从第一版跑崩之后总结出来的。宠物识别和猫狗二分类不一样用户拍的照片里经常出现“不知道是什么猫”的情况比如小区楼下的流浪猫、串串猫甚至一只长得像猫的毛绒玩具。如果你只训练纯种猫模型遇到不认识的猫就会硬猜给一个低置信度的错误答案用户体验极差。有了中华田园猫这个类别反而等于给模型开了一条“我见过很多猫但我不确定具体品种”的退路。那 400 张一个类别的量够不够以我的实际结果来说勉强够训练出一个达到可用水平的 MVP但离精细投产还有距离。关键不在于总量而在于每一类内部的多样性。猛一看 2400 张挺多摊到 6 个类目上每类就 400 张。如果这 400 张全是同一种姿势、同一个背景、同一个光照环境模型的泛化能力会差得离谱。我后续补数据的优先顺序完全是根据模型在哪些照片上翻车来决定而不是继续均匀地给每个品类加图。1.2 “数量足够”和“数据合格”之间的差距很多人在数据集这个话题上有一个误区以为数量够了模型效果自然好。2400 张确实不算少但我整理到一半就意识到真正要花心思的是“合格”两个字。怎么定义合格我给自己定了几条硬标准每张图里的猫主体必须清晰可辨模糊到完全看不出轮廓的不能留。画质要贴近真实手机拍摄场景尽量混合一些低分辨率、逆光、夜间红外监控的画面。同一只猫不能在训练集和验证集里反复出现否则模型是“记住了猫”不是“学会了识别品种”。每张图只保留一个主目标框如果画面里有多只猫我尽量分开裁成独立样本。说实话筛选这 2400 张嘴比论坛上写教程的人想象的要枯燥很多。有一批图是从旧相册里翻出来的分辨率只有 400×400但是画面里的猫特别典型舍不得丢最后我用脚本统一做了分辨率统计把低于阈值的单独放在一个候选文件夹里后续专门测试模型对小图和模糊图的鲁棒性。这个做法后来救了我。2. 标注是这套数据集里最费手的一环也是回报最高的一环2.1 YOLO标签的标准姿势一行数字讲清楚一只猫用 YOLO 做检测标注文件不是用多边形或者圆形框而是用一行纯数字描述目标位置和类别。格式长这个样子class_id x_center y_center width height注意四个坐标全部是归一化坐标既不是像素值也不是左上角和右下角。比如一张 1000×800 的图片猫的边界框左上角在像素 (100, 200)右下角在像素 (900, 700)那么归一化之后x_center ((100 900) / 2) / 1000 0.5y_center ((200 700) / 2) / 800 0.5625width (900 - 100) / 1000 0.8height (700 - 200) / 800 0.625这一行最终写进 txt 里就是0 0.5000 0.5625 0.8000 0.6250类别的 0、1、2 与配置文件中的 names 列表一一对应。如果你自己写程序转换坐标千万注意不要拿左上角和右下角的坐标直接除以图片宽高宽度和高度也必须归一化否则 YOLO 训练时会把框的位置算到火星上去。我实际整理这套猫品种检测数据集时并没有手工去敲每一行 label而是先用标注工具画框再统一脚本转成 YOLO 格式。标注文件命名必须和图片文件完全一致只是后缀不同IMG_001.jpg对应IMG_001.txt。这个细节看似简单但我曾经因为大小写不一致训练时发现一个类别的样本量突然少了三分之一排查了半天。2.2 那些让标注标准反复摇摆的边界情况标注标准看起来是“画出猫的位置”这么一句话但实际执行时会遇到大量边角案例。我在这套数据集上碰到过三类最折磨人的情况第一类是遮挡。猫蜷在沙发里只露出一截尾巴或者半个头画面里根本没有完整的躯体。我的标准是只要能看到明显的可辨识特征比如两只耳朵加一只眼睛或者一整条尾巴连着一部分臀部就按可见主体画框如果只剩毛茸茸的一团连朝向都分不清直接丢弃。因为这种图放进数据集里会教模型把类似“毛球”的东西当成猫。第二类是品类之间的视觉重叠。英短和美短在毛色、脸型上都高度相似如果只拍一个大头照标注的边界非常模糊。我在标注规则里加了一条判断依据脸部轮廓的圆润程度、耳距、毛发的长度和花纹走向。但这个标准不是所有人看了都能立刻执行于是我后来把容易混淆的品种图单独建了一个“待讨论”文件夹人工二次复核。整个数据集整理完我复检了差不多 600 张图。第三类是多目标场景。客厅里两只猫并排坐着脚边还有一只布偶。按检测逻辑每只猫都应该有各自的框。但我为了控制难度只保留画面中面积占比最大的那只其他小目标裁切掉。这样做的原因是 2400 张图本身类别样本就少如果一张图里出现两三个不同品种的框模型会把上下文信息也当成品类特征产生耦合。等你训练完发现“蓝眼睛的白猫一定出现在地毯上”这种诡异的隐性规律时想回头清理已经来不及了。2.3 训练集和验证集切分一个被低估的“作弊点”切分数据集看起来是最简单的操作随机抽样 80% 训练、20% 验证就行。但这里有个隐藏的坑同源图片泄漏。我只用照片编号做种子随机切分时验证集里出现了一批和训练集几乎一模一样的连续拍摄帧。比如一只猫从趴着到抬头的全过程连拍了很多张切分算法把其中的 100 张放进了训练集又把另外 40 张放进了验证集。结果验证集指标虚高一到真实场景就露馅。解决方法是按“场景/组”做切分。我先把来源相同的照片分组同一只猫、同一个时间段、同一背景的照片算一组然后按组去切分而不是按单张图切分。这样能最大程度保证验证集是模型没见过的场景组合。我最后切出来的分布是训练集 1920 张、验证集 480 张6 个类目的比例基本一致没有出现某一个品种的训练量是另一个品种两倍多的失衡情况。切分时记得固定随机种子我用的 42。这个小细节能让你在复跑实验时保证每次喂给模型的数据顺序一致排除了偶然性因素后面再调模型参数才有可比性。3. 拿这份数据集跑 YOLOv8配置、命令和坐过的坎3.1 模型体积选择我用 YOLOv8n 起步省下的时间都花在调上面了YOLO 的版本和变体很多从 yolov5 到 yolov8再到各种搭配注意力机制的自定义版本。我没有一上来就上最重的模型而是选用了 YOLOv8n 作为 baseline。原因很现实2400 张数据对一个大模型来说太少了。n 模型参数量小训练快过拟合风险也相对低更适合在小数据集上快速验证标注质量和训练流程是否正确。如果你问我什么时候该换更大的模型我的经验是先看 mAP 是不是已经卡在一个明显偏低的值上。如果验证集 mAP0.5 连 0.7 都不到那大概率不是模型容量的问题而是数据本身有问题换成 l 或者 x 只会让过拟合更严重。反过来如果 mAP 已经超过 0.85而你还想继续提升边界框精度这时再换大模型才有意义。在本地跑的时候我用的是一张单卡 V100batch size 设为 16输入尺寸 640训练一轮大概十几分钟。n 模型一晚上能跑完上百个 epoch非常舒服。如果只有 CPU我建议直接把整个训练测试流程简化成“先跑 30 epoch 观察损失有没有下降”别一开始就追求完整训练。3.2 配置文件与训练命令数据集的配置文件我用 YAML 写法放在数据集根目录下path: /path/to/cats-dataset train: images/train val: images/val nc: 6 names: 0: British Shorthair 1: American Shorthair 2: Siamese 3: Ragdoll 4: Maine Coon 5: Domestic Cat训练命令用的是 ultralytics 的命令行方式yolo detect train \ datacat.yaml \ modelyolov8n.pt \ epochs200 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ seed42 \ projectcat_detect \ namev8n_run1这里我有几个刻意做的选择。用了预训练权重yolov8n.pt而不是随机初始化yolov8n.yaml。2400 张图从零训练完全不够迁移学习的收敛速度和最终精度会好很多。patience20意思是连续 20 个 epoch 验证集指标没有提升就自动停止省时间。lr00.01YOLOv8 默认的大致是这个量级数据量小的时候学习率太高容易震荡太低又收敛太慢。这个参数我在跑完前 50 个 epoch 后观察 loss 曲线如果曲线像锯齿一样上下跳就降一半。训练完成后模型权重和验证集预测结果都会输出到cat_detect/v8n_run1/目录下里面有weights/best.pt和weights/last.pt。我在这个项目里一直只用 best它对验证集表现最好last 往往已经过拟合。3.3 训练过程中三个常见崩溃点和解决办法这一节我总结了这次训练里真正中断过实验的三类问题第一类是“BN 崩溃”。症状是训练到某一个 epoch 后损失突然飙升成 NaN或者直接溢出。网上搜“yolo 训练中 BN 崩溃”会得到一堆答案但我的实际排查结果很简单batch size 设得太小。我在 V100 上为了同时跑多个实验把 batch 从 16 降到 4结果 BN 层统计信息不稳定几十个 epoch 后数值就崩了。解决办法是把 batch 调回 16或者同时开启--batch对应的梯度累积。如果你显存不够与其硬塞小 batch不如减小 imgsz 到 480。第二类是“系统性误检”不是崩溃而是指标看着还行但跑真实图片时大量误报。原因出在我之前说的“待讨论”样本上。有一批超低分辨率的英短照片被塞进了训练集模型学到了一个奇怪的纹理特征导致它在看到毛毯上的花纹时也给出高置信度。这个问题的唯一有效解法是清洗训练集不是靠调参。第三类是“混淆矩阵总合不唯一”这类疑惑。你可能会看到 sklearn 生成的混淆矩阵行总和不是 100%列总和也不是一致的值因为不同类别的真实样本数量不同按类别归一化之后每一行独立为 100%列自然对不上。别纠结是不是代码写错了重点看行方向的召回率分布这样更容易定位哪些品种被漏检。4. 评估阶段把“模型的错”和“数据集的错”分开看4.1 mAP、精确率、召回率先看懂钱花在了哪里YOLO 训练结束后会直接输出一堆指标但大多数人只看 mAP 就走了。我建议你在看总指标前先按类别看一次 precision精确率和 recall召回率。这两个指标在猫品种识别场景里的含义很直白精确率模型说“这是暹罗猫”说对的比例。召回率画面里真是暹罗猫模型能找回来的比例。品牌更关注精确率因为用户上传一张普通橘猫照片你告诉他“这是英短”比“我不确定”更伤信任。但在实际业务里召回率低了用户也会生气因为真问的是布偶结果被识别成缅因等于功能没用。所以我要做的不是追求某一个指标而是看“误判都发生在哪些品种之间”。4.2 最容易混的猫英短、美短和“看起来都像”的照片拿到验证集逐类指标后我立刻看到了问题英短和美短的混淆特别严重。这两种猫在标准正面照里其实有明显差异英短脸更圆、鼻子更短、整体骨骼更粗但一到仰拍、侧脸、逆光环境下两者的全身轮廓几乎一样。模型在验证集上对美短的召回率只有不到 0.8大量美短被误判成英短。解决这个问题的常规思路是加大两类样本量但我更推荐先做“困难样本重标注”。我把所有被模型预测错误的图导出来按类别汇总然后一张一张看。结果发现很多误判图其实是角度太刁钻比如猫从高处俯视镜头、整个脸处于严重透视变形中人眼都会犹豫模型自然更容易混。这些图我分为两类一类保留用来增强模型对极端角度的鲁棒性另一类直接删掉因为它们连“标签是否正确”都很难确认。布偶和缅因的混淆则更多来自毛色。布偶的重点色块分布和缅因的条纹在某些光线条件下看起来很像再加上长毛猫的轮廓本来就模糊边界框里可能只有一风吹起来的毛。这部分我通过增加“强调身体轮廓清晰度”的标注标准来改善同时补充了更多自然光下的全身照。4.3 用验证集预判真实场景生活照和标准图册的差距数据集里如果全是精修图模型在真实手机照片上一定会打折扣。我这次特别做了一个测试单独留出 120 张没有参与训练的生活照照片来自随机场景主观评价标准是“用户随手拍也能识别”。结果和验证集指标差距非常大。验证集 mAP 看起来有 0.86但在生活照测试集上直接掉到 0.7 出头的水平。差距来源很明显生活照里猫不是中心构图经常背景杂乱、目标偏小再加上手机自动调焦导致局部模糊。这个现象给我提了个醒在评估阶段不能只依赖 YOLO 自动切出来的验证集你得额外交代一部分“伪真实数据”来当试金石。后来我用训练初期的权重在生活照上做了一轮失败分析发现最大的漏检发生在“猫只占画面 15% 以下”的情况。解决办法很直接在训练时用imgsz640跑推理时把输入尺寸提高到imgsz960小目标特征保留得更多漏检有明显改善。代价是推理变慢但换召回率的提升是值得的。5. 这套数据集的后续扩展比继续堆图更值得做的事5.1 主动学习先从难度样本开始整理完第一版 2400 张 YOLO 宠物识别数据集后很多人会下意识继续加图到 5000 张、10000 张。我想说的是“图源不同边际收益完全不同。”与其均匀补图不如把模型在验证集中得分低、置信度接近阈值的样本全部挑出来排序后人工重标注一遍。这个流程叫主动学习听起来很高级实操起来非常简单把模型跑过的未标注图片按预测置信度从低到高排序人工优先标注那些置信度最低的图。因为这些图大概率对应最难分类的猫标完它们比标十张标准图有价值的得多。我还把训练过程中的增强参数单独拎出来做了实验。YOLOv8 自带 mosaic 和翻转增强我用默认配置跑了一个版本再手动关掉 mosaic 跑了一个版本。结果很有意思mosaic 确实大幅增加了数据多样性但在 2400 张这种规模下训练后期出现过拟合迹象更晚的反而是关闭 mosaic 的版本。可能是因为 mosaic 生成的合成图里猫的边缘太假模型学到了不真实的形状特征。我建议你按自己的数据规模做小范围消融不要迷信默认参数。5.2 部署时把阈值和NMS调成一个更“识趣”的状态训练完模型不是终点部署时还有一个被忽略的环节置信度阈值和 NMS 策略。训练默认的置信度阈值一般在 0.25但用 0.25 跑生活照杂七杂八的物体很容易被识别成猫。我调参时把置信度提高到了 0.45误检明显减少代价是少量置信度本身很低的猫被漏掉了。上线场景如果偏“娱乐向”可以容忍偶尔漏检但不能胡说八道那阈值不要低于 0.4如果是辅助人工审核阈值降到 0.3 反而更有帮助因为模型多“犯错”一些人工还能兜底。NMS 的 IoU 阈值用的是默认 0.45这在猫品种检测里够用。唯一要注意的是密集多猫场景好几只猫堆在一起时NMS 会把两个相邻的框合并成一个导致只检测出两只但漏掉第三只。遇到这种情况我可以把 IoU 阈值调高到 0.6让 NMS 更“克制”尽量保留互相重叠但可能属于不同猫的框。调完以后可以再用前面拆出来的那批常见图片复测一遍别只靠验证集拍脑袋。整套流程走下来我觉得最有价值的收获不是那个接近可上线效果的 YOLO 模型而是这一套“先让数据合格、再让模型收敛、最后让指标讲人话”的做事顺序。如果你手头也有一套刚下载完或者刚整理好的宠物图片我建议你先把标注标准写清楚再花半天时间做一遍同源去重最后再点火训练。这一步省下来的时间会比你在调参里抠出来的多得多。
返回列表