1. 猫情绪检测数据集的项目缘起与整体设计思路做宠物行为识别这行的朋友应该都有体会猫的情绪标注比狗难做太多。狗摇尾巴、龇牙、耳朵后贴这些信号相对外显而猫是典型的“低表达欲望”动物疼痛、焦虑、慢性应激往往只体现在耳尖角度、瞳孔缩放、胡须朝向、尾巴摆动频率这些极细微的变化上。我最初接触这个方向是因为一个智能猫窝的项目需求客户希望设备能判断猫咪当前是放松、警觉还是紧张从而自动调节环境音量和灯光。当时市面上公开的猫情绪数据集几乎为零能找到的多是狗的行为数据集或者只是简单的猫狗分类。于是我们决定自己搭一套最终沉淀下来的就是这套3200张的YOLO格式猫情绪检测数据集。这套数据集的核心定位很明确面向目标检测任务用边界框标注猫的面部与身体关键区域并映射到情绪类别。它不是单纯的图像分类数据集而是把“情绪判断”拆解成可被YOLO系列模型学习的空间特征。为什么选择YOLO而不是分类网络因为在实际部署场景里摄像头视野中往往不止一只猫还可能有家具、人、其他宠物必须先做目标定位再做情绪归类。YOLO的单阶段检测架构在速度和精度之间平衡得比较好尤其是YOLOv8和后续版本对小目标猫耳朵、瞳孔区域的检测能力比早期版本强不少。数据集整体按照情绪类别分为五类放松、警觉、恐惧、攻击、疼痛。这个分类体系不是拍脑袋定的而是参考了国际猫科动物行为医学领域的经典研究框架同时结合了我们实际采集时的可区分度。比如“疼痛”和“恐惧”在面部表情上有重叠但疼痛更多表现为眼睛半闭、耳朵向两侧压平、胡须前伸僵硬而恐惧则是瞳孔放大、耳朵后贴、身体压低。为了让标注员能稳定区分我们在标注手册里给每一类都配了至少15张典型示例图和5张边界案例图。3200张的规模在目标检测数据集里不算大但胜在标注质量。每一张图都经过两轮标注和一轮交叉校验最终保留的边界框超过6800个。图像来源分三块一部分是我们自己用固定机位在猫舍和家庭环境拍摄的一部分来自合作宠物医院提供的诊疗录像截帧还有一部分是公开宠物视频中经过授权使用的片段。分辨率统一处理到640×640这是YOLO系列常用的输入尺寸兼顾了细节保留和训练效率。注意做动物情绪数据集最忌讳的是把“情绪”当成绝对标签。猫的情绪是连续谱同一只猫在3秒内可能从警觉转为放松。我们的做法是给每个边界框标注一个主情绪标签同时记录一个置信度分数0.6到1.0训练时可以通过损失函数加权来弱化低置信度样本的影响。这套数据集适合谁用如果你是做宠物智能硬件、宠物行为分析、动物福利监测的开发者可以直接拿去做迁移学习。如果你是想入门目标检测的学生这套数据集的类别定义清晰、标注规范适合用来练手YOLO训练全流程。甚至做动物行为研究的科研人员也可以用它来验证自己的情绪分类模型。2. 数据集构建的核心细节与标注实操要点2.1 情绪类别定义与边界框标注策略标注这件事最怕的就是定义模糊。我们一开始也走了弯路第一版标注手册里只写了“放松猫看起来很舒服”结果标注员把打盹的猫和刚睡醒的猫都标成放松但后者其实处于一种“低警觉”状态。后来我们把每一类拆解成可观察的物理特征组合标注员只需要对照特征表打勾最后综合判断。具体来说放松的特征包括耳朵朝前且自然直立、瞳孔呈细缝或中等大小、胡须自然下垂、尾巴缓慢摆动或静止、身体呈舒展姿态。警觉则是耳朵朝前但微微转动、瞳孔中等放大、头部抬高、尾巴快速小幅度摆动。恐惧耳朵向后压平或向两侧折、瞳孔明显放大、身体压低或蜷缩、尾巴紧贴身体。攻击耳朵后贴但身体前倾、瞳孔缩小、胡须前伸、尾巴炸毛或快速甩动。疼痛眼睛半闭或眯眼、耳朵向两侧平压、胡须僵硬前伸或下垂、面部肌肉紧张、身体蜷缩但头部低垂。边界框的标注策略也经过三次迭代。第一版只标猫的整身结果模型学到的特征太粗糙情绪分类准确率只有62%。第二版改成只标面部准确率上去了但遇到背对镜头的猫就失效。第三版最终采用双框策略一个框标整身用于定位和姿态判断一个框标面部区域用于表情细节判断两个框通过同一个情绪标签关联。这样模型在训练时可以通过多任务学习同时优化定位和分类。实操心得标面部框的时候一定要把耳朵完整包含进去。猫的耳朵角度是情绪判断的黄金特征如果框只切到眼睛和鼻子耳朵信息丢失恐惧和警觉的区分度会下降至少20%。我们后来统一规定面部框的上边界要高于耳尖至少10个像素。2.2 图像采集与预处理的关键参数采集环节的坑比标注还多。最开始我们用手机随手拍结果发现光照变化太大模型在暗光环境下几乎失效。后来固定了一套采集规范光源色温控制在4000K到5000K之间照度不低于300lux避免直射强光造成瞳孔剧烈变化。拍摄角度也有讲究正面、侧面45度、俯视30度三种角度各占一定比例因为实际部署时摄像头位置不可能永远正对猫脸。预处理流程分四步。第一步是去重用感知哈希算法把相似度超过95%的图片剔除这一步从最初的4100张砍到了3600张。第二步是分辨率统一所有图片短边缩放到640像素长边按比例缩放后居中裁剪保证猫的主体区域不丢失。第三步是色彩校正用灰度世界算法做白平衡减少不同设备带来的色偏。第四步是数据增强的预留我们不在原始数据集里做增强而是把增强策略写在训练脚本里这样用户可以根据自己的需求调整。这里重点说一下为什么不做离线增强。很多教程会建议先把数据集增强到几万张再训练但我们实测下来离线增强容易导致数据泄漏——同一张原图的增强版本可能同时出现在训练集和验证集里验证指标虚高。正确的做法是在训练时用albumentations或torchvision.transforms做在线增强每个epoch看到的增强样本都不同既扩充了数据又避免了泄漏。2.3 数据集划分与类别平衡处理3200张图按7:2:1划分训练集、验证集、测试集也就是2240张训练、640张验证、320张测试。划分时不是随机分而是按拍摄场景和猫的个体ID分层抽样。比如猫舍场景的图片不能全部分到训练集否则验证集里全是家庭场景模型泛化能力评估就不准。我们给每只猫分配了唯一ID确保同一只猫的不同图片不会跨集出现避免模型“记住”某只猫的脸而不是学情绪特征。类别平衡方面原始数据里“放松”和“警觉”占比超过60%“疼痛”和“攻击”加起来不到15%。直接训练会导致模型偏向多数类。我们的处理方式是对少数类做过采样强增强过采样倍率控制在1.5倍以内增强手段包括随机旋转±15度、亮度对比度扰动、高斯噪声、随机遮挡模拟部分遮挡场景。同时用Focal Loss替代标准交叉熵让模型更关注难分类样本。实测下来少数类的召回率从最初的0.41提升到了0.73。情绪类别原始数量过采样后数量主要增强手段放松980980基础增强警觉870870基础增强恐惧620780旋转亮度扰动攻击410620旋转遮挡噪声疼痛320560旋转遮挡噪声对比度注意过采样不是简单复制图片而是对同一张图应用不同的增强组合生成新样本。复制会导致模型过拟合到特定背景。我们用的是imgaug库的Sometimes策略每张图随机应用2到3种增强。3. YOLO训练全流程与核心环节实现3.1 环境搭建与YOLO版本选型环境配置这块我踩过的坑足够写一篇避雷指南。先说版本选择YOLOv5虽然经典但已经停止大版本更新YOLOv8是目前最稳妥的选择Ultralytics官方维护活跃文档齐全对自定义数据集的支持也最好。如果你追求最新特性YOLOv11在注意力机制上有改进小目标检测略强但生态还不如v8成熟。至于网上热传的“yolov26”截至我写这篇分享时并没有官方发布遇到打着这个旗号的资源要谨慎。硬件方面训练这套3200张的数据集最低配置是单卡RTX 3060 12GBbatch size可以开到16。如果要做消融实验或者调参建议RTX 4090或A100。CPU训练不是不能跑但一个epoch可能要半小时以上调参效率太低。内存至少32GB因为数据加载时会把图片缓存到内存里加速。存储用NVMe SSD机械硬盘在随机读取小文件时会成为瓶颈。环境搭建步骤我整理成可直接复制的命令conda create -n cat_emotion python3.10 -y conda activate cat_emotion pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics8.1.0 pip install albumentations1.3.1 opencv-python4.8.1.78实操心得ultralytics库更新很快不同版本之间的API有差异。建议锁定版本号不要直接pip install ultralytics装最新版否则可能遇到配置文件格式不兼容的问题。我习惯在项目根目录放一个requirements.txt把每个包的版本都钉死。3.2 数据集配置文件与目录结构YOLO格式的数据集目录结构必须规范否则训练脚本找不到文件。标准结构如下cat_emotion_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml是核心配置文件内容如下path: /home/user/cat_emotion_dataset train: images/train val: images/val test: images/test nc: 5 names: 0: relaxed 1: alert 2: fearful 3: aggressive 4: painful这里有个容易忽略的点nc和names的索引必须和标注文件里的类别ID严格对应。我们有一次因为把fearful和aggressive的顺序写反了训练出来的模型把恐惧判成攻击排查了半天才发现是yaml文件的问题。建议写完yaml后用脚本随机抽10张图可视化标注框和类别名确认无误再开始训练。标注文件是每张图对应一个.txt格式为class_id x_center y_center width height所有坐标都归一化到0到1之间。因为我们用了双框策略所以每张图会有两个标注行一个是整身框一个是面部框它们的class_id相同。训练时YOLO会把它们当作两个独立的目标来检测推理时再通过IOU匹配把面部框和整身框关联起来。3.3 训练参数配置与损失函数调优训练脚本我基于Ultralytics的train.py做了定制核心参数如下from ultralytics import YOLO model YOLO(yolov8s.pt) # 从预训练权重开始 results model.train( datadata.yaml, epochs150, imgsz640, batch16, workers8, device0, optimizerAdamW, lr00.001, lrf0.01, momentum0.937, weight_decay0.0005, warmup_epochs3, warmup_momentum0.8, box7.5, cls0.5, dfl1.5, hsv_h0.015, hsv_s0.7, hsv_v0.4, degrees10.0, translate0.1, scale0.5, shear2.0, perspective0.0, flipud0.0, fliplr0.5, mosaic1.0, mixup0.1, copy_paste0.1, patience30, save_period10, projectcat_emotion_runs, nameyolov8s_exp1 )重点解释几个参数。box、cls、dfl是损失函数的权重系数分别对应边界框回归损失、分类损失和分布焦点损失。默认值是7.5、0.5、1.5我们调参时发现把cls提高到0.8能改善少数类的分类效果但太高会导致定位精度下降。最终定在0.6比较平衡。mosaic和mixup是强增强手段mosaic把四张图拼成一张能显著提升小目标检测能力但训练后期要关掉否则模型学到的上下文关系不真实。我们的策略是前120个epoch开mosaic后30个epoch关掉做微调。学习率调度用的是余弦退火lr00.001是初始学习率lrf0.01是最终学习率相对于初始值的比例也就是最终降到0.00001。warmup_epochs3让学习率在前3个epoch从0线性上升到初始值避免一开始就大更新导致模型震荡。注意如果你用YOLOv8n或YOLOv8m学习率要相应调整。nano模型容量小学习率可以设大一点0.002m模型建议用0.0008。我试过用nano模型跑这套数据mAP50只有0.71换成s模型后到了0.83再往上到m模型提升就不明显了但推理速度慢了一倍。所以yolov8s是性价比最高的选择。3.4 训练过程监控与关键指标解读训练启动后Ultralytics会在runs/detect/下生成日志和权重文件。重点看几个指标box_loss、cls_loss、dfl_loss三条损失曲线是否稳定下降mAP50和mAP50-95是否持续上升precision和recall是否平衡。如果cls_loss震荡剧烈通常是学习率太大或者batch size太小。如果mAP50在某个epoch后突然掉下来大概率是过拟合了需要看验证集的loss是否同步上升。我们这套数据集训练到第80个epoch左右mAP50达到0.85mAP50-95在0.62左右。继续训练到120个epochmAP50微涨到0.87但验证集loss开始抬头说明过拟合了。最终我们取第95个epoch的权重作为最终模型在测试集上mAP50为0.86各类别的AP如下情绪类别AP0.5精确率召回率放松0.920.910.89警觉0.880.860.85恐惧0.840.820.80攻击0.810.790.77疼痛0.760.740.72疼痛类的指标最低这在意料之中。疼痛的表情特征和恐惧、警觉有重叠而且疼痛样本最少。如果你的应用场景对疼痛检测要求高建议额外采集更多疼痛样本或者用半监督学习利用无标注数据。混淆矩阵也值得细看。我们跑出来的矩阵显示恐惧和攻击之间的混淆率最高有12%的恐惧样本被误判为攻击。分析后发现主要是那些“炸毛但未攻击”的边界案例。后来我们在标注手册里补充了一条规则炸毛但身体后倾的标恐惧炸毛且身体前倾的标攻击重新标注了这部分样本后混淆率降到了7%。4. 常见问题排查与实战避坑指南4.1 训练不收敛与BN层崩溃的排查思路“yolo训练中bn崩溃”是搜索热词里经常出现的问题我也遇到过。现象是训练几个epoch后loss突然变成NaN或者BN层的running_mean和running_var变成异常值。根本原因通常是batch size太小导致BN统计量估计不准或者学习率太大导致梯度爆炸。排查步骤我总结成一张速查表现象可能原因排查方法解决方案loss变NaN学习率过大看前10个epoch的loss曲线降低lr0到0.0005或更低BN层参数异常batch size过小检查batch是否小于8增大batch或改用GroupNorm验证集指标震荡数据增强过强关闭mosaic和mixup对比降低增强概率mAP不上升标注错误可视化标注框重新校验标注文件显存溢出imgsz或batch过大看nvidia-smi降低imgsz到512或batch到8我个人的经验是训练前一定要做一次小规模过拟合测试。拿20张图关掉所有增强训练100个epoch如果模型能把训练集loss降到接近0且mAP接近1.0说明模型结构和数据管道没问题。如果连20张图都过拟合不了那肯定是代码或标注有bug。这个测试花不了10分钟但能省下后面几小时的无效训练。还有一个隐蔽的坑是类别ID从1开始而不是0。YOLO要求类别ID从0开始连续编号如果你从1开始训练时不会报错但模型学到的类别会整体偏移一位导致所有预测都错位。检查方法是看data.yaml里的names列表长度是否等于nc且索引从0开始。4.2 小目标检测效果差的优化手段猫的耳朵和瞳孔在640×640的输入下只有十几个像素属于典型的小目标。YOLOv8默认的检测头对小于32×32像素的目标检测效果一般。我们试过几种优化手段效果从低到高排列第一种是提高输入分辨率到1280小目标检测AP提升了8个百分点但推理速度从45FPS降到了12FPS实时性没了。第二种是修改锚框尺寸把最小的锚框从10×13改成6×8提升约3个百分点。第三种是在颈部网络增加一个P2检测层专门检测8×8以上的小目标提升约6个百分点但参数量增加了15%。第四种是用SAHI切片推理把大图切成小图分别检测再合并提升约10个百分点但推理时间翻倍。最终我们采用的是P2检测层适度提高分辨率到800的组合在精度和速度之间取得了平衡。具体做法是在YOLOv8的yaml配置文件里把head部分的检测层从P3、P4、P5扩展到P2、P3、P4、P5。代码改动不大但需要重新从头训练不能直接用预训练权重微调。实操心得加P2层后显存占用会增加约30%batch size要相应调小。另外P2层的特征图分辨率很高正样本数量会暴增建议把cls损失权重稍微调低否则分类损失会主导训练。4.3 模型部署与推理速度优化训练完的模型最终要部署到边缘设备上比如RK3588或者Jetson Nano。直接拿PyTorch的.pt文件去部署推理速度往往不达标。我们实测下来YOLOv8s在RK3588上用ONNX Runtime跑640输入下只有8FPS换成RKNN推理后到了22FPS再经过INT8量化后到了35FPS。部署流程分三步。第一步是导出ONNX用Ultralytics自带的export功能model YOLO(best.pt) model.export(formatonnx, imgsz640, simplifyTrue, opset12)第二步是ONNX转RKNN用RKNN-Toolkit2。这里要注意RK3588的NPU对某些算子支持不好比如SiLU激活函数需要替换成ReLU否则会回退到CPU执行速度反而更慢。第三步是量化校准用200张训练集图片做校准集INT8量化后精度损失控制在2个百分点以内。推理后处理也有优化空间。YOLO的输出是8400个候选框NMS非极大值抑制是耗时大户。我们用了两种加速手段一是把置信度阈值从0.25提高到0.4减少进入NMS的框数量二是用GPU版本的NMS替代CPU版本。在RK3588上这两招加起来让后处理时间从15ms降到了6ms。4.4 数据集使用中的法律与伦理注意事项最后说一个容易被忽视但很重要的问题。动物情绪数据集涉及动物福利和隐私。我们采集时遵循了几个原则不使用任何引起动物痛苦的方式诱导情绪所有疼痛和恐惧样本都来自自然发生的场景或诊疗录像不人为制造。家庭场景的图片都获得了饲主书面授权并且对背景中的人脸、门牌号等隐私信息做了模糊处理。数据集仅限用于动物福利和学术研究不得用于任何伤害动物的应用。如果你要基于这套数据集做二次开发建议在项目文档里明确使用边界。比如用于智能猫窝的情绪监测是合理的但用于训练动物攻击性武器就违背了初衷。技术本身中立但使用技术的人需要有底线。5. 从数据集到产品实际落地中的经验沉淀这套数据集从最初采集到最终形成可用的模型前后花了将近四个月。中间返工了两次标注三次调整训练策略部署阶段又踩了不少坑。回头看有几个经验值得分享给准备做类似项目的朋友。第一数据集的质量比数量重要得多。我们最初追求5000张的规模结果标注质量参差不齐训练出来的模型还不如后来精标3200张的效果。标注手册要写得足够细最好配图说明标注员培训至少半天前100张必须全检。第二情绪标签的粒度要匹配应用场景。如果你的产品只需要区分“舒服”和“不舒服”那五分类就过度设计了二分类反而更稳。我们后来给客户做方案时会根据他们的实际需求裁剪类别体系有时候三分类放松、紧张、疼痛就够用了。第三模型迭代是持续过程。上线后我们收集了用户反馈的bad case发现模型对长毛猫的情绪判断准确率明显低于短毛猫因为长毛遮挡了耳朵和瞳孔。后来专门补充了300张长毛猫的样本做微调准确率才追平。所以数据集不是一次性的要有持续迭代的机制。第四推理速度要和场景匹配。如果是实时监控35FPS是底线如果是定时抓拍分析5FPS也够用。不要盲目追求高精度大模型边缘设备上跑不动的模型没有价值。我们最终交付的模型只有7MBINT8量化后不到2MB在RK3588上稳定跑30FPS以上。这套数据集和训练流程目前已经在我们内部的几个宠物硬件项目里跑通了从智能猫窝到宠物医院的行为监测系统都有应用。如果你也在做类似的方向希望这些经验能帮你少走点弯路。数据集本身因为涉及授权问题不能直接公开但标注规范和训练脚本我可以整理成模板分享有需要的朋友可以按这个框架去构建自己的数据集。