ARTICLE DETAIL

资讯详情

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

基于YOLO的猫情绪检测:数据集构建、模型训练与TensorRT部署实战

基于YOLO的猫情绪检测:数据集构建、模型训练与TensorRT部署实战 养猫的人大概都经历过这种时刻猫拱起背、尾巴炸毛的时候你还在傻乎乎地伸手去摸结果被挠了一下。猫的情绪不会像狗那样直白地写在脸上但它的尾巴、耳朵、瞳孔、身体姿态全是信号。这两年我一直在这个方向折腾从图像采集、行为标注到模型训练踩了不少坑也攒下了一套3200张的YOLO宠物行为数据集。这篇文章就把我从数据集设计到落地部署的完整经验拆开讲一遍包括标注类别怎么定、训练参数怎么调、BN崩溃和类别不平衡这些问题是怎么处理的。不管你是刚入门目标检测还是已经跑过YOLO想找一份具体的领域数据集这套流程都可以直接参考复现。1. 为什么做猫情绪检测一个比想象中复杂的视觉任务1.1 猫情绪检测的真正难点猫的情绪识别比看起来要难得多。人脸情绪识别只需要一拍正面脸部区域判断表情就能完成大部分工作但猫不是这样。猫的脸部肌肉并不发达你盯着它的脸看半天很难直接说它是开心还是生气。真正传递情绪信息的是尾巴摆动频率、耳朵转动角度、瞳孔缩放程度、胡须张力以及整个身体的姿态——弓背、压低身体、侧躺、四脚朝天每种姿态都对应不同的情绪状态和行为意图。这意味着猫情绪检测本质上是多部位联合分析任务模型不能只看局部必须同时感知猫的整体姿态和细节特征。比如一只猫耳朵后压、瞳孔收缩、身体压低并伴有尾巴缓慢摆动这通常代表紧张或准备攻击而耳朵自然直立、眼睛半闭、身体舒展侧躺大概率是放松状态。这些信号组合起来才构成一个完整判断单看耳朵或单看尾巴都很容易误判。更麻烦的是猫的动作变化极快。一个放松的状态可能在0.5秒内切换为警觉状态。摄像头采集的画面中猫的运动模糊、遮挡、光线变化都是常态。这让猫情绪检测对数据质量的要求非常高——如果标注图本身就没抓拍到关键姿态模型再怎么训练也学不好。1.2 为什么选YOLO而不是其他模型最初我也考虑过分类网络ResNet、EfficientNet和更重的检测框架Faster R-CNN后来还是选了YOLO。理由其实很实际。分类网络只能回答这只猫现在处于什么情绪但真实场景里一帧画面可能同时出现两只猫或者猫只占画面一角。分类网络需要先把猫裁出来这又多了一道工序而且裁剪框不准会直接影响分类结果。YOLO这类目标检测模型输出的是目标框类别天然支持同时检测多只猫并且能通过框的大小和位置判断猫在画面中的占比后续接行为分析也更方便。Faster R-CNN这类两阶段检测器精度理论上更高但推理速度摆在那里。猫情绪检测的落地场景一般是摄像头实时监控或嵌入式设备帧率要求至少25FPS以上才有实用价值。YOLO系列在精度和速度的平衡上做得最好尤其YOLOv8针对小目标和中目标做了不少优化用在宠物行为分析上比较顺手。1.3 3200张图够不够数据量这件事得说清楚先说结论对于单类别目标检测任务3200张图用好了是够的对于多类别情绪识别3200张算起步量必须配合数据增强和预训练权重才能压得住过拟合。我自己在做这套数据集时的定位是行为检测基准重点关注六个情绪类别愤怒、恐惧、放松、紧张、好奇、正常。平均下来每个类别大约500多张图这个数量在目标检测里不算多但也不至于完全学不动。关键是怎么用。如果全图是1920×1080的原始帧YOLO训练时会缩放到640×640目标框可能会缩小到几百像素甚至几十像素这时候标注质量和增强策略就比数量更影响最终性能。3200张图全部认真做完标注、选场景多样的素材效果远好过12000张粗糙批次标注的图。数据量这个数字看起来唬人真正决定模型上限的永远是标注一致性和场景覆盖度。2. 数据集设计标注类别、采集策略与目录规范2.1 情绪类别怎么定从行为学角度看标注标注类别的定义是整个数据集最关键也最容易偷懒的地方。很多初学者直接拍脑袋定几个情绪标签就开始标结果模型训练出来准确率惨不忍睹其实就是类别边界太模糊标注员自己都分不清。我参考了动物行为学中关于猫的压力与情绪评估方法把类别定义为行为单元的组合而不是单纯的主观情绪判断。每个类别必须能通过可观测的行为特征来确认。比如愤怒的判据是耳朵外展或后压、瞳孔收缩、尾巴快速摆动、可能伴有哈气或嘶吼恐惧是身体蜷缩压低、瞳孔放大、耳朵完全平贴后脑、尾巴夹在两腿之间放松则是眼睛半闭或缓慢眨眼、身体舒展侧躺、尾巴缓慢摆动或静止。如果你去看宠物行为领域成熟的数据集或论文会发现情绪标签一般都伴随行为描述。这样做的好处有二一是不同标注员对同一张图意见统一的概率大幅提高标注一致性能直接反映在模型收敛速度上二是后期如果要扩展类别或做行为识别复合任务原始的行为特征标签还能复用。我的建议是千万不要只标happysad这种笼统词。哪怕最终预测类别只有六个标注时也要为每个类别写清楚哪些行为特征组合属于这个类别把标注规范文档做好多人协作时才不会标出五花八门的结果。2.2 图片采集与筛选质量比数量先行的几个原则采集猫的图片素材很多人第一反应是去网上批量爬图。这个思路效率高但坑也大图片版权、分辨率参差不齐、同一来源的图片高度相似导致数据集分布不平衡。我自己最后采用的是三分法三分之一来自自己拍摄的宠物猫视频抽帧三分之一来自公开的宠物图像数据集三分之一来自网络图库并做去重和筛选。筛选图片有一套硬性标准比数量重要得多单张图里至少有完整可辨识的猫体或清晰可见的大半个身体只有猫头或只有尾巴不算合格样本。猫体占比不能过大也不能过小。占满全图的图对训练没有意义因为模型学到的全是局部毛色纹理占比太小的图在小目标检测时容易学不到位。必须覆盖多角度正面、侧面、背面、俯拍、仰拍每种姿态至少都要有稳定比例。光线多样化尤其是逆光和暗光场景。情绪检测在夜间红外摄像头场景下也有应用需求所以适当加入低照度样本有好处。同源图片去重。网络爬图最大的坑就是看似800张图实际来自同一个视频连续帧的就有200张这些图几乎相同对模型泛化能力的提升接近于零。建议用一段简单的脚本算感知哈希来查重把相似度超过0.9的图片自动归组每组只保留一两张。这样3200张图片实际的信息量会远比网上盲爬几千张要高。2.3 YOLO标注格式从LabelImg到txt的一步到位YOLO系列的标注格式是统一的图片同名txt文件每行代表一个目标框格式为类别序号 框中心x 框中心y 框宽 框高其中中心坐标和宽高都是相对图片宽高的归一化比例值。实操时我用的标注工具是LabelImg它支持YOLO格式直接导出界面也够直白。这里补充一个很多人第一次接触YOLO格式时会犯的错误坐标归一化是按像素坐标除以图片宽高不是除以目标框尺寸。比如一张1600×1200的图某个目标框左上角在(400, 300)右下角在(800, 750)那么中心点像素坐标是(600, 525)框宽高为(400, 450)归一化后的值为(600/16000.375, 525/12000.4375, 400/16000.25, 450/12000.375)写成一行就是类别序号 0.375 0.4375 0.25 0.375。标注完成后的目录结构通常长这样cat_emotion_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── data.yaml └── classes.txtdata.yaml是YOLO训练时的关键配置文件里面指定了训练验证路径、类别数量和类别名称path: ./cat_emotion_dataset train: images/train val: images/val test: images/test nc: 6 names: [angry, fear, relaxed, tense, curious, neutral]类别顺序必须和标注txt里的序号一一对应。我建议在标注阶段就把类别顺序固定下来明确写进classes.txt中途不要随意调整。YOLO可以自动根据目录生成格式配置但如果你在多个版本之间迁移类别顺序不一致会导致模型输出全部错位排查起来非常痛苦。3. YOLO模型训练从配置文件到loss曲线全流程3.1 环境准备与预训练模型选择训练环境我用的是单卡RTX 3090显存24GB。如果你手头显卡只有8GB左右也不用慌这套数据量不大YOLOv8s或YOLOv8n在batch size 8到16的情况下都能跑。预训练模型选择上我直接采用官方发布的COCO预训练权重而不是从随机初始化开始训练。原因很朴素3200张图的数据量不足以让模型从头学出通用的视觉特征。COCO预训练权重里的特征提取层已经学会了边缘、纹理、形状等基础模式我们只需要在新数据集上微调检测头和部分特征层即可。YOLOv8官方仓库下载yolov8s.pt或yolov8n.pt即可下面是常见选择对比模型速度(FPS640, V100)精度(mAP50)适合场景YOLOv8n较高较低嵌入式、实时预览YOLOv8s中等中等通用设备兼顾速度和精度YOLOv8m较低较高离线分析、精度优先我自己在这套数据集上首选是YOLOv8s理由是在低算力设备上也能跑到25帧以上精度又比nano模型高了接近3个点。如果你打算部署在Jetson这类边缘设备上可以先用s模型训练收敛再蒸馏或转换后量化到nano的规模。3.2 数据划分与增强策略数据划分我用的是分层随机抽样先按类别统计每张图的标注分布保证训练集、验证集、测试集中每个类别的样本比例大致相同。如果直接全局随机划分某些稀有类别可能训练集有100张而验证集只有10张评估指标波动会非常大。比例上我实际用了70%训练、20%验证、10%测试。3200张图分出来测试集大约320张已经足够给出相对稳定的mAP评估。数据增强是这套小样本数据集能训练出效果的核心手段。YOLOv8默认开启的马赛克增强、随机翻转、色彩抖动、缩放平移都很有效但有几个参数针对猫情绪检测需要特别留意。马赛克增强默认开启后每张训练图由4张图拼接而成相当于在变相扩大数据量。但同时它也会让训练图里的目标框变小、遮挡变多对小物体检测不一定有利。我在训练前中期都开着马赛克到训练后期比如总epoch的70%后建议关掉或调低马赛克概率让模型在接近真实分布的图上做最后的细调。随机翻转对猫情绪检测要谨慎使用。左右翻转不影响大多数情绪判断因为姿态结构是对称的但耳朵朝向和瞳孔位置会因为翻转产生语义偏移训练时影响不大如果部署时数据从固定角度拍摄且猫常在画面一侧活动翻转后再推理可能产生小幅度误检。我实际做法是只开上下翻转水平翻转保持关闭。3.3 训练参数设置与原理解读训练命令本身很简单关键在于参数为什么这么设。yolo detect train datacat_emotion_dataset/data.yaml \ modelyolov8s.pt \ epochs200 \ batch16 \ imgsz640 \ lr00.01 \ lrf0.01 \ momentum0.937 \ weight_decay0.0005 \ warmup_epochs3.0 \ close_mosaic60epochs设200对3200张图略多但配合早停策略是安全的。batch size 16在24GB显存下很稳如果你的卡只有12GB降到8也行学习率建议等比缩小到0.005左右。学习率初始值0.01是YOLOv8官方默认对微调其实偏大一点。我做了一轮实验lr0从0.001到0.02区间内0.005到0.01之间的mAP差异很小但超过0.02会出现明显的训练震荡。建议新手直接0.01起跑观察前50个epoch的loss曲线如果震荡剧烈就回调到0.005重启。warmup_epochs是前3个epoch用较小学习率做预热避免初始权重被大步长学崩。close_mosaic60表示从第60轮epoch开始关闭马赛克增强这个参数和epochs相关如果你只训练100轮close_mosaic就要调到30左右。loss部分YOLOv8的边界框回归用的是CIoU损失分类用的是BCE损失。CIoU损失同时考虑重叠面积、中心点距离和长宽比训练过程中如果你看到box_loss在后期仍持续下降但cls_loss平稳说明模型在定位上还能压榨一点性能可以适当增加epoch或调低学习率做精调。3.4 训练过程监控loss、mAP、混淆矩阵怎么配合看训练过程中不要只看总loss三个分量要分开观察。YOLOv8训练日志里有box_loss、cls_loss、dfl_loss它们下降趋势应该大体同步。如果box_loss下降很快但cls_loss停滞说明模型学会了找到猫但学不会区分情绪这时候应该去检查标注类别一致性而不是盲目加大训练轮数。每几个epoch保存一次的验证结果里除了mAP还有混淆矩阵。混淆矩阵能直观看出哪些情绪类别互相混淆。我跑出来的结果里tensed和fear混淆度最高原因也合理——猫紧张和恐惧的姿态特征本来就有大量重叠二者都是压低身体、瞳孔放大、耳朵后压。这时要么合并这两个类别要么在标注规范里补充更严格的区分判据。还有一个容易被忽略的点验证集mAP50和mAP50-95之间的差距。mAP50-95对目标框定位精度更敏感如果你的mAP50到了0.85但mAP50-95只有0.5说明检测框位置有明显偏移这时优先检查标注框是否过大或过小而不是情绪分类弄错了。4. 实操中踩过的坑BN崩溃、小目标漏检、类别不均衡4.1 BatchNorm崩溃现象、原因与解决方案训练过程中遇到最多的异常就是BatchNorm崩溃一般表现为训练loss突然变成NaN或者某个类别的loss剧烈震荡完全无法收敛。BatchNorm在训练时会统计当前batch的均值和方差如果某个batch里的特征分布极端——比如马赛克增强拼出大量黑色背景、某个类别目标太小导致梯度异常——统计值就会跑偏后续batch继续叠加偏差最后数值溢出。解决这个问题的第一道防线是降低batch size并同步调低学习率。BatchNorm对batch size很敏感batch越小统计噪声越大但尤其在数据分布不均衡时小batch容易抽样到同类图片统计漂移概率更高。我会先确认batch size不小于8再检查数据预处理。如果个别图片本身是纯黑或纯白背景训练时经过归一化后的特征可能直接击穿数值范围这类脏样本要先清理。更稳妥的办法是把模型训练的批归一化层临时切换为GroupNorm做一次测试训练确认问题是否由BN引起。不过这种改动涉及模型结构一般只用来做问题定位真正修复还是要回到数据清理和学习率调整上。4.2 数据不平衡少数类别学不动怎么办六个情绪类别里好奇和正常相对容易采集恐惧和愤怒比较难拍到。我统计下来恐惧类大约只有400张而正常类接近700张这个差距看似不大但在检测任务里依然会体现为少数类别精度明显偏低。处理数据不平衡我试过三种方法。第一种是类别权重YOLO没有直接配置类别权重的参数需要在训练循环里自己实现或改用第三方训练脚本性价比一般。第二种是过采样训练时让恐惧类图片被抽样到的概率乘以1.5到2倍这个在ultralytics的dataset实现里可以通过自定义sampler做到效果最直接。第三种是复制粘贴增强把恐惧类目标框区域切出来旋转缩放后粘贴到背景图上再生成新样本对提升检测框识别能力有帮助但要注意粘贴后目标比例不能太失真。我最终采用的是组合方案恐惧类过采样1.8倍愤怒类过采样1.5倍同时给这两类单独开了轻度复制粘贴增强。训练后恐惧类的mAP从0.62提升到0.74效果显著。4.3 目标太小漏检猫离得远就检测不到3200张图如果猫的占比普遍较小YOLO在640输入下很容易漏检。推理图里某只猫可能只占约5000像素的框经过640缩放后就更小。针对小目标我总结下来比较有效的几招增大输入分辨率。把imgsz从640提到896或1280小目标的AP提升通常很明显代价是推理帧率下降。如果部署环境允许优先把输入分辨率提上去。调整anchor。YOLOv8开始不再需要手工设置anchor但自动anchor的缩放比例只和训练集分布相关。如果训练集中小目标多自动anchor生成出来的尺度会偏向小目标一侧这实际上是合理的不需要额外干预。损失函数层面对小目标的回归损失会偏小可以考虑给box_loss加上针对小目标的加权项。实现起来需要修改loss函数一般到这一步都是被逼到角落了才做。最简单的排查方法是把验证集漏检图单独拿出来看看漏检的目标框像素大小分布。如果用肉眼确认检测框大部分都小于8000像素那直接上大分辨率比改loss更立竿见影。4.4 混淆矩阵里的总和不对指标解读的常见误区不少人在跑完训练后看混淆矩阵发现横轴或者纵轴的数字总和不是100%还以为模型出错了。混淆矩阵里的数字是样本计数不是百分比每个类别对应的行代表该类别所有真实样本被预测到各个类别的数量百分比化之后每行的和才是该类别在验证集中的总数。真正需要警惕的是矩阵里的背景列。YOLO目标检测里背景类别指的是模型预测出了框但该框与真实目标IoU不够格的误检框。如果某个情绪类别有大量被分到背景列的样本说明该类别目标框建议质量不够好模型学到的特征和真实目标的区分度太低或者标注框本身位置就不准。如果混淆矩阵对角线值在0.7以上整体模型就属于可用了后续优化的优先级是找出哪个类别对角线最低针对性采集该类别更多素材或调整标注规范。5. 部署到实际场景从PyTorch到ONNX再到TensorRT5.1 模型转换与量化训练完成后模型是PyTorch格式的.pt文件部署时需要做转换和量化。转换路径一般是pt转ONNX再转TensorRT。ONNX作为中间格式的优势是跨平台可以把模型部署到不同推理引擎上。yolo export modelbest.pt formatonnx dynamicTrue imgsz640导出时记得打开dynamicTrue或指定固定batch否则后续转TensorRT时batch size固定为1处理多路视频时效率不高。导出后可以用onnxruntime跑一遍测试图确认输出和原模型一致。TensorRT转换我在Jetson Orin和RTX 4090上分别试过核心关注点有两个精度模式和显存优化策略。TensorRT支持FP32、FP16、INT8三种精度。FP16精度损失微乎其微mAP下降不会超过1个百分点速度却接近翻倍是首选。INT8需要准备校准集做量化速度最快但mAP可能下降2到3个点对于情绪检测这种非安全攸关场景可以接受。如果你想要最均衡的效果建议FP16跑。TensorRT在FP16下对猫情绪检测模型的推理延迟在RTX 4090上大概只要2到4毫秒每帧即使在Jetson Orin Nano上也能跑到30毫秒以内完全满足实时监控需求。5.2 推理性能评估在具体硬件上能跑多少路关于T4 1080p 25帧每秒用TensorRT YOLO 640分辨率检测可以支持多少路这类问题我实际测过一次。T4显卡上YOLOv8s FP16经过TensorRT处理单路1080p缩放到640分辨率后推理延迟大约15到20毫秒。25帧每秒要求每帧时间控制在40毫秒以内单路显然没问题但要支持多少路得算总算力。最简化的估算方法是单路每秒需要的推理次数是25次单次20毫秒意味着单路占用0.5秒的GPU时间每秒钟。理论上10路连续推理正好把GPU打满。但实际还要考虑图像预处理、缩放、后处理NMS、CPU与GPU之间的数据拷贝这些都会额外占用资源实测下来T4跑6到8路YOLOv8s FP16就已经接近极限了。如果目标是想跑更多路建议从三方面入手缩小输入分辨率到512甚至416、把模型量化为INT8、减少后处理中NMS的候选框数量。这些都做完10路以上不是问题。5.3 一个完整的猫情绪检测demo流程部署后的检测效果我用一个简单脚本验证过。流程包括打开视频流或摄像头、逐帧做预处理、推理、后处理、把检测框和情绪标签画在帧上。import cv2 import torch from ultralytics import YOLO model YOLO(best_fp16.engine) # TensorRT部署版 cap cv2.VideoCapture(0) while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, imgsz640, conf0.45, iou0.5) annotated results[0].plot() cv2.imshow(Cat Emotion Detection, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()conf0.45是置信度阈值iou0.5是NMS的IoU阈值。置信度设得太低比如0.25在真实场景会出现大量误检尤其光线复杂时设太高又会漏掉姿态不标准的猫。我实际测试下来0.4到0.5之间对这套数据集最平衡。真机部署时还有一个小细节直接把全帧缩放到640会损失画面边缘的信息。如果猫经常在摄像头边缘活动建议预处理时做letterbox填充而不是暴力拉伸YOLO官方代码默认就是letterbox但自己做管道时要保持一致否则推理效果会莫名下降。这套猫情绪检测的项目做到这里功能上已经相当完整了。我个人在实际操作中最大的体会是数据集的设计感比数据量本身更容易被忽视却又是决定模型上限的隐性因素。标注规范的严谨程度直接决定了混淆矩阵的干净程度采集图片的多样性决定了模型在不同光线和场景下的泛化能力训练参数里的细节则决定了收敛速度和最终精度。最后再分享一个小技巧每轮训练结束后把验证集的预测结果把错误样本按类别归档你会发现自己对模型在哪些情绪上容易犯错的判断远比自己预想的准确得多。这些错误样本就是下一轮数据补充和标注修正的明确指南。
返回列表