☰
基于YOLO的猫情绪识别:3200张宠物行为数据集构建与训练实践
2026/9/30 13:49:47 网站建设 项目流程

做宠物摄像头项目时,我卡在一个很朴素的问题上:猫到底处于什么状态?如果只是“有猫/没猫”,用现成人脸检测模型就行,但客户要的是“放松、警觉、紧张、害怕、玩耍”这种细粒度情绪反馈。翻遍公开数据集,猫狗二分类一大把,品种识别也不少,专门做猫情绪行为标签的却几乎没有。于是我整理了一套 3200 张 YOLO 宠物行为数据集,用来训练基于 YOLO 系列检测器的猫情绪识别模型。这篇文章的核心是把数据集的设计逻辑、标注规范、训练过程和排错经验完整写下来,给准备从零做小型行为检测方案的朋友当参考。

先说下数据集的定位:不是把猫框出来这么简单,而是让模型在输出目标框的同时,给出行为/情绪类别。从工程角度看,这套数据和 YOLO 的任务要求刚好对齐——目标检测天然具备“定位 + 分类”两个输出,不需要额外接分类头。3200 张图对于表情/姿态这类中粒度行为识别足够做 MVP。

1. 先说结论:3200 张图做猫情绪检测,够不够?

1.1 这个数据集到底解决什么问题

很多入门的同学会疑惑:猫情绪检测不是可以用图像分类模型吗?输入一张猫脸,输出情绪类别。但在真实场景里问题没这么简单。首先,宠物摄像头里猫的位置不固定,猫可能只占画面的一小块,直接用分类器会连背景一起学进去;其次,我希望系统在“无猫画面”下保持静默,在有猫时才触发情绪分析,这本质上就是一个目标检测任务。

所以我选择用 YOLO 来做:模型先产生一个边界框,把猫定位出来,再做类别判断。3200 张数据里,每张图都对应一个 txt 标注文件,标记了猫的位置和情绪标签。这套结构直接由 YOLO 格式规定,能省掉一大部分数据处理步骤。

1.2 3200 张规模怎么“够用”

很多做深度学习的朋友会习惯性追求大规模数据,动辄五万十万张,这在小团队自研项目里其实不现实。我这套“猫情绪检测数据集”定位是快速验证和场景化训练,3200 张是一个经过权衡的数量级。

  • 情绪识别是相对粗粒度的分类,不需要像人脸识别那样处理超级细的类间差异;
  • 迁移学习帮了大忙:使用 YOLOv8 或者 YOLOv5 在 COCO 上的预训练权重,模型已经具备了基础的物体边缘、纹理、形状感知能力,我们只需要微调最后几层和部分骨干网络;
  • 每张图包含一只或多只猫、含背景信息、含姿态变化,实际信息量远超过单价 3200 这个数字。

3200 张图并不意味着每张只有一只猫。数据集中有部分图包含两只甚至三只猫,按目标框数量算,可用于训练的样本目标数大概在 3800 个左右。加上训练时的 Mosaic、HSV、随机缩放等增强,模型见过的形态组合是非常丰富的。

2. 数据集从 0 到 1:情绪怎么定义、标注怎么落盘

2.1 情绪类别划分:5 类比“开心/难过”更可标注

做情绪数据集最怕定义模糊。如果你让标注员区分“开心”和“兴奋”,大概率每人给一个标准。我在设计最终数据集时,参考了宠物行为学里一些相对可观察的指标,而不是人的主观情绪投射。

类别ID类别名视觉行为线索标注样本量
0relaxed(放松)眼睛半闭、耳朵自然、尾巴慢摆、身体舒展820
1alert(警觉)耳朵向前/转动、瞳孔放大、身体定住760
2defensive(防御/紧张)飞机耳、弓背、炸毛、尾巴竖直640
3fear(害怕)压低身体、夹尾巴、视线回避、后退430
4playful(玩耍)伏低身体、尾巴快速摆动、扑咬前的姿势550

这套类别体系避开了“难过/开心”之类抽象情感,改用可观察的姿态和动作组合来解释。标签名最终定为英文,是为了后续直接喂给 YOLO 的 names 配置,减少转换成本。样本量分布上,我把害怕类刻意控制在 430 张,因为它和其他类别在视觉上有一定重叠,如果强行加太多模糊样本,反而会拉低整体 mAP。

2.2 文件目录与 YOLO txt 标注格式

整个数据集按照 YOLO 训练惯例组织,结构是下面这个样子:

cat_emotion_dataset/ ├── images/ │ ├── train/ # 2600张 │ └── val/ # 600张 ├── labels/ │ ├── train/ # 对应同名txt │ └── val/ └── cat_emotion.yaml

训练集和验证集按 8:2 划分,验证集覆盖全部 5 个类别,并且尽量保证验证集里出现的新场景不在训练集里出现过。比如同一只猫在不同光线下的照片,我只把其中一部分放训练集,另一部分放验证集,这样指标才不至于虚高。

labels 里每个 txt 文件与图片同名,例如relaxed_023.jpg对应relaxed_023.txt。文件里每行代表一个标注框,格式为:

类别ID 中心点x比例 中心点y比例 框宽比例 框高比例

拿一张具体的图举例:

0 0.5102 0.4335 0.2781 0.3412 2 0.1265 0.3100 0.3521 0.4120

这表示该图有两个目标框:第一只猫是 0 类(放松),第二只猫是 2 类(防御)。坐标全部归一化到图片宽高,这跟传统的 x1、y1、x2、y2 格式不一样,实测中转换时最容易漏的就是没有归一化,导致训练 loss 直接飘到 NaN。

2.3 标注过程中我踩的坑

标注这套数据花了我大概两周的碎片时间。第一个坑是框的粒度控制。一开始我习惯把整只猫框进去,结果发现 YOLO 模型对猫尾的识别很不稳定,因为尾巴的方向变化大且经常超出主体区域。后来我重新调整标准:目标框包含身体主干和头部,尾巴如果完全伸展且不贴近身体,就不强行包进去。这样训练损失更平稳,而且吹毛求疵地讲,情绪判断主要依赖头耳和前半身,尾巴是辅助线索。

第二个坑是极短时间内的重复帧。很多视频抽帧得到的图片,相邻帧的光线和姿态几乎一样,等于标注了两张几乎一样的图。这种重复数据会让验证集指标虚高。我处理的方法很简单:用图像感知哈希对候选图片做去重,相似度高于 0.95 的直接剔除,只保留一张。

第三个坑和文本标签有关:不要用中文标签,也不要在类别名里带空格。YOLO 的配置文件对 names 列表的读取非常敏感,我早期填过一个名字叫defensive_fear,模型训练没问题,但导出 ONNX 后推理结果和类别对不上,排查起来极其费时。建议全小写英文,必要时用下划线连接。

3. 用 YOLO 训练猫情绪的实操记录:选型、配置与损失函数

3.1 为什么最终用 YOLOv8

先说结论:选 YOLOv8 不是因为它在精度上碾压老版本,而是因为我需要一套能快速迭代、自带数据增强和评估可视化的工作流。

YOLOv5 和 YOLOv8 在小数据集上的差距并不大,甚至在某些任务上 YOLOv5 更稳,但 YOLOv8 在训练管线上更省心。它默认开启了 mosaic、mixup、随机 HSV、平移缩放等增强,不需要额外写预处理代码。它的模型设计里把 anchor-free 和 anchor-based 的优势做了整合,对小目标猫脸检测也比较友好。尤其是猫在画面里占比较小时,v8 的解耦检测头比 v5 的耦合头更容易收敛。

如果你想做实时部署,YOLOv8 也能一键导出 ONNX/TensorRT,不折腾。老版本还有 Anchor 大小需要考虑,在自定义数据集上可能需要手动聚类生成合适的 Anchor;YOLOv8 的解耦头方式让 Anchor 不再是主要超参数。建议从yolov8m.pt权重开始微调,而不是 n 或 s 起步。m 模型在 3200 张数据上的过拟合风险可控,特征提取能力却明显好于 n。

3.2 数据集 yaml 和训练命令

先提供一个最小可用的数据集配置:

# cat_emotion.yaml path: /path/to/cat_emotion_dataset train: images/train val: images/val nc: 5 names: 0: relaxed 1: alert 2: defensive 3: fear 4: playful

训练命令我用的是:

yolo detect train \ data=cat_emotion.yaml \ model=yolov8m.pt \ epochs=200 \ imgsz=640 \ batch=16 \ lr0=0.001 \ patience=30

这里解释一下参数选择。imgsz=640是均衡速度和精度的常用值,如果你希望模型更关注猫的耳朵角度等细节,可以上到 768,代价是显存和推理时间增加。对于带尾巴、四肢伸出等细长姿势的猫来说,稍微提高分辨率确实有一点收益,但 640 基本够用。

学习率 0.001 是我在踩坑后固定下来的。YOLOv8 默认的自适应学习率在小数据集上容易飙得过头,导致早期训练波动巨大。稳妥的做法是直接用固定学习率,配合patience做早停。patience=30表示连续 30 个 epoch 验证集指标没有提升就停止训练,实际中往往在 130 到 160 个 epoch 之间才会触发早停。

3.3 损失函数在猫情绪任务里怎么起作用

YOLOv8 的损失函数由三部分组成:分类损失、边界框回归损失、DFL 分布损失。很多同学习惯把这三大块打包理解,但在行为数据上有必要拆开想一想。

分类损失用的是 BCE(二分类交叉熵),它让每个检测框输出一个 5 维的概率向量,分别对应 5 个情绪类别。实际操作中,类别重叠的问题很常见:一只弓背炸毛的猫,它可能同时具备防御和害怕两种特征。BCE 允许一个框同时输出多个类别的概率,这就比 Softmax 多任务强一点。最终推理时我们用conf=0.5过滤低置信度结果,如果defensive和fear的概率分别是 0.7 和 0.6,系统会输出两个候选框,再由后处理合并。

边界框回归损失负责把猫框得更准。这里我补充一点为什么这对情绪识别很重要:如果框只框住猫头,模型会丢失身体姿态信息;如果框扩大太多,背景干扰又会混进来。回归损失的作用就是让模型自动找到信息最充分的框范围。我在实验中发现,框住“头+身体主干”比只框住头部的 mAP 高接近 5 个百分点。

DFL 损失是对边界框坐标做精细分布估计的机制。在猫尾巴甩动、四肢伸展这类边界不清晰的目标上,DFL 能减少框的抖动,让检测框边界不那么容易被毛发的边缘带偏。

3.4 数据增强里必须和 Mosaic“和解”的地方

YOLOv8 默认开启 Mosaic 增强,将四张图拼成一张训练。这个策略在通用检测任务上效果显著,但情绪行为识别里要小心处理。

我第一次训练直接使用默认参数,验证集上 mAP 在约 80 个 epoch 时突然掉了一截。后来排查发现是 Mosaic 把四只完全不同的猫拼在一起,模型被迫去学习跨图的碎片化特征,导致行为语义被切碎。猫的情绪特征依赖整体姿态,尤其是弓背、飞机耳这些线索,如果被中间线切开,反而变成有害噪声。

我最终的做法是:前 100 个 epoch 开启 Mosaic,之后关掉,再训练 30 个 epoch 让模型在真实比例的数据上重新校准。在命令行里可以通过mosaic=1.0和mosaic=0.0控制,或者直接分两阶段训练。这比一直开着 Mosaic 效果好,也比完全关掉收敛慢、泛化弱。折中方案是保留 Mosaic 但把拼接数量从 4 降到 2,也就是只用两张图拼接,语义破坏明显减轻。

4. 那两个把训练卡住的问题:BN 崩溃和混淆矩阵“总和不唯一”

4.1 BN 崩溃现场:怎么回事、怎么救

训练中我遇到的最典型异常是 Batch Normalization(BN)崩溃,表现为训练 loss 从 1.0 附近突然跳到 nan,或者验证集 mAP 在某个 epoch 之后持续归零。BN 层在 YOLO 骨干网络中无处不在,它通过统计当前批次数据的均值和方差来归一化特征。什么时候会崩?就是当批次内样本数量太少,或学习率设置过大时,统计量变得极不稳定。

我的第一次崩溃场景是:为了省显存,把 batch 调成 4,学习率用默认 0.01,结果在第 7 个 epoch 就出现 loss 升高,第 12 个 epoch nan。原因很简单,batch=4 时,BN 层每个 step 看到的只有 4 张图的统计量,而这些图可能是三只放松猫加一只害怕猫,分布差异极大,归一化参数被拽得来回抖动,最终训练崩溃。

解决办法有三步:第一,batch 提到 16,让 BN 统计量更稳定;第二,学习率降到 0.001,配合预热;第三,重新使用预训练权重而不是随机初始化。这三步做完之后,崩溃问题再没出现过。

如果 batch 大小受显存限制无法提升,另一个技巧是冻结前几层骨干网络训练。在 Ultralytics 里可以用freeze=10冻结模型前 10 层,这样 BN 层的统计量不会因为训练初期的混乱而崩坏,等后半段再解冻微调。这个方法对只有单张 GPU、显存吃紧的开发者格外实用。

4.2 混淆矩阵总和为什么“对不上”以及正确的读法

训练结束后,你会在runs/detect/train/confusion_matrix.png看到一张 6×6 的混淆矩阵(5 个情绪类别 + 背景)。很多同学第一反应是把每一行加起来和验证集样本数做对比,会发现“总合不唯一”,矩阵最后一行的数字和前面各行的汇总对不上。这不是数据集错误,而是目标检测混淆矩阵的计数规则和普通分类不同。

目标检测的混淆矩阵里,每个真实目标框要和预测框做 IoU 匹配。如果一个真实框同时匹配到多个预测框,只有置信度最高的那个算 True Positive,其他预测框会被计入错检;如果一个预测框没有匹配到任何真实框,则落入背景行。这意味着同一个目标可能会同时引发真实类别的正确计数和背景类的误判计数,因此行与列的总和并不守恒。

我在实际实验里遇到过具体的例子:验证集里有一张图包含两只猫,一只放松,一只警觉。模型正确框出了放松猫,却把警觉猫重复框了两个框,一个 IoU 0.63 算正确,另一个 IoU 0.44 落在置信度较低的重复位置,被计入背景误检。混淆矩阵里就会出现 relaxed 列 +1、背景列 +1,推送回真值背景总数却不变。

所以读混淆矩阵时不要追求“行和等于背景样本数”。你应该关注的是对角线上每一类的数值,以及哪些类之间最容易互相混。我这份数据集中,fear和defensive之间的互相混淆最明显,因为压低身体和弓背在部分角度下确实相似。后续改进方向是增加这两类的边界样本,而不是去检查矩阵加总。

5. 从 best.pt 到真实摄像头:指标、导出与时序平滑

5.1 我只看几个核心指标:P、R、mAP50

模型训练完成后,Ultralytics 会输出 Precision、Recall、mAP50、mAP50-95 等指标。很多新手只盯着 mAP50-95,觉得越大越强,但对行为检测项目来说,mAP50-95 并不完全反映落地性能。

我习惯构建一张分类别评估表:

类别PrecisionRecallmAP50
relaxed0.910.890.94
alert0.880.850.91
defensive0.870.840.90
fear0.810.760.83
playful0.860.820.89
overall0.870.840.91

从这张表里能读出的关键信息是:fear 类的召回率明显偏低。也就是说,有些害怕状态被模型看成了 calm,或者被漏检。为什么更在意 recall 而不是 precision?因为宠物摄像头场景里,漏报一只害怕的猫可能意味着用户错过应激事件;而把 relax 误判成 defensive,最多是多发一条提示。所以在推理阶段,我针对 fear 类把置信度阈值单独下调到 0.4,其他类别保持 0.5。

mAP50 是更贴近这类任务的指标,因为它对边界框的要求比较宽松:只要框中心和真实目标重叠程度尚可,就判定为正样本。宠物检测不需要像素级精确,mAP50 是更合理的评价标准。

5.2 导出为 ONNX/TensorRT 并在轻量设备上跑

最终要部署到嵌入式设备,我建议直接导出 ONNX 再转 TensorRT,而不是用 PyTorch 模型跑推理。PyTorch 模型的推理速度和内存占用在树莓派、Jetson 这类设备上完全不能接受。

导出 ONNX 的命令很简单:

yolo export model=best.pt format=onnx opset=12 simplify=True

导出时注意两个细节:一是opset不要贪新,选择 12 左右的兼容性比较好;二是simplify=True可以移除一些冗余计算节点,让 ONNX 在 CPU 或移动端运行时更快。

如果在 Jetson 设备上,可以继续转 TensorRT:

trtexec --onnx=best.onnx --saveEngine=best.engine --fp16

TensorRT 引擎通常能比 ONNX 再快 30% 到 50%。我测试过用 YOLOv8n 导出的引擎在 Jetson Orin Nano 上推理一帧 640×640 图,耗时大约在 18-25ms 左右,能勉强跑到 30FPS。如果换更大的模型,就要牺牲帧率来换取准确率,或者在摄像头端降低检测频率。

5.3 帧级检测不能直接用,加一阶时序平滑

最后一步也是最容易被忽略的一步:单帧检测结果在视频里会剧烈抖动。同一只猫在连续几帧里被识别成 relax、alert、relax,你会看到情绪状态在界面上来回跳,这种体验很糟糕。

解决方案是维护一个固定长度为 5 的队列,对最近几帧的情绪结果做投票:

from collections import Counter, deque frame_results = deque(maxlen=5) def update_status(detections): if detections: frame_results.append(detections[0].boxes.cls[0].item()) counter = Counter(frame_results) top_label, top_cnt = counter.most_common(1)[0] if top_cnt >= 3 and top_label != current_status: current_status = top_label return current_status

这段逻辑等于要求某一个情绪在 5 帧里出现至少 3 次,才认可状态变化。实测能显著降低误跳变。同时我也加入了置信度平滑:如果当前帧的最高置信度低于 0.5,就不把该帧结果存入队列,等于给检测器一个“犹豫权”。

有一点要说明:时序平滑会增加响应延迟,5 帧窗口在 30FPS 下大约延迟 170ms,对于人眼观察宠物状态来说完全可以接受。如果你要接入自动喂食器或逗猫棒这类需要快速响应的设备,窗口长度可以缩短到 3 帧。

最后想分享的经验是,情绪识别模型的成败一半在数据标注策略,一半在训练参数的稳定性。3200 张的“猫情绪检测数据集”绝不是一个小到可以被忽略的规模,只要类别定义清晰、标注边界统一、训练流程稳健,它能撑起一个可落地的宠物行为感知系统。后续扩展方向也很明确:增加同一只猫的不同机位视角,加入不同品种的样本,再引入时间序列信息,用若干帧的上下文做状态判断。恰好这套数据集的 txt 格式是 YOLO 标准结构,后续做视频级行为识别时,只需要按时间戳拼接检测结果即可,不需要返工整理。

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

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

立即咨询