
猫的情绪到底能不能被机器识别出来这个问题我在两年前第一次接触宠物行为分析项目时就想过。当时团队想做一个智能猫窝能根据猫的状态自动调节环境结果卡在了最基础的一步——怎么让模型知道镜头里这只猫到底是放松、紧张还是炸毛。人工标注太慢公开数据集又几乎没有专门针对猫情绪的。后来我们决定自己攒一个前后折腾了大半年最终沉淀下来一套3200张规模的猫情绪检测数据集标注格式直接对齐YOLO拿来就能训练。这篇文章就把整个数据集的构建思路、类别设计、标注规范、训练调参和实际踩过的坑完整讲一遍不管你是做宠物硬件、动物行为研究还是单纯想练手目标检测这套东西都能直接上手用。1. 为什么猫情绪检测这件事值得单独做一个数据集1.1 通用宠物数据集解决不了情绪粒度的问题市面上能拿到的猫类数据集绝大多数只标注了cat这一个类别。像COCO、Open Images这类通用数据集里猫就是猫顶多再分个幼猫成年猫。但情绪检测要的是更细的粒度——同一只猫耳朵前倾还是后压、瞳孔放大还是收缩、尾巴高举还是夹紧对应的情绪状态完全不同。你拿一个只标了cat的数据集去训练模型学到的只是这是不是猫根本区分不了情绪。我一开始也想过偷懒用通用数据集先训一个猫检测器再在上面接一个分类头做情绪判断。实测下来效果很差因为检测框的定位精度不够情绪相关的关键部位耳朵、尾巴、瞳孔经常被框在外面分类头拿到的特征图里根本没有这些信息。所以情绪检测必须从一开始就在标注阶段把粒度做细让检测框尽可能贴合猫的身体主体同时保证关键部位落在框内。1.2 猫情绪的表达方式和狗完全不同做这个数据集之前我查了不少动物行为学的资料也请教了做兽医的朋友。一个很反直觉的结论是猫的情绪表达比狗隐蔽得多。狗摇尾巴基本就是开心但猫摇尾巴往往是烦躁甚至攻击前兆。猫的耳朵、胡须、瞳孔、身体姿态、尾巴位置这几个维度是判断情绪的核心而且它们之间会组合出非常多的状态。比如耳朵前倾瞳孔放大身体后缩和耳朵前倾瞳孔收缩身体前倾是两种完全不同的情绪前者是恐惧后者是好奇或准备捕猎。如果数据集只按单一维度标注模型学到的映射关系就是错的。所以我们在设计类别时采用的是综合状态而不是单一部位特征这一点后面会详细讲。1.3 3200张这个规模是怎么定下来的很多人会问3200张够不够说实话如果做通用目标检测3200张确实偏少。但情绪检测有个特点类别之间的视觉差异相对集中而且我们做的是单类别多状态检测不是几百类的通用检测。经过几轮实验3200张在YOLOv8n这种轻量模型上已经能跑到比较可用的mAP了。更重要的是这3200张不是随便凑的。我们按情绪类别做了分层采样保证每个类别至少有400张以上的有效样本避免出现严重的长尾分布。实际训练时还做了数据增强把有效样本量又扩了一倍多。所以这个规模是经过验证的不是拍脑袋定的。2. 数据集的核心设计类别体系与标注规范2.1 情绪类别到底怎么分才合理这是整个项目里最纠结的部分。我们前后改了四版类别体系最终定下来的是五类放松relaxed、好奇curious、紧张nervous、恐惧fearful、攻击aggressive。为什么是这五类而不是更多第一类别太多会导致标注一致性急剧下降。我们做过测试让三个标注员独立标同一批图七类体系下的一致率只有六成多五类体系下能到八成五以上。第二这五类在实际应用场景里是有明确区分价值的。智能猫窝需要区分的是放松和紧张宠物医院监控需要区分的是恐惧和攻击五类刚好覆盖了主要需求。这里要特别说明一点我们没有把开心单独列出来。因为猫的开心在视觉上很难和放松区分强行分只会引入噪声。这是基于实际标注经验做的取舍不是理论上的完美分类。2.2 标注框的边界怎么定标注框的边界直接决定了模型能学到什么。我们的规范是框要包含猫的完整身体包括耳朵和尾巴但不包含明显不属于猫的物体。如果猫是蜷缩状态框就贴着身体轮廓如果是伸展状态框要覆盖到尾巴尖。有个细节很多人会忽略当猫的尾巴和身体分离较远时比如尾巴高高竖起框要不要把尾巴包进去我们的做法是包进去但会在标注文档里注明尾巴区域可能占比较大。原因是如果尾巴被切掉模型就学不到尾巴姿态和情绪的关联。实测下来包含尾巴的标注方式让攻击和恐惧的区分度提升了将近十个百分点。2.3 遮挡和模糊样本的处理原则真实场景里猫经常被遮挡比如躲在沙发后面只露出一个头或者背对镜头。这类样本要不要保留我们的原则是遮挡面积超过百分之五十的直接剔除遮挡面积在百分之二十到五十之间的保留但单独标记遮挡小于百分之二十的正常标注。模糊样本同理。运动模糊严重的比如猫在跑动如果还能辨认出耳朵和身体姿态就保留完全糊成一团的剔除。最终3200张里有遮挡标记的样本大约占了一成五这个比例在训练时通过增强策略做了平衡。3. 从零构建数据集的完整流程3.1 素材采集去哪里找这么多猫的图素材来源主要有三个渠道。第一是公开的宠物图像库这部分大概贡献了四成优点是画质好、背景干净缺点是情绪状态偏单一大部分都是放松或好奇。第二是跟几家宠物店和猫舍合作拍摄的这部分贡献了三成半优点是能拍到紧张、恐惧、攻击这些少见状态缺点是背景杂乱、需要大量清洗。第三是网络公开素材的筛选贡献了两成半这部分质量参差不齐但能补充一些特殊角度和光照条件。采集阶段有个经验不要只拍正面。猫的情绪在侧面和背面同样有表达尤其是尾巴和耳朵的姿态。我们最终的数据集里正面视角大约占五成五侧面三成背面和俯视占一成五。这个分布是刻意控制的避免模型只学会看脸。3.2 清洗什么样的图必须扔掉清洗环节我们扔掉了将近两千张原始素材主要分几类。第一类是分辨率过低的短边小于四百像素的直接不要因为YOLO输入通常要缩放到六百四十分辨率太低会导致细节丢失。第二类是猫占比过小的猫在画面里小于百分之十五的剔除这类图训练时会被大量背景干扰。第三类是重复或高度相似的用感知哈希做了去重阈值设得比较宽松避免把不同情绪的相似姿态误删。还有一类比较特殊多猫同框。如果两只猫情绪状态一致保留并分别标注如果情绪状态不一致我们选择保留但只标注其中情绪更明确的那只。这样做是为了避免标注歧义但代价是损失了一部分多猫交互的样本。如果你的应用场景是多猫家庭这部分可能需要单独补充。3.3 标注工具和流程标注工具用的是LabelImg虽然后来也试过CVAT和Roboflow但LabelImg胜在轻量、离线、YOLO格式导出直接。标注流程是三人独立标注加交叉校验每张图至少经过两个人确认。遇到分歧的样本由我这边做最终裁定裁定不了的直接剔除。标注格式就是标准的YOLO txt每行一个目标格式是类别索引 中心x 中心y 宽 高坐标都归一化到零到一之间。类别索引按字母顺序排列对应关系写在classes.txt里。这里提醒一句YOLO的坐标是归一化的不是像素值很多人第一次标容易搞错导出的框会跑到画面外面去。# classes.txt relaxed curious nervous fearful aggressive3.4 数据集划分的讲究训练集、验证集、测试集按七比一点五比一点五划分。但这里有个坑不能随机划分。因为同一只猫可能出现在多张图里如果随机划分同一只猫的图可能同时出现在训练集和测试集导致测试结果虚高。我们的做法是按猫的个体划分同一只猫的所有图只出现在一个集合里。另外测试集要保证每个类别都有足够样本而且尽量覆盖不同的拍摄条件。我们最终测试集里每个类别至少六十张光照、角度、遮挡情况都做了均衡。这样测出来的指标才有参考价值。4. 用YOLO训练这套数据集的实操细节4.1 模型选型为什么从YOLOv5换到了YOLOv8一开始用的是YOLOv5s跑下来mAP五十左右但推理速度在边缘设备上不太理想。后来换到YOLOv8n参数量更小mAP反而涨了两个点。原因主要是YOLOv8的anchor-free设计和更优的损失函数对小目标和密集场景更友好。猫的耳朵、尾巴这些关键部位相对身体来说算小目标YOLOv8在这方面的优势比较明显。如果你用的是更新的YOLOv11或者带Efficient Head的改进版也可以直接套这套数据。我们试过YOLOv11nmAP又涨了一个点左右但推理速度差异不大。选哪个版本主要看你的部署环境如果追求极致轻量YOLOv8n是稳妥选择。4.2 训练参数怎么调基础配置用的是预训练权重输入尺寸六百四十batch size根据显存来十六或者三十二都行。学习率初始设零点零一用余弦退火调度。训练轮数设了三百但实际在两百轮左右就收敛了后面基本是微调。这里重点说两个参数。第一个是数据增强我们开了mosaic和mixup但mixup的概率调得比较低只有零点一。原因是情绪检测对姿态敏感mixup把两张图叠在一起容易破坏姿态信息。第二个是类别权重因为攻击类样本相对少给它设了一点五倍的权重避免模型偏向多数类。# 训练命令示例 yolo detect train \ datacat_emotion.yaml \ modelyolov8n.pt \ epochs300 \ imgsz640 \ batch16 \ lr00.01 \ cos_lrTrue \ mosaic1.0 \ mixup0.1 \ cls1.5 \ namecat_emotion_v14.3 训练过程中最容易崩的几个点第一个坑是BN层崩溃。训练到一百多轮的时候loss突然变成nan查下来是某批数据里有个异常样本标注框宽高接近零。解决办法是在数据加载阶段加一个校验宽高小于零点零一的框直接过滤掉。这个坑很隐蔽因为标注的时候看不出来只有训练时才暴露。第二个坑是混淆矩阵不唯一。有段时间发现紧张和恐惧的混淆特别严重查了混淆矩阵才发现这两个类别的样本在视觉上确实有重叠。后来我们重新审视了标注规范把耳朵后压但身体不后缩统一归到紧张耳朵后压且身体后缩归到恐惧边界清晰之后混淆率降了不少。第三个坑是过拟合。训练集mAP到九十多验证集只有六十出头。加了dropout和权重衰减之后有所缓解但根本解决办法还是补充数据。我们后来又补了四百多张恐惧和攻击的样本过拟合问题才明显改善。4.4 评估指标怎么看主要看mAP五十和mAP五十到九十五两个指标。mAP五十反映的是能不能检测到mAP五十到九十五反映的是框得准不准。情绪检测里框得准不准直接影响情绪判断所以后者更重要。我们最终在测试集上mAP五十大概八十五左右mAP五十到九十五在六十二上下。除了mAP还要看每个类别的召回率。如果某个类别召回率特别低说明模型对这个情绪状态学得不好需要针对性补数据。我们当时攻击类的召回率最低补了样本之后才拉到和其他类别接近的水平。5. 实际部署和推理时的经验5.1 边缘设备上的推理优化如果部署在智能猫窝这类边缘设备上模型大小和推理速度是关键。YOLOv8n的权重文件大概六兆量化到INT8之后能压到两兆以内推理速度在树莓派这类设备上大概每秒五到八帧基本够用。如果设备性能更弱可以考虑进一步剪枝但精度会掉一些。有个实用技巧把输入尺寸从六百四十降到四百八十速度能提升将近一倍mAP只掉三个点左右。如果应用场景对精度要求不是极致这个取舍很划算。我们最终在猫窝项目里用的就是四百八十的输入。5.2 连续帧的情绪平滑处理单帧检测会有抖动同一只猫在连续几帧里可能被识别成不同情绪。解决办法是加一个滑动窗口做平滑比如取最近十帧的检测结果做投票取众数作为最终输出。这样处理之后情绪切换的稳定性明显提升不会出现放松-紧张-放松这种频繁跳变。窗口大小要根据帧率来定。如果帧率是十帧每秒窗口取十就是一秒的平滑比较合适。如果帧率更高窗口可以适当缩小避免响应延迟太大。5.3 误检和漏检的常见场景误检最多的情况是把玩偶、抱枕这类毛绒物体识别成猫。解决办法是在训练数据里加入负样本也就是不含猫但包含类似物体的图。我们加了大概两百张负样本之后这类误检明显减少。漏检最多的情况是猫蜷缩成一团或者只露出局部。这类样本在训练集里占比不高模型学得不够。如果应用场景里这种情况常见建议针对性补充数据或者在推理时降低置信度阈值用召回率换精确率。6. 这套数据集还能怎么扩展6.1 加入时序信息做情绪变化检测目前的数据集是单帧的只能判断某一时刻的情绪。如果想做情绪变化检测比如识别猫从放松到紧张的转变过程就需要时序标注。这个工作量很大但价值也高。我们正在尝试用视频抽帧加时序标签的方式做扩展初步想法是每段视频标注情绪变化的关键帧中间帧用插值。6.2 多模态融合的可能性单纯靠视觉判断情绪有局限比如猫的叫声、心率这些信息视觉上拿不到。如果做多模态融合把音频和视觉结合准确率应该能再上一个台阶。不过这需要额外的传感器和标注成本比较高适合有硬件条件的团队尝试。6.3 迁移到其他宠物这套标注规范和流程其实可以迁移到狗、兔子等其他宠物。狗的情绪表达更明显标注一致性应该更高。但类别体系需要重新设计不能直接套用猫的五类。我们试过用这套流程做狗的情绪数据集标注效率比猫高不少因为狗的姿态变化更外显。7. 一些零散但重要的实操建议标注的时候一定要定期做一致性检查不要等全部标完再查。我们当时标到一半才发现前期的规范有歧义返工了将近八百张非常痛苦。建议每标两百张就抽检一次发现问题及时调整。数据增强不要无脑开满。情绪检测对姿态敏感旋转、翻转这些增强要谨慎。水平翻转一般没问题但垂直翻转会导致猫的姿态看起来不自然建议关掉。颜色抖动可以开但幅度不要太大避免把毛色特征破坏掉。训练日志要留着。我们当时有个模型效果特别好但忘了记参数后来想复现怎么都复现不出来。现在养成习惯每次训练都把配置文件、命令行、环境版本全部存档方便回溯。最后说一句数据集的质量比数量重要得多。我们这3200张是精挑细选出来的实际采集的原始素材超过八千张。如果你也在做类似的项目宁可多花时间在清洗和标注上也不要为了凑数把低质量样本塞进去。模型是很诚实的你喂它什么它就学什么。