ARTICLE DETAIL

资讯详情

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

3200张YOLO宠物行为数据集:猫情绪检测从标注到训练全流程

3200张YOLO宠物行为数据集:猫情绪检测从标注到训练全流程 养过猫的人都知道猫的情绪不像狗那么直白——狗开心会摇尾巴紧张会夹尾巴这些特征相对固定猫呢耳朵动一下、瞳孔缩一下、尾巴尖抖两下每一种都是信号但组合起来千变万化。想用AI做宠物行为分析第一步不是选模型而是先搞定一套能反映真实场景的标注数据。这篇就说说我做的这份3200张YOLO宠物行为数据集从采集、清洗、标注到训练YOLOv8的完整过程以及中间踩过的那些坑。1. 猫情绪检测这件事为什么卡在数据集上1.1 行为识别和情绪识别是两码事很多入门项目会把“行为识别”和“情绪识别”混为一谈。行为识别解决的是“猫在干什么”比如跑、跳、抓挠、舔毛但情绪识别要回答的是“猫现在处于什么心理状态”比如放松、警觉、紧张、愤怒。行为是外显的、可观测的情绪则要靠行为姿态去推断。我自己一开始也走了弯路直接去搜“cats emotion dataset”结果发现公开数据集非常少而且大部分是学术场景下的实验室录影猫被固定在一个小房间内光线单一镜头角度固定。这种数据训练出来的模型放到家里客厅用基本没法看——猫的角度、遮挡、光照全变了模型立刻退化。另一个常见误区是拿人脸的FERFacial Expression Recognition逻辑硬套到猫身上。人的情绪表情有相对稳定的面部肌肉模式但猫的面部肌肉结构完全不同表达情绪更多依靠耳朵角度、胡须位置、瞳孔宽度和整体身体姿态。单看一张脸很多时候判断不了必须把头部和身体一起放进检测框里。所以我最终确定的思路是用YOLO目标检测框架把整只猫作为检测目标类别直接定义为情绪状态。一张图里可能有三只猫就标三个框框里分别写猫的类别标签。这不是分类任务是检测任务——模型不仅要判断情绪还要输出猫在画面中的位置。1.2 通用宠物数据集到底缺在哪市面上能用的宠物数据集比较有名的是Oxford-IIIT Pet Dataset里面有37类猫狗品种但标签是品种和物种和情绪没有半点关系。COCO数据集里有猫这个类别但只有“cat”一个标签模型能识别出猫却分辨不了它处于什么状态。还有一些做“猫行为识别”的论文数据集比如用传感器记录猫的日常活动配合加速度计和摄像头。这类数据集有一个致命问题——传感器数据很难和视频帧精确对齐而且传感器的佩戴会影响猫的自然表现。猫这种生物身上挂个东西之后行为会明显改变采集到的所谓“自然行为”其实已经变形了。情绪数据的采集困难还在于状态的不均匀分布。猫一天中大多数时间是放松或睡觉紧张、愤怒、恐惧这类负向情绪状态占比很低。如果按自然比例采样3200张图里可能有一半以上都是“放松”模型训练出来对负向情绪的召回率会非常难看。这种情况下自制数据集几乎是唯一出路。3200张图不算多但针对单一物种、单一目标类别、六类情绪状态做精做细落地到家庭环境中是完全够用的。2. 3200张图的采集与清洗宁可少不能脏2.1 采集渠道和场景分布我这批数据主要来自三个渠道每个渠道的比例刻意做了平衡避免模型对某一类场景过拟合。第一类是家里安装固定摄像头录制的视频。这是主要来源——固定机位的好处是视角稳定白天和夜晚的光线变化都在同一个位置猫经过时产生的姿态变化非常自然。我从大约40小时监控录像中按照关键帧抽取抽帧策略不是均匀抽帧而是先过一遍预检测帧里有猫才抽出来送人工筛选。这一步省了大量时间否则40小时视频按1秒1帧来抽要抽14万帧人根本看不过来。第二类是手机手持拍摄的日常片段。这部分补充了固定机位缺失的角度比如俯拍、猫躺在人腿上的仰拍、贴近地板的平拍。手持拍摄的抖动问题比较严重清洗时我简单粗暴地用了一个标准目标区域的模糊程度超过肉眼可辨识的限度就删。第三类是网络公开图库中明确标注可商用的素材。使用前要确认授权范围挨个检查来源页面上的license声明。图库素材能补充一些家里拍不到的品种和场景比如布偶猫、缅因猫这类长毛品种我家没有。品种多样性对情绪检测是有意义的因为长毛猫的耳朵形态和短毛猫有差异模型如果只在短毛猫上训练遇到长毛猫的“飞机耳”就容易误判。最后留下来的分布大概是监控视频抽帧约1400张、手机拍摄约1000张、图库素材约800张合计3200张。2.2 去重、模糊帧和负样本处理清洗阶段我会按优先级筛掉四类素材一是相似度极高的连续帧。视频抽帧很容易产生几乎相同的画面——猫在同一个位置睡了十几秒抽出来的每一帧姿态都差不多。这种帧如果在数据集中大量存在会让训练集和验证集之间产生信息泄漏模型会“记住”而不是“学会”。我用感知哈希算法算图像之间的相似度阈值设置得比较保守相似度超过0.92的帧只保留一张。二是运动模糊。猫的动作速度比人快尤其是扑咬、跳跃的瞬间快门跟不上就大面积糊掉。人眼能识别但YOLO的标注框在这种图上很难框准边界。YOLO训练时对边界框的精准度要求其实很高框差5个像素损失函数里就会体现出来模糊图上标的框位置是随机的等于给模型喂了噪声。三是目标占比过小的图。猫在画面边缘、只占整张图的不到5%检测框太小模型基本学不到有效特征。这种图保留下来会拉低学习效率。我定了条线猫的边界框短边小于32像素的直接删这是YOLO普遍采用的锚框尺寸下限小于这个值模型很难提取到稳定纹理特征。四是真正无法判断情绪的图。比如猫完全背对镜头、身体被毯子整个盖住、只能看到一团轮廓。这种图硬标的话标注员只能靠猜标签可靠性存疑训练出来也是带噪声的映射。清洗之后的数据量会有明显缩水——我从抽出来的6000多张里筛到4200张左右标注过程中又因为边界框质量问题淘汰了一批最后稳定在3200张。这个过程很肉痛但是值得。数据质量不过关后面所有调参工作都是白费。3. 情绪类别体系设计先定义清楚再看YOLO3.1 六类情绪状态的判定标准很多项目上来就定十几个类别什么“好奇”“无聊”“期待”“不满”标注员和模型全都一头雾水。情绪类别不是越多越好类别之间要有可观察的形态差异模型才学得动。我最终用了六个类别每个类别都有明确的视觉标志放松relaxed身体舒展往往侧躺或俯卧眼睛半闭耳朵自然竖立或微微后转尾巴缓慢摇摆或静止。警觉alert耳朵直立向前瞳孔放大身体紧绷但不收缩视线集中在某一点尾巴大幅摆动。愉快happy尾巴高高竖起且尖端微颤身体蹭人前爪交替踩踏面部肌肉松弛有时伴随眼睛眯成缝。紧张anxious身体蜷缩压低耳朵向侧面下压瞳孔扩大尾巴紧贴身体或夹在两腿间频繁舔嘴唇。愤怒aggressive炸毛弓背耳朵完全压成飞机耳瞳孔缩小张嘴露牙或发出哈气尾巴快速拍打地面。恐惧fearful身体试图躲藏头部压低贴近地面耳朵完全向后贴平瞳孔异常放大身体整体收缩成球状。这六个类别的区分度并不是均匀的。放松和愉快之间经常混淆——猫在踩踏时身体姿态是放松的但行为上已经表现出愉悦。我的处理原则是只要一组典型行为占据主导就标成对应类别如果两种状态的标志行为同时出现且强度相当比如放松侧躺但尾巴尖在颤动就取那个更具体的行为状态。这里我建议宁可少标也不要标一半一半的“混合态”——模型没有能力学出模糊边界只会学会输出错误的置信度。3.2 同一画面中的多猫与多状态处理多猫家庭的场景同一个画面里会出现几只猫状态可能不一致一只醒着、一只睡着一只放松、一只紧张。YOLO允许一张图有多个边界框每个框可以有独立的类别所以不存在结构上的障碍。实际操作中比较棘手的是两只猫挤在一起的情况——身体有重叠边界框交叉猫A的身体遮住了猫B的头。我的标注规则是边界框只覆盖可见部分被遮挡超过50%的目标不标。这样做会让边界框的类别特征弱一些但总比标注错误的边界框好得多。另一个容易被忽视的问题出在边界框的标注范围上。YOLO训练时框内的特征会被池化提取如果框得过大把背景算进太多模型学到的特征里会混入环境噪声如果框得过小只框住头部那愤怒类别的炸毛特征就丢失了。我的经验是框住全身但需要容纳全身粗细变化最大的区域。比如一只炸毛的猫宽度比平时大三分之一标注时一定要把炸开的毛圈进去否则模型会误认为这是“胖”而不是“愤怒”。更细化的一个问题同类别不同个体的体态差异极大。小猫的耳朵比例比成年猫大长毛猫和短毛猫的轮廓线条也不同。我在标注规范里加了备注以行为特征为主要判据不要被品种外观带偏。一只耳朵贴平的长毛猫和一只耳朵贴平的短毛猫都要标成同一种情绪状态。数据集中六个类别的分布如下表类别图像数量边界框数量放松9801210警觉620690愉快480505紧张430470愤怒370385恐惧320330可以看到“放松”类别占了将近三分之一其他类别相对平衡。这种分布不是完全均衡的但我训练时没有直接做数据均衡而是用了损失函数加权的方式。原因后面会提到。4. YOLO标注实操工具选择与质检流程4.1 目录结构和坐标归一化数据集的标注格式严格按照YOLO检测的标准datasets/cat_emotion/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── classes.txt每个图像文件对应一个同名的纯文本标注文件放在labels目录下对应的子目录里。比如images/train/000123.jpg对应labels/train/000123.txt。如果某张图没有任何标签也就是没有含猫目标对应的txt文件为空但文件本身要保留否则数据加载器会报错。txt文件的每行格式为五个字段class_id x_center y_center width heightclass_id整数从0开始对应classes.txt中的行号x_center边界框中心点的x坐标除以图像宽度取0到1之间的浮点数y_center同x_center根据高度归一化width边界框的宽度除以图像宽度height边界框的高度除以图像高度举个例子一张1280像素宽、720像素高的图上猫的边界框左上角在(300, 200)右下角在(800, 600)对应的归一化计算如下x_center (300 800) / 2 / 1280 0.4297 y_center (200 600) / 2 / 720 0.5556 width (800 - 300) / 1280 0.3906 height (600 - 200) / 720 0.5556这行就是0 0.4297 0.5556 0.3906 0.5556类别0是放松。这个坐标系的坑在于如果标注时不小心把x_center和y_center写反或者忘了归一化模型训练时的损失值会爆炸式增长且很难排查。我推荐标注工具直接导出YOLO格式不要手工写坐标。我用的标注工具是LabelImg和CVAT组合。LabelImg适合单人小批量标注界面简单直接支持YOLO格式导出快捷键D切换下一张图W创建框效率还算可以。但批量处理几百张图以上LabelImg没有云端协作和冲突检测团队协作时容易出乱子。后来转到CVAT它支持多人同时标注、任务分配、标注结果审查而且有自动标注辅助功能可以先拿一个初版模型对未标注的图做预标注人工只需要调整框的位置效率至少提升一倍。4.2 双人复核与标注纠错标注环节单独一个人做越到后面越手滑。我两个批次共整理了三个月的数据中间出现过的错误大概有三类第一类是类别误判。最典型的是紧张和愤怒的混淆一只猫耳朵后压、身体压低但没有炸毛标注员标成紧张实际上是已经发出哈声的愤怒前兆。这类错误不是纯粹手滑而是类别边界本身的模糊性带来的。解决办法不是重新培训标注员而是在标注规范里加了一条存在哈气、露牙、炸毛这三种行为中的任意一种一律标愤怒。第二类是边界框偏移。猫在快速转头时标注框往往会框住头部加一部分空气漏掉脖子以下的身体。这会导致训练时“愤怒炸毛”特征被截断模型学到一半特征。CVAT的“按目标追踪”功能可以帮一点忙但最终质检还是要人眼一格一格看。第三类是文件格式错误。我们有一批图是从视频抽帧工具导出的图像尺寸是1920x1080但标注工具读取时被压缩成了960x540。在压缩图上标注的框坐标归一化后没问题——因为归一化是相对于图像尺寸的——但导出后如果图像路径对不上训练脚本按原图尺寸加载框的位置不会错但比例信息是准的。这个坑发生在标注工具缓存了旧尺寸的情况下。质检脚本要多加一道校验读取每张图像的实际宽高重新核算标注框是否在合法范围内。质检流程我按三步走脚本先生成一个叠加边界框的预览图每张生成后随机抽样10%人工检查重点关注框是否贴合目标、是否漏标、有没有类别错标。跑一遍yolo格式的合法性校验检查每个txt文件的行数、字段数量是否都为5、坐标是否在[0,1]区间内。用初版权重对全量数据做一次推理把模型输出和人工标注不一致的样本拉出来给人看——不一定是标注错可能是模型错但差异大的样本一定是需要关注的。这个流程走完3200张图大概又修正了150多个标注框最终才进入正式训练。标注这步偷的懒后面训练时全都会加倍还回来。5. 用这份数据集训练YOLOv8的实战记录5.1 训练环境与参数设定硬件上我用的是一张RTX 3090显存24GB这个规模跑YOLOv8s完全没有压力。CPU是AMD Ryzen 9内存32GB数据集整体加载进内存绰绰有余。如果用V100或者更小的卡把batch size调小也可以跑只是慢一些。训练脚本基于ultralytics的YOLOv8s模型。选择s版本而不是m或l是因为情绪检测的应用场景基本是实时摄像头FPS要稳定在30帧以上m和l在CPU或低端GPU上很难做到。精度上s版本比m低一些但在单目标检测任务上差距没有想象中那么大毕竟我们只是检测“猫”不是检测80个类别。以下是训练配置的关键参数最大输入尺寸640。虽然YOLOv8支持多尺度训练数据集里的原始图尺寸基本都大于640比如1080p或720p训练时会被缩放。640是精炼速度和质量后的常用折中。如果你的显卡足够强可以试试640以上的输入比如832边界框会稍微更精细但显存占用会上升不少。 batch size设为16。3090跑640分辨率16这个值不会爆显存而且BN层的统计量足够稳。 epoch数设为80配合早停。我用patience20——如果连续20个epoch验证集mAP没有上涨就提前结束。实际训练到第61个epoch时早停触发了验证集mAP50稳定在0.78左右。 数据划分按6:2:2的比例也就是train 1920张、val 640张、test 640张。提示验证集和测试集必须从不同来源的视频序列中抽取不能简单随机乱分。如果测试集里混入了和训练集同一时间段拍摄的监控帧模型“背下来”的痕迹会被当成有效学习成果验证结果会虚高10个百分点以上。我做划分时以“拍摄片段”为最小单位同一个片段里的帧全部放进同一份数据集避免帧级数据泄漏。5.2 训练结果与逐类指标分析训练完成后最关心的自然是每类情绪能不能正确识别。我在测试集上的指标如下数据集随机划分不同种子略有波动但方向一致类别精确率Precision召回率RecallAP50AP50-95放松0.870.910.910.62警觉0.740.800.780.49愉快0.680.710.720.42紧张0.700.660.690.43愤怒0.820.770.820.55恐惧0.750.690.730.46整体mAP50约0.78mAP50-95约0.51。这个结果作为基准线是能用的但细看逐类指标问题集中在愉快、紧张、恐惧三类的召回率上。一个共性原因是这三类状态的典型特征持续时间短猫可能在一两秒内从紧张转成防御甚至攻击标注时如果抽帧时机没有捕捉到峰值状态标签就会偏弱。比较典型的一个失败案例画面的猫正对着镜头啃猫抓板尾巴轻轻摆动我标成了“愉快”。模型实际预测为“放松”置信度0.85。这其实是合理的——啃咬的动作模式在YOLO看来更接近静态行为除非把“尾巴竖直踩踏”这种动态特征清晰拍进画面否则模型学不出反差。6. 训练中的典型翻车现场与调优经验6.1 类别不平衡的损失函数加权看到前面那张数据分布表可能有读者会说放松类占比接近三分之一要不要随机下采样让六个类别的框数量接近我试过效果不好。原因在于放松状态本身的“形态特征”波动巨大——有侧躺、俯卧、蜷缩、舒展身体姿态差异不亚于跨类别差异。如果把放松类的样本数量硬压下来模型对放松类各种姿态的判别边界就会退化实际应用中出现误报的频率反而增加。我最终在损失函数里给“紧张”“恐惧”“愤怒”三个类别加了系数做法是在ultralytics的配置里传入每个类别的权重向量让低样本量的类别梯度贡献更大。具体数值“放松”1.0“警觉”1.0“愉快”1.1“紧张”1.3“愤怒”1.2“恐惧”1.4。这是相对黑盒的调优方式效果上恐惧类AP50提升约6个百分点但并没有解决根本问题——最终还是要补数据的。如果你的项目预算允许这个方向值得投入更多。6.2 相似行为类别的人工规则兜底YOLO的可解释性差它内部不会告诉你“这只猫为什么被判定为紧张”——它只会输出一个置信度。如果置信度比较低比如0.5以下人根本不知道模型的依据是什么。我在部署阶段加了一个简单的后处理规则检测结果在紧张、恐惧、愤怒三类置信度都不超过0.4时默认归入“警觉”类别。理由很简单非常刻板的“警觉”特征——耳朵竖起、瞳孔较大、身体紧绷——是这三种负向情绪的共同前兆。模型对负向情绪的区分不完备但至少对“非放松”的识别还是稳定的。用保守策略宁可错报“紧张”也不要漏报真正的攻击前兆。6.3 从单帧检测到时序预测的延伸上面所有结论都基于单帧静态推理。实际部署时我发现单帧预测的稳定性不够——同一只猫在同一段监控里连续出现10秒每隔0.5秒抽一帧帧与帧之间的预测类别会在“放松”和“警觉”之间跳来跳去。这不能完全怪模型猫本身的情绪状态就是实时波动的加上抽帧间隔期间姿态连续变化单帧的瞬时状态只能表示那一刻的行为不能代表一段时间的整体情绪。解决的思路有两种。第一种是滑动窗口多数投票取最近30帧的检测结果统计每一类的出现频次取频次最高且置信度超过阈值的类别作为当前情绪状态。这种做法实现简单10行代码就够但会引入几个帧的延迟对实时交互场景有影响。第二种是引入视频行为识别模型比如用SlowFast或TimeSformer对连续帧序列建模利用时序特征判别情绪走向。这种方法精度上限更高但需要额外的GPU资源而且标注成本直接翻倍——时序模型训练需要的是标注到帧的连续事件时间段我的数据集目前还不足以支撑这个方向。如果后面继续扩展我更倾向于先给现有数据集补充音频特征。猫的哈气声、呼噜声、嚎叫声都是非常强烈的情绪信号和视觉特征互补性极强。到时候数据集就需要做多模态对齐了不只标注框还要标注声音事件的时间戳。这是一个完整的系统工程但和纯视觉方案相比它能解决“画面静止但情绪波动”的核心盲区。我个人的感受是这份3200张的数据集在单帧静态检测上已经具备实用价值但真正的落地场景几乎没有纯静态的。摄像头方案加一个时序投票模块准确率立刻能起来一两个点。如果你也在做类似的项目我建议从一开始就把时序信息考虑进标注规范里不然等模型库训练完后想回补成本是现在的三倍还不止。
返回列表