
做宠物行为分析这行最难搞的往往不是模型结构而是数据本身。尤其猫情绪检测比狗子识别要麻烦一个量级。狗的情绪至少尾巴、耳朵、姿势都相对直白猫这东西喜怒哀乐全藏在耳朵角度、瞳孔大小、胡须松紧、尾巴摆动幅度这些细节里再加上猫天生爱躲镜头一个场景里可能只给你露出半张脸或者一条尾巴。最近我在整理一套 3200 张的 YOLO 宠物行为数据集专门用于猫情绪检测从图片收集、标注规范、格式转换到 YOLOv8 训练调参整个流程都踩了一遍今天把做得通的部分和掉过的坑一起整理出来给后面想拿 YOLO 做宠物行为识别的朋友一份可以直接照着干的参考。先说结论这套 3200 张的数据集对单猫、少遮挡、面部清晰的场景训练出来的模型效果是够用的mAP50 能到 0.85 上下。但如果你想直接拿去应对多猫混战、极端光线、或者只有背影的监控画面那还得在数据增强和标签细化上多下功夫。文章后面我会把类别定义、标签规则、训练参数、坑位清单全部列出来新手可以直接抄作业有经验的朋友也能对照检查自己项目里容易忽略的细节。1. 这个数据集到底解决什么问题1.1 猫情绪识别的真实场景与痛点先聊个很现实的问题你做个猫情绪检测到底是给谁用我给三个最常见的使用场景。第一个是宠物摄像头。现在很多养猫家庭装了家用摄像头白天上班想看看猫在家里干嘛单纯看录像没意义得靠算法做事件摘要比如“猫在焦虑地舔毛”“猫炸毛了”“猫躲起来了”这就需要模型能实时检测猫的位置和行为状态。第二个是宠物医院和寄养中心用来评估动物福利状态猫在陌生环境里的应激反应很值得关注耳朵后背、不停舔嘴唇、蜷缩角落都是典型信号靠人眼盯监控不现实检测模型可以做辅助评估。第三个是宠物内容平台比如自动给猫视频打行为标签方便内容分类和推荐。这几个场景有一个共同要求模型得同时输出“猫在哪”和“猫什么状态”。这就决定了不能用纯分类模型。你拿 ResNet 去做图片分类它只能告诉你这张图里猫是高兴还是紧张但猫在哪、是哪只猫、什么时候开始情绪变化全都不知道。而 YOLO 这类目标检测模型天然输出边界框和类别框住猫的位置同时给出情绪类别一套流程把定位和分类全干了。这也是我坚持用 YOLO 而不是分类模型的核心原因。1.2 YOLO 在这里扮演的角色YOLO 的强项是单阶段检测速度快、结构简单、训练流程成熟。对于猫情绪检测这种任务你不需要像做自动驾驶那样追求极致精度你更需要的是能在边缘设备上跑得起来、能处理视频流、能快速迭代标注数据。具体到 YOLOv8 这个版本它在 Backbone 和 Neck 上的改进让特征提取更高效对不同尺度的目标更友好。猫这种目标有个特点大小跨度非常极端。近距离拍摄时猫头能占画面三分之一远距离监控画面里猫可能只有 20 像素大小。YOLOv8 的多尺度检测头P3/P4/P5就是用来应对这种跨度的。配合适当的分辨率设置能在不牺牲太多速度的情况下兼顾大小目标。另外 YOLO 的生态实在太好用。Ultralytics 提供的训练接口封装得很完善数据格式、数据增强、训练可视化、模型导出一条龙哪怕你是第一次接触目标检测也能在半天内跑通一套 baseline。对于情绪检测这种需要反复迭代数据标注的任务来说工具链的成熟度直接决定项目能不能跑下去这点比模型精度本身更重要。2. 3200 张数据集的内容架构与标注规则2.1 情绪类别如何定义别凭感觉拍脑袋做情绪检测数据集第一关就是类别定义。猫的情绪不是一个离散的标签它是一段连续光谱从放松到紧张再到恐惧中间还夹杂好奇、攻击性、捕猎本能等状态。如果一开始就把类别定得太细比如分 12 种情绪标注人员会疯掉模型也会疯掉——很多类别之间差异极小标注一致性根本无法保证。我最终把类别收敛到 6 个这 6 类在行为表现上有足够明显的可区分特征类别判断特征行为/姿态典型场景Relaxed放松眼睛半闭或正常睁开、耳朵自然前竖、身体舒展趴着晒太阳、正常睡觉Alert警觉耳朵竖直朝前、瞳孔略放大、身体微蹲听到陌生声音、注视移动物体Anxious焦虑耳朵向两侧或后方压低、频繁舔嘴唇、尾巴快速甩动陌生环境、医院候诊Fearful恐惧耳朵完全后背贴头、瞳孔放大、身体压低弓背受惊吓、躲藏状态Aggressive攻击瞳孔收缩、耳朵平贴或竖起、张嘴露牙、炸毛领地冲突、防御反击Playful/Hunting游戏/捕猎瞳孔放大、身体伏低、臀部抬高、尾巴缓慢摆动逗猫棒互动、盯猎物这 6 类覆盖了宠物摄像头和医院评估的绝大多数需求。注意我把 Playful 和 Hunting 并在一起是因为两者姿态特征高度重合都是伏低、瞳孔放大、臀部抬高强行分开会让标注一致性大幅下降模型学起来也会在两者之间反复横跳。标注边界是最容易出问题的地方同一张猫图不同标注员可能对“警觉”和“焦虑”给出不同判断。我的做法是在标注规范里加行为特征清单要求标注员必须看到至少两个特征才能选对应类别一眼看不出来的图直接标为不确定并丢进待确认池不硬标。2.2 数据收集与筛选3200 张是怎么凑出来的3200 张听起来不多但考虑到标注成本和场景覆盖已经能支撑一个可用的 baseline。我收集数据的来源主要有三块公开宠物数据集比如 Oxford-IIIT Pet Dataset 中可用部分、自己拍摄的家养猫、以及从合法授权渠道获取的用户投稿素材。这里特别提一句网络爬图要谨慎涉及版权和隐私问题最稳妥的做法是用开源数据集叠加自己采集的真实场景。数据筛选遵循几个硬性规则画质底线分辨率低于 320 像素的图直接丢弃模糊图、重度滤镜图全部排除。目标可辨识性猫在画面里至少 30% 面积可见或者虽然小但轮廓清晰无重遮挡。场景多样性不只是室内布景要包含白天/夜晚、暖光/冷光、单猫/多猫、干净背景/复杂背景。姿态多样性每类情绪至少保证 400 张以上避免类别严重不均衡。如果你自己收集图片我建议多拍视频再抽帧。视频抽帧的好处是能拿到大量连续姿态变迁比如猫从放松到警觉、到炸毛的整个过程这类连续状态序列对训练模型理解行为演变非常重要单纯靠静态图很难覆盖。2.3 标注工具与质量控制标注我用的 LabelImg 和 Roboflow 混合流程。LabelImg 免费、开箱即用适合小批量精标Roboflow 的好处是标注完直接做数据增强和格式导出省去中间转换的功夫。标注规范里有几条实操经验值得单列出来边界框贴合主体。猫的情绪信息不只集中在头部耳朵、瞳孔、尾巴、身体姿态都很关键所以我建议框选范围是“身体主体头部”不框伸出画面外的尾巴。你如果只框头身体姿态信息就丢了框全猫小目标数量又增加影响检测。遮挡情况标注策略。超过 50% 身体被遮挡的猫依然要框因为耳朵和头在也能判断情绪低于 30% 可见部分则放弃标注包括那些只露出半条尾巴的那种图标注了对训练只有反效果。多人交叉验证。每批图片至少由两个标注员独立标注随机抽 20% 做重合度比对目标框 IoU 低于 0.7 或类别不一致的都回到争议池人工复核。我踩过最大的坑是第一批 800 张图全部我自己标结果模型训练出来对“警觉”的识别总是偏向“焦虑”。后来分析发现我标注时把“耳朵稍微后压但不紧张”的猫全归到了警觉但按特征清单来看耳朵后压已经指向焦虑。人眼判读和特征清单脱节标签就出现了系统偏移。所以后面严格要求标注员按特征清单勾选而不是凭整体印象打标签。3. YOLOv8 训练实操从原始图片到可用模型3.1 数据集目录组织与格式转换标注完成后的原始数据可能是 PASCAL VOC 格式XML 文件或者 COCO 格式JSON 文件YOLO 不直接认这两种格式需要转换成 YOLO 格式的 txt 标注文件。YOLO 格式的一个 txt 文件对应一张图片每行内容为class_id x_center y_center width height注意这里 x_center、y_center、width、height 全部是归一化坐标除以图片宽高后的 0~1 浮点数。目录结构建议这样组织cat_emotion_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # 训练集标签 │ ├── val/ # 验证集标签 │ └── test/ # 测试集标签遇到 XML 转 txt可以直接用 Ultralytics 自带的转换脚本或者写个 Python 小工具批量处理这里给一个最简示例import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_list): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) out_file os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) lines [] for obj in root.findall(object): class_name obj.find(name).text if class_name not in class_list: continue class_id class_list.index(class_name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) dw 1.0 / img_w dh 1.0 / img_h cx (x1 x2) / 2.0 * dw cy (y1 y2) / 2.0 * dh w (x2 - x1) * dw h (y2 - y1) * dh lines.append(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_file, w) as f: f.write(\n.join(lines))数据集划分方面我采用 80/10/10 的比例训练集 2560 张验证集 320 张测试集 320 张。这里有两个小建议一是划分时按猫个体分组同一只猫的连续抽帧尽量放到同一个集合里不然模型在验证集上会“见过”训练集里的同一只猫评估指标虚高二是确保每个类别在三个集合中的比例大致相同用 Stratified Split 来做。3.2 训练关键参数解析数据准备好了接下来就是训练配置。下面是我调完认为最可复用的 YOLOv8 训练参数YOLOv8n 为例yolo train \ modelyolov8n.pt \ datacat_emotion.yaml \ epochs300 \ imgsz640 \ batch16 \ patience50 \ optimizerAdamW \ lr00.001 \ lrf0.01 \ weight_decay0.0005 \ warmup_epochs5 \ mosaic1.0 \ fliplr0.5 \ scale0.5 \ translate0.2这里每个参数我解释一下imgsz640默认值对猫这个目标来说足够。如果你的验证集里大量猫是小目标比如摄像头远处可以试 imgsz960但训练时间会增加约 50%建议先用 640 跑 baseline看结果再调。epochs300 配 patience50早停策略50 轮内验证集指标没提升就自动停止。我用这个配置基本在 150 轮左右收敛到最佳300 轮上限是为了留出足够探索空间。mosaic1.0YOLO v8 自带的 Mosaic 数据增强把 4 张图拼成一张训练能显著提升模型对遮挡、小目标的鲁棒性。但有个注意点Mosaic 对尺度变化大、姿态变化大的猫图片效果特别好如果你的数据集里猫都是大头照可以把 mosaic 调到 0.5避免增强过度导致模型在真实场景上泛化反而变差。fliplr0.5水平翻转增强。这个对猫基本没有副作用因为情绪特征不依赖方向。但是注意不要开 fliplr 后训练 Ferret 这类有文字的方向性目标通用模型开没问题。关于预训练权重三种选择给到大家用 COCO 预训练权重yolov8n.pt做迁移学习收敛速度快通常比我后面要说的从头训练好 8~10 个点 mAP用自己之前宠物数据集训练过的权重也完全可行因为底层的纹理特征可以复用。除非找不到任何预训练模型否则我不建议从头训练。YOLO 在 COCO 上学到的特征提取能力是很通用的底子。3.3 训练中的观察点与结果评估训练过程中有几个指标值得重点盯box_loss 和 cls_loss 的下降曲线。正常情况是 smooth 下降如果出现剧烈震荡或发散大概率是学习率过大或标签里有大量错误框。我遇到过把类别标错之后 cls_loss 在后期不断反弹这时候回去查数据比调参更有用。mAP50 和 mAP50-95 的差距。如果 mAP50 不错但 mAP50-95 明显偏低说明模型能框准位置但类别判断还不够稳。这通常意味着情绪类别之间区分度不够回到 2.1 节的特征边界去审视数据集。混淆矩阵。训练完后顺手看 ultralytics 自动生成的 confusion_matrix.png这是发现类别混淆最直接的方式。我在这上面发现最典型的问题就是 Anxious 和 Fearful 互相混淆占比超过 18%这个情况出现在类别定义中两边确实存在很长的中间态灰度后来我把 Anxious 的特征限定为“耳朵后压但瞳孔未明显放大”Fearful 则要求“耳朵贴头且身体明显下压”重新标注后提高了 4 个点。提示YOLOv8 的验证集指标 mAP50 是用 IoU 阈值 0.5 算的平均精度。目标检测里这个指标对框位置要求还好但如果你的场景需要精确监控比如自动判断猫是否靠近危险区域还得重点看精确率和召回率的平衡别只看 mAP。4. 踩坑记录情绪检测里最容易翻车的几个环节4.1 标签混淆耳朵方向与情绪误判前面提过类别之间边界模糊的问题这里再展开一个具体案例。我训练完第一版模型拿真实监控视频测试发现一个典型的错误模式猫对着窗外看鸟姿态是伏低、瞳孔略放大、尾巴轻摆模型判断成了 Aggressive攻击。仔细分析发现问题出在“伏低尾巴摆动”这个特征组合在 Playful/Hunting 和 Aggressive 语境下都出现过。训练数据里 Aggressive 类的样张很多是两只猫对峙时怼脸拍的猫伏低身体但嘴巴微张、毛稍微炸起而 Playful/Hunting 类如果没有逗猫棒或猎物入镜只拍猫单独的动作人眼也很难区分。解决方式是在标注阶段就加入上下文标记如果画面中有明显互动对象另一只猫、逗猫棒、玩具单独在标签体系里增加context_type辅助字段虽然 YOLO 训练时不读取但标注人员在标注时有明确指引减少因猜测上下文导致的误标。同时在图像增强阶段把 Playful/Hunting 的场景尽量增加互动项出现。4.2 小目标与遮挡YOLO 在猫脸上的表现家用摄像头拍到猫咪往往不是摄影棚效果。猫会躲在沙发后露出半个脑袋会在门口留一条尾巴会在跑跳中出现动态模糊。我统计了自己的验证集目标尺寸在 32x32 像素以下的占总目标数的 22% 左右。YOLOv8n 在这么小的目标上检测效果不算理想——特别是当情绪类别判断还需要看耳朵角度这样更小的局部细节时。应对策略有几个提高输入分辨率imgsz 从 640 提到 960小目标 mAP50 提升明显代价是训练和推理速度变慢约 40%。如果部署在 Jetson 这类边缘设备上要慎重权衡。TTATest Time Augmentation推理时开启多尺度测试能提高小目标召回但推理时间翻倍适合离线分析场景不适合实时视频流。切图检测对大图先做目标检测定位猫再对猫的区域做第二次精细分类。这种两段式方案在监控场景实测效果好于单模型硬扛小目标。数据增强中的 copy-paste把小尺寸猫目标复制粘贴到大图上增加小目标样本数量。YOLOv8 内置的 copy-paste 增强可以开 0.3实测对提升小目标精度有效。还有一个很多新手容易漏的细节边界框的最小尺寸限制。YOLO 训练时默认忽略掉小于一定尺寸的目标具体是 2 像素以下。在情绪检测里耳朵瞳孔这些特征在 32 像素框里已经很勉强如果再被缩小或切掉等于白标。我建议训练时检查一下 labels 中的框尺寸分布把那些小于 10 像素的框要么剔除要么通过补样本把它们对应的图裁出来放大再训练。4.3 训练中的损失异常与 BN 问题训练第二条大业当时碰到过,一批新图加入训练集后loss 突然出现周期性峰值。排查半天发现是这批图里有几十张被错误标注了 0 尺寸框——即 XML 里 xmin 和 xmax 相等转 YOLO 格式后 width 为 0训练时坐标回归目标无效直接导致 loss 波动。建议在训练前写个脚本做标签清洗检查import os def check_labels(label_dir): issues [] for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f)) as fh: for line in fh: parts line.strip().split() if len(parts) ! 5: issues.append((f, 格式错误)) continue _, _, _, w, h parts if float(w) 0 or float(h) 0 or float(w) 1 or float(h) 1: issues.append((f, 框尺寸异常)) return issues另外提一下 BN 崩溃的应对。我实际训练中用过 batch4显存不够硬跑结果发现模型在验证集上输出特别不稳定精度忽高忽低。YOLOv8 默认用的 BN 层在 batch 很小时统计量估计不准这种情况在近期 YOLO 实现中通过更新机制有所缓解但不代表你可以随便用小 batch能开梯度累积batch16, accumulate4就不要只调 batch4。4.4 部署阶段视频流检测与置信度调优模型训练完部署到摄像头场景又是另一套问题。最直接的问题是单帧检测是否稳定。单帧检测的置信度阈值我建议这样设置如果目标是做事件告警比如炸毛报警阈值可以设低一点到 0.3拿到更多候选框再结合时序做判断如果目标是做统计分析和标签入库比如给日常监控视频打情绪标签阈值设到 0.5~0.6宁可漏检也不要错检污染标签数据。为了过滤单帧抖动我加了一个简单的时序平滑逻辑连续 N 帧中同一目标IoU 大于 0.5被判定为同一情绪超过 60% 才输出最终结果。这个操作把误判从高频抖动中大幅削减。最开始直接输出单帧结果猫摇个尾巴可能从 Relaxed 变成 Alert 又变回来加了时序平滑后就稳定多了。推理框架我用的 TensorRT。YOLOv8n 导出 engine 做 FP16 量化后在 Jetson Orin 上能跑 70~90 FPS完全满足实时监控需求。导出命令简单记录一下yolo export modelbest.pt formatengine halfTrue导出后建议用trtexec跑一下测速和显存占用别到部署现场才发现资源不够。5. 数据集的扩展空间与后续迭代建议5.1 跨品种与多视角泛化3200 张数据集的一个明显短板是品种覆盖。如果训练集中橘猫和狸花猫占了大头将来遇到无毛猫、波斯猫这种脸型差异很大的品种模型表现会明显下降——因为情绪特征依赖的耳朵位置、瞳孔、毛发的视觉表现差异巨大。一个可行的补充路径在现有模型上做主动学习。把模型部署到测试环境跑一批新视频收集低置信度或误判的样本人工复核后回流训练集。我实测 200 张针对新品种的补充样本就能显著改善跨品种泛化比无脑再标 2000 张全品种图片效率高得多。多视角问题同样不可忽视。目前数据集大多为平视和俯视拍摄。而家用摄像头很多是斜上方视角。同一行为在斜俯视角度看耳朵开合和瞳孔大小仍可辨认但身体姿态的解读变得不同。建议收集新数据时重点标注摄像头视角最少包含三个层级平视、俯视、斜俯视。5.2 从单帧检测到行为序列分析情绪不是瞬时状态是动态过程。猫紧张之前可能先有短暂警觉攻击之前可能出现反复舔嘴唇的安抚行为。单帧 YOLO 检测只能看到“切片”看不到“过程”。我的后续计划是要在这个数据集基础上叠加行为序列模型。具体做法先用 YOLO 以 10 FPS 的帧率从视频中抽帧检测将检测框按 IoU 追踪关联成行为轨迹段再按时间窗口喂给一个轻量级时序模型比如 GRU 或者 1D CNN输出这一段时间内的情绪演变趋势。这样一个系统既能定位到猫在哪、当前什么状态又能理解状态的连续变化。要注意的是做时序分析前最好保证 YOLO 在单帧上的精度足够扎实否则时序输入会累积错误。我的标准是单帧 mAP50 至少打到 0.8否则先回到数据端迭代。5.3 开放标签体系与多模态扩展这套数据集目前是纯视觉标注没有声音信息。猫的情绪表达里声音占了很大比重。哈气声、低吼声、喵叫声频率和模式完全不同。如果你有条件可以同步录制音频做视觉音频的多模态情绪检测。数据层面比较简单视觉标注保留音频按时间轴对齐切段做环境音分类。另外我建议在标注体系里预留行为描述字段例如“舔嘴唇”“尾巴紧贴”“弓背”“炸毛”等细粒度动作标签。这些细粒度动作即使在情绪大类标注有争议时也能为模型提供辅助监督信号。多任务学习时一个模型可以同时输出动作标签和情绪标签动作识别反过来增强情绪判断的可解释性。整体来说3200 张的猫情绪检测数据集对于想做宠物行为分析的项目来说是一个可以落地起步的体量。我用它训练出的 YOLOv8n 模型已经能在家庭摄像头场景下稳定检测大多数情绪状态。当然它的局限性也很明显——类别边界需要持续打磨、小目标和极端视角仍需补充数据、跨品种泛化依赖进一步迭代。在实际操作中我的体会是这类宠物行为数据集真正的门槛不在标注工作量本身而在于情绪类别定义的可操作性和上下文信息的完整性。如果你能把“什么叫焦虑”“什么叫警觉”用可观察的行为特征定义清楚后面的训练和调优都是水到渠成的事。最后再分享一个小技巧每标完一批数据先用 YOLOv8 训练一个几十轮的快速版本拿验证集里最容易出错的图反过来修订标注规范迭代两三轮后你会发现数据集质量指数级提升——这比一次性堆完所有标注再训练有效得多。