news 2026/10/11 9:47:54

YOLO犬类情绪识别实战:从目标检测到行为特征分类的完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLO犬类情绪识别实战:从目标检测到行为特征分类的完整方案

简介:基于YOLO的犬类情绪识别设计是一份面向深度学习教育场景的完整项目资源包,适合毕业设计、课程设计或期末大作业使用。它围绕犬类情绪分类这一具体任务,展示了从数据准备、模型训练到测试部署的完整链路,帮助学习者掌握目标检测与图像识别项目的基本方法。资源共含72个文件,以41张jpg、14张jpeg和8张png图像数据为主,并配有Python测试脚本、3个Markdown说明文档以及训练结果动图与曲线图,整体约60.25MB,结构清晰,便于按图索骥。目前已有57人浏览学习,适合正在开展相关课题的学生或研究者参考。通过该资源,使用者可直观了解YOLO在动物情绪识别上的应用思路,包括图像标注与样本组织方式、模型训练参数调整、结果评估指标(如PR曲线、混淆矩阵)的查看方法,同时可借鉴其代码组织与文档撰写方式,快速迁移到自己的实验或项目之中。

1. 让YOLO做犬类情绪识别:检测只是第一步,后面的坑才是主战场

把「基于YOLO的犬类情绪识别」拆开看,你会发现它其实是一个两阶段问题:先用目标检测把画面里的狗找出来,再对每只狗判断情绪状态。很多刚入门的朋友以为只要把数据集换成“狗的照片”,训练一个分类器就能输出“开心/生气”,实际跑起来才发现,YOLO只负责回答“狗在哪”,而情绪识别需要另一套特征设计、时序处理和标签体系,才能从“检测到狗”走到“判断这只狗是焦虑还是放松”。这篇文章要解决的,正是从数据标注、模型训练到部署验证的完整落地路径。

做这个方向的人,一般是宠物产品研发工程师、动物行为研究者,或者想用视觉方案做宠物看护、医院候诊监测的创业者。你需要具备基本的Python能力,能跑通Ultralytics YOLO的训练命令,并且愿意在数据标注上投入时间。如果只想拿现成模型跑一跑demo,直接跳过前两章也没问题;但如果你想做一个能真正上线的东西,数据这一关绕不过去。

2. 把「犬类情绪」拆成YOLO能学的东西:两阶段方案与标签体系设计

2.1 为什么不做端到端的情绪识别模型

情绪识别在计算机视觉里属于“细粒度视觉理解”问题,表达情绪的线索分散在耳朵、眼睛、尾巴、整体姿态和口鼻区域,单靠一个检测模型很难直接回归出情绪类别。早期我试过用ImageNet预训练的ResNet直接对整张图做情绪分类,翻车很严重:狗在画面里的位置一变、背景换个颜色、甚至离镜头远近不同,预测结果就剧烈抖动。原因很简单,当狗只占画面10%时,模型学的其实是背景和狗的整体轮廓,根本看不到耳朵、尾巴这些关键部位。

常见的做法是拆成两阶段。第一阶段用YOLO做犬体检测,输出每个个体的边界框;第二阶段对每个边界框做处理——要么把框内图像裁剪出来送进一个独立的情绪分类器,要么用YOLOv8-pose提取耳朵、尾巴、眼睛等关键点,再把关键点坐标和相对距离拼成特征向量送进分类器。两阶段的好处是可调试:检测框不准,你单独优化检测器;分类不准,你单独优化分类器。整个链路里黑匣子变少了。

2.2 情绪标签的坐标系:从行为学到可直接标注的类别

这是整个项目里最需要耐心的部分。人类表情有通用的动作单元编码(AU),犬类没有这么完善的标准,但行为学上常用的情绪维度是:兴奋(Excitement)、放松(Relaxed)、警惕(Alert)、焦虑(Anxious)、恐惧(Fearful)、攻击性(Aggressive)。实际标注时不能直接让大家看一张照片凭感觉选“焦虑”,因为焦虑和恐惧的视觉线索重叠度太高,不同标注员的判定可能互相打架。我一般会把标签再细化成“可观察姿势组合”,用表格定义每条标签对应的部位特征,而不是让标注员猜情绪。

情绪标签典型身体线索对应的YOLO可观察区域
放松尾巴自然下垂或轻摆、耳朵自然、身体舒展、嘴微张全身轮廓、尾巴位置、耳根角度
兴奋尾巴高频上翘摆动、前肢下压、身体重心前移、嘴张开尾巴朝向、前足位置、身体姿态比
警惕耳朵竖直前倾、身体僵硬、目光专注、尾巴僵直耳尖Y坐标、尾巴摆动幅度、头部朝向
焦虑耳朵后贴、尾巴夹紧、频繁舔唇、哈欠耳根位置、尾巴遮挡、口鼻区域纹理
恐惧身体压低、耳朵后贴、尾巴夹于两腿之间、颤抖躯干高度/宽度比、尾巴可见面积
攻击性呲牙、皱眉、身体前倾、尾巴高高竖起且僵硬口鼻区域开合、眉毛角度、躯干倾斜角

标注时我倾向于做“部位级标注”而不是“全图级标注”:先标犬体框,再针对耳、尾、口鼻三个区域打状态标签。比如一只狗耳朵后贴且尾巴夹紧,这两个区域各自的状态组合起来,再推导出“焦虑”这个结论。虽然标注工作量变大,但模型的判读逻辑变得可追溯,后续如果发现误判,你能知道是耳朵识别错了还是尾巴识别错了。

2.3 推荐Pipeline:犬体检测 + 部位ROI + 情绪分类

做了一轮试验后,我最后稳定下来的方案是:YOLOv8检测犬体 → YOLOv8-pose提取头部、耳朵、尾巴附近的关键点 → 把关键点距离、角度和躯干比例组成低维向量,送入一个多层感知机分类器。完整结构如下:

# pipeline_demo.py:犬类情绪识别两阶段推理流程示意 import torch import numpy as np from ultralytics import YOLO # 第一阶段:犬体检测 det_model = YOLO("yolov8n.pt") # 用预训练权重迁移,后续替换为自定义狗检测权重 # 第二阶段:关键点提取 pose_model = YOLO("yolov8n-pose.pt") # pose模型可同时输出躯干和关键点 results = det_model.predict("test.jpg", conf=0.45, classes=[0]) # COCO中狗是第0类 for r in results: boxes = r.boxes.xyxy.cpu().numpy() for box in boxes: x1, y1, x2, y2 = [int(v) for v in box] crop = im[y1:y2, x1:x2] pose = pose_model(crop, conf=0.3) keypoints = extract_keypoints(pose) # 拿到15个关键点坐标 feats = build_behavior_features(keypoints) # 计算耳位角、尾巴摆动幅度、躯干比例 emotion = mlp_classifier.predict(feats) # 输出情绪类别

这段代码里值得注意的参数有三个。conf=0.45表示只保留置信度45%以上的检测框,犬类在监控画面中经常半遮挡,阈值设太高等于是把难样本全丢了,设太低又会把背景误检成狗。classes=[0]限定只检测COCO类别中的狗,避免把猫、人、沙发框进来干扰后续裁剪。而conf=0.3用于pose模型,因为身体部位比整体目标更难识别,置信度可以放宽。真正上线时,我建议把“犬体检测”和“情绪分类”拆成两个独立服务,检测器跑GPU,分类器跑CPU,这样显存占用能被压下来。

3. 训练数据怎么来:COCO格式标注、抓取清洗与类别平衡

3.1 犬类情绪数据集的3个现实来源

犬类情绪识别的首要问题不是模型,而是数据。现有开源数据集里,标注好情绪的犬类图片非常稀缺,我整理下来主要有三个来源:第一,从视频网站或短视频平台抓取宠物狗行为录像,按帧抽取,再人工筛选画面清晰、光线正常的帧,这类数据贴近实际场景,但标注成本高;第二,在宠物医院、犬舍、寄养中心等场地部署临时摄像头,自采一段时间的监控视频,好处是连续帧可以构成时序样本,模型能学到“从放松转为焦虑”的过程;第三,用现有公开的狗类数据集作为检测任务的训练基底,比如COCO里的狗类样本、Stanford Dogs,这些只解决“找到狗”的问题,情绪标签必须自己补标注。

我不建议一开始就追求上万张图。犬类情绪识别的类别差异很大,一张图里往往只有口鼻和尾巴能传递关键信号,有效样本比总样本量更重要。我一般准备800到1500张已经人工清洗过的图片,就能把一个基础版本的情绪分类器训练到可以上demo的程度。

3.2 用LabelImg标注COCO格式的最小流程

标注工具我常用LabelImg,它保存的是Pascal VOC格式的XML文件,但我们可以通过脚本转成YOLO训练需要的格式,也可以直接用LabelImg的YOLO模式保存为txt。下面是最小可用的安装和启动命令。

# 安装labelImg标注工具(依赖Python3和PyQt5) pip install labelImg # 启动标注工具 labelImg # 在界面里设置: # 1. 打开图片目录 # 2. 点击“Change Save Dir”设置标注输出目录 # 3. 在PascalVOC和YOLO两种模式之间切换(菜单栏 View → Auto Saving mode) # 4. 按W键创建矩形框,为每只狗框出边界,并输入标签 dog

参数上最容易踩坑的是Change Save Dir这一步,很多新手打开图片就标,结果发现XML文件被存到了图片同目录,跟原图混在一起,后续数据集划分脚本处理起来很麻烦。我习惯建立独立的labels目录。另外,框选范围有一个玄学规则:边界离狗身体尽量近,但尾巴如果完全伸展开,一定要把尾巴尖端框进去。因为尾巴是情绪识别的重要线索,框太紧把尾巴截掉,相当于给模型丢掉了关键特征。

3.3 数据清洗与类别平衡:少样本类别的三类干预

情绪数据天然不平衡。我实际统计过自己采集的样本,放松状态占了约55%,警惕占25%,焦虑只占8%,恐惧和攻击性各占6%左右。如果不做任何干预,模型会把所有模棱两可的样本判成“放松”,因为这样做损失最小。常见的处理手段有三种:对少数类样本做过采样,也就是在训练集里重复出现;对多数类做欠采样,删掉一部分冗余的放松样本;或者使用Copy-Paste增强,把焦虑、攻击性样本中的狗抠出来,粘贴到不同背景上,既增加样本量又缓解背景过拟合。第三种方法在YOLO系模型上尤其有效,因为检测器本身需要学习“目标在任何背景中都存在”这一事实。

下面是一个简单的Copy-Paste增强脚本,核心逻辑是用检测框掩码把狗从原图抠下,再缩放粘贴到随机背景图。

# copy_paste_aug.py:少样本类别增强 import cv2 import numpy as np def paste_object(src_img, src_box, dst_img, scale=0.8): x1, y1, x2, y2 = src_box obj = src_img[y1:y2, x1:x2] h, w = int((y2 - y1) * scale), int((x2 - x1) * scale) obj = cv2.resize(obj, (w, h)) # 随机选择粘贴位置,避开画面边缘和中心偏下区域 dst_h, dst_w = dst_img.shape[:2] x_off = np.random.randint(0, max(dst_w - w, 1)) y_off = np.random.randint(0, max(dst_h - h, 1)) # 直接用矩形区域替换,不做精细边缘融合;对YOLO训练足够 dst_img[y_off:y_off+h, x_off:x_off+w] = obj return dst_img, [x_off, y_off, x_off+w, y_off+h]

这个脚本里scale=0.8是为了防止目标在粘贴后过大或过小,因为真实监控画面中狗大多占画面的20%到40%。严格来说还能用泊松融合消除粘贴边缘颜色突变,但实际操作中发现,YOLO对边缘融合并不敏感,直接用矩形替换反而保留了一些真实遮挡关系,训练效果差别不大。粘贴后要同步更新标注框的位置,这一点经常被人忽略,导致标签和图像错位,训练时损失值高得不正常。

3.4 用脚本做训练集/验证集划分与统计

标注完成后,接下来要做的是数据集划分和格式转换。我习惯把数据按照3:1:1的比例拆成训练、验证、测试三份,但要求同一只狗的所有帧必须出现在同一个集合里。这个要求极其关键,否则模型在训练集中见过某只狗,验证时又见到同一只狗的不同姿态,测出来的指标会虚高,上线后立刻打回原形。

# 目录结构约定 data/ images/train/ images/val/ images/test/ labels/train/ labels/val/ labels/test/ dog_emotion.yaml

dog_emotion.yaml内容如下:

# dog_emotion.yaml:YOLO训练配置文件 # 注意:路径必须写绝对路径,Ultralytics在某些版本中不支持相对路径自动拼接 train: /home/user/dog_emotion/images/train val: /home/user/dog_emotion/images/val test: /home/user/dog_emotion/images/test nc: 1 names: ['dog']

这里nc: 1意味着第一阶段只做“狗”这一个类别的检测。如果你把每个情绪状态都当成独立类别做检测,比如“兴奋的狗”和“焦虑的狗”当成不同的检测目标,模型会学得很差,因为同一只狗在不同情绪下外形差异远小于不同狗之间的外形差异。检测器只负责定位,情绪交给第二阶段分类器,这是最稳的做法。

4. 用YOLOv8训练犬体检测器:配置、命令与损失函数的影响

4.1 选YOLOv8还是YOLOv5:训练资源与精度的权衡

有人问我为什么不用YOLOv5,或者最新的YOLO系列其他分支。我的判断依据很简单:Ultralytics的YOLOv8工程化程度最高,预训练权重、导出ONNX、部署文档都齐全,适合快速验证和项目落地。如果你的显存只有4GB到6GB,我建议直接选yolov8n.pt或yolov8s.pt,它们参数量小,训练一张640×640的图占用不到4GB显存。如果手里有16GB以上显存的卡,可以考虑yolov8m.pt,精度提升明显。

选型时另外一个关键因素是推理设备。如果你最终部署到树莓派或者Jetson Nano这类边缘设备,那么n系列基本是唯一选择;如果跑在服务器或PC上,s或m更合适。不要一上来追求大模型,狗的体型差异很大,柯基和边牧从外形到比例完全不同,检测器首先得学会“这是狗”而不是“这是柯基”,太大的模型容易在训练阶段过拟合到训练集的品种结构上。

4.2 一步到位的数据yaml与训练启动命令

训练命令行如下,这一条命令可以完成从加载预训练权重到输出可部署模型的全过程。

# 使用YOLOv8n预训练权重,在自己的犬类数据集上微调 yolo train \ data=/home/user/dog_emotion/dog_emotion.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=15 \ project=/home/user/dog_emotion/runs \ name=dog_detector

参数说明:epochs=100是常规设置,但配合patience=15会在验证集指标连续15轮不提升时提前终止,实际训练一般在40到60轮就收敛了。imgsz=640是YOLOv8的默认输入分辨率,如果你发现狗在画面中非常小,比如监控画面里狗只占几十个像素,可以尝试imgsz=960,小目标召回率会有明显改善,但显存占用和训练时间也随之上涨。lr0=0.01是初始学习率,微调场景下0.01比默认的0.02稳,尤其是自定义数据集的标注噪声比较大时,学习率太大模型容易被个别错误标注带偏。

训练结束后,模型会保存在项目目录下weights/best.pt和last.pt。判断用哪个:best.pt是验证集上精度最高的模型,last.pt是训练结束时那个epoch的模型。我一般只看best.pt,但如果你遇到验证集指标震荡很厉害,可以把last.pt也拿去做测试对比,有时后者反而泛化更好,这是一种常见的“玄学”情况,背后的原因是验证集本身分布不够全面,训练末期的权重恰好躲过了一些过拟合陷阱。

4.3 训练日志里应该盯哪三个指标

训练过程中,控制台会滚动输出box_loss、cls_loss、dfl_loss以及mAP50、mAP50-95。box_loss是边界框回归的损失,cls_loss是分类损失,dfl_loss是分布焦点损失。对犬类检测来说,box_loss下降不理想往往意味着标注框本身质量差,比如框过大、过小或者边缘被毛发干扰。cls_loss高则说明“狗”这个类别的特征内部差异太大,可能需要增加数据或调整类别定义。

训练结束后,最好打开results.csv画出指标变化曲线,确认mAP50和mAP50-95都处于上升状态。

# 读取训练结果的损失曲线与指标 import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("/home/user/dog_emotion/runs/dog_detector/results.csv") # 检查损失是否收敛 plt.plot(df["epoch"], df["train/box_loss"], label="box_loss") plt.plot(df["epoch"], df["val/box_loss"], label="val_box_loss") plt.legend()

这个脚本的用途是快速定位训练异常。如果你发现验证损失先降后升、训练损失持续下降,模型已经过拟合到训练集中的环境背景了。此时优先检查训练集中的图片是否来自单一拍摄场地,如果是,需要在训练集里补充多场景样本,而不是简单加大正则。另一个值得盯的指标是mAP50-95和mAP50之间的差距,正常情况下差距在0.1到0.2之间;如果差距过大,说明检测框定位不够精准,模型对犬体边缘的把握很差,典型原因是标注框本身抖动太大。

4.4 从检测框到情绪分类的特征抽取

检测器训好后,就可以对每个检测框做情绪特征提取。最直接的方法是裁剪框内图像,缩放后送进一个在犬类情绪数据上微调过的图像分类器,比如EfficientNet或MobileNet。但我更推荐把YOLOv8-pose的关键点加入特征,因为姿态特征比像素特征更稳定,也更接近行为学家的判断逻辑。

# extract_features.py:把YOLO检测框转换为情绪分类器的输入特征 import numpy as np from ultralytics import YOLO pose_model = YOLO("yolov8n-pose.pt") def build_behavior_features(keypoints): # keypoints是shape [15, 3]的数组,最后一维是置信度 nose = keypoints[0] ear_l, ear_r = keypoints[3], keypoints[4] tail = keypoints[12] if keypoints.shape[0] > 12 else nose # 耳朵后贴程度:双耳中点与鼻尖的夹角 ear_mid = (ear_l[:2] + ear_r[:2]) / 2 snout_vec = nose[:2] - ear_mid ear_spread = np.linalg.norm(ear_l[:2] - ear_r[:2]) # 尾巴夹紧程度:尾巴关键点与躯干中心的距离 body_center = (keypoints[5][:2] + keypoints[11][:2]) / 2 tail_dist = np.linalg.norm(tail[:2] - body_center) # 身体压低程度:肩部与髋部的垂直距离比例 shoulder = keypoints[5][1] # 肩部Y坐标 hip = keypoints[11][1] # 髋部Y坐标 body_low = abs(shoulder - hip) / max(abs(shoulder + hip), 1e-6) return np.array([ear_spread, tail_dist, body_low, nose[2]])

参数说明:这里的15个关键点是COCO-Pose的约定顺序,索引3和4对应左右耳朵,5是左肩,11是左髋,12是尾巴或腰部位置。由于YOLOv8-pose在宠物狗身上的预训练效果不如人类好,关键点置信度普遍偏低,所以我把所有特征都归一化到相对尺度,并且给nose[2]这个置信度也作为特征传入分类器,当关键点不可靠时,分类器可以学会不依赖姿态特征,而转向看图像颜色纹理。

5. 犬类情绪识别避坑:5个高频翻车点与排查方法

5.1 半遮挡场景下犬体检测漏检,情绪识别无米下锅

现象:多只狗在画面里相互遮挡,或者在笼子里只露出头和前爪,YOLO的检测框频繁闪烁,一会儿框到狗一会儿框到背景边缘,导致情绪分类器根本没有稳定的输入。

原因:半遮挡和身体蜷缩在训练数据里占比太少。YOLO在训练时看到的多是完整舒展的狗,一旦遇到遮挡,模型提取到的局部特征不足以支撑高置信度检测。

解决:在数据集中专门加入“遮挡样本”类别,标注时即使只露出头部和一只耳朵,也要把可见部分完整框出来。另外把conf阈值从默认的0.25提升到0.4,宁可漏检也不接受低质量检测框,因为一个偏移的框送进情绪分类器,产生的错误预测比漏检更难受。

5.2 攻击性与兴奋的混淆,嘴上在笑实际要咬人

现象:模型把呲牙、前压姿势的狗判成兴奋,把尾巴僵直高举也判成兴奋。在实际场景中这是最危险的误判,因为兴奋和攻击前兆的视觉相似度很高,都表现为张嘴、耳朵竖起、身体活跃。

原因:单帧图像难以捕捉“攻击性”特有的时间特征,比如身体前倾速度、尾巴僵硬抖动频率。从静态图看,兴奋的狗张嘴摇尾巴,攻击的狗呲牙、竖尾巴,两者的区别更多集中在肌肉张力和运动趋势上。

解决:用连续帧做时序判断,不要对单帧输出情绪结论。对同一个检测ID连续取5到10帧的特征向量,计算耳尖位置和躯干倾斜角度的一阶差分,然后把差分值和原始特征一起送入分类器。这种“状态+变化量”的输入方式,能把攻击前兆的误判率降低一个明显档次。

5.3 幼犬和迷你犬种检测率暴跌,框到一半就没了

现象:幼犬体型小,或者像吉娃娃这类品种整体形态贴近小型犬,训练好的检测器在遇到这类目标时,检测框要么偏大把背景框进去,要么只框住躯干漏掉头部。

原因:YOLO的Anchor-Free检测头对目标尺寸有一定敏感范围,训练集中如果缺少小尺寸目标,模型就很难对小目标做出精确定位。

解决:把训练图像的imgsz从640提高到960,相当于把原始图像放大后再训练,小目标的像素面积变大,检测器能学到的细节更多。同时可以把训练集中的小目标实例做Mosaic增强之外的“大图切片”,也就是把原图切成四个象限分别训练,让模型在推理时也能处理更大分辨率。

5.4 背景过拟合,换个环境模型就失灵

现象:模型在训练集来源的犬舍场景里mAP很高,一换到宠物医院候诊室,检测精度下降,情绪分类结果也变得混乱。

原因:训练集中的狗常常和特定笼舍、特定地板颜色同时出现,检测器连带学习了背景纹理。这不是YOLO的缺陷,而是所有监督模型的通病,只要背景方差小,模型就会偷懒去学背景。

解决:做数据采集时有意识地覆盖多个场景,至少包含室内、室外、不同光照、不同地面颜色四个维度。如果实在采集不到多场景数据,可以用Copy-Paste增强把狗粘贴到不相关的背景上,同时把检测器输入图片做随机颜色扰动(HSV增强),削弱模型对背景色块的依赖。

5.5 类别不均衡让“焦虑”和“恐惧”几乎不被预测

现象:测试集里凡是模棱两可的样本都被预测成“放松”或“警惕”,焦虑和恐惧类别的召回率只有10%左右。

原因:训练集中这两个类别的样本太少,分类器没有足够的证据去建立决策边界。此时单纯调阈值没有用,因为特征空间里少数类样本几乎被多数类覆盖。

解决:除了在数据层面做重采样,还可以调整分类器的损失函数。对犬类情绪分类器,我使用加权交叉熵,权重按类别样本数的反比计算。代码里的class_weight是手动指定的,通常焦虑和恐惧的权重设为3到5,放松设为0.5,让模型在训练时对少数类误判付出更高代价。代价敏感训练只能缓解问题,最终还是要靠补充数据。

# weighted_loss.py:给情绪分类器配置类别权重 from sklearn.utils import class_weight import numpy as np # y_train是训练集所有样本的情绪标签 classes = np.array([0, 1, 2, 3, 4, 5]) # 对应放松、兴奋、警惕、焦虑、恐惧、攻击 weights = class_weight.compute_class_weight("balanced", classes=classes, y=y_train) # 输出类似 [0.5, 1.2, 1.1, 3.2, 3.8, 4.1] # 将weights传入torch.nn.CrossEntropyLoss(weight=torch.tensor(weights, dtype=torch.float32))

这段代码的关键是class_weight.compute_class_weight会根据每个类别的样本频率自动计算反向权重。样本少的类别获得较大权重,模型更新时会产生更大的梯度,从而迫使分类器把边界向多数类方向推。这个方法能显著改善少数类的召回率,但也要注意别过头——把权重设到6以上时,模型会把大量放松样本误判成焦虑,需要在验证集上反复试探一个平衡点。

6. 部署落地与验证:把模型跑进真实犬舍与宠物医院

6.1 导出ONNX并用CPU推理的最小代码

训练完成后,如果部署设备上没有PyTorch环境,最好把模型导出为ONNX格式,然后用onnxruntime推理。这样不仅摆脱了Ultralytics的依赖,推理速度也能提升。

# 导出为ONNX,opset=12兼容性较好 yolo export model=/home/user/dog_emotion/runs/dog_detector/weights/best.pt format=onnx opset=12

导出后的ONNX文件可以直接加载:

# onnx_infer.py:不依赖ultralytics的轻量推理 import onnxruntime as ort import cv2 import numpy as np session = ort.InferenceSession("best.onnx") input_name = session.get_inputs()[0].name img = cv2.imread("test.jpg") img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (640, 640)) img = img.astype(np.float32) / 255.0 img = np.transpose(img, (2, 0, 1))[None] outputs = session.run(None, {input_name: img}) # 输出格式为 [1, 6, 8400],分别是 cx, cy, w, h, conf, class_prob

这里要注意ONNX导出的输出是未经过NMS后处理的原始预测,8400对应640×640输入下不同尺度的预测框数量。如果你需要部署到生产环境,还需要自己写非极大值抑制逻辑,或者直接用yolo predict的Python API在服务端跑。我的经验是:如果并发量不大,直接保留Ultralytics的Python推理反而更省事,NMS和类别过滤都帮你做好了。

6.2 时序平滑:单帧情绪识别为什么不可靠

单帧图像的情绪预测会有明显的抖动,同一个视频片段里,前后两帧的预测结果可能在“放松”和“警惕”之间反复横跳。原因很简单,狗在呼吸、摆动尾巴或者调整姿势时,关键点坐标每一帧都有个位数的像素抖动,这些抖动被情绪分类器放大成了类别切换。常见的解法是滑动窗口投票:每5帧为一个窗口,对每个窗口内的预测结果取众数,作为最终输出。

# smoothing.py:滑动窗口多数投票 from collections import deque, Counter window = deque(maxlen=5) def smooth_predict(raw_pred): window.append(raw_pred) if len(window) < 5: return None # 数据不足,不输出结论 counter = Counter(window) # 众数结果;如果票数并列,取置信度最高的一帧 return counter.most_common(1)[0][0]

如果把窗口长度从5改为9,情绪切换的响应会变慢,但稳定性更好。我一般在监控实时性要求不算高的场景用9帧窗口,在交互场景里用5帧。这里的关键调整是maxlen和最后返回条件,窗口未填满时不输出任何结论,避免冷启动时给出无意义预测。

6.3 用一段带标注的视频检验泛化边界

训练指标只能说明模型在验证集上的表现,真正的验收要放到一段独立拍摄的视频上。我习惯的做法是:拍一段5分钟左右、包含不同场景和情绪状态的视频,按每秒一帧提取,人工为每一帧标注情绪标签,然后让模型跑同一条视频,最后输出一个逐帧对比的错误矩阵。通过这个矩阵能看到模型具体在哪些时刻误判。

最值得注意的往往是误判发生的时间轴特征。比如一个放松的狗在听到门外声音时耳朵突然竖起,模型会在状态切换的过渡帧上频繁误判,这是正常的,因为训练集里也很难覆盖所有过渡姿态。我能给出的经验是:与其费大量精力处理过渡帧,不如在业务逻辑上做一个3秒的延迟确认,情绪状态必须在连续3秒内保持稳定才触发告警或通知,这样能过滤掉绝大多数过渡抖动。

做这个项目让我学到最重要的一件事是:情绪识别系统的瓶颈不在模型结构,而在数据定义。我第一次把“尾巴夹紧”粗暴地当成恐惧的条件,结果把一只正在排便的狗判成了恐惧,后来才意识到尾巴的姿态要和身体整体姿态结合起来解读。后面我给整个系统加了“身体压低”和“耳根后贴”两个伴随特征,误判才明显减少。如果你准备做这个方向,我的建议是从小数据、多特征、可解释的角度出发,先跑通一个能稳定识别2到3种情绪的版本,再逐步扩展类别。希望这些踩坑记录能帮你少走一段弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 9:46:20

rea工具集:搞定批量重命名、代码重构与文件整理

如果你和我一样&#xff0c;经常要在一堆旧项目里做重复的机械变更——批量改名、重整目录、统一日志格式、扫出废弃的重复代码……你可能也会需要一个 “rea”。这不是什么新框架&#xff0c;是我花了几周实际打磨、又在上百个文件规模的项目里反复跑过才敢拿出来说的工具集合…

作者头像 李华
网站建设 2026/10/11 9:45:47

Transformer工程落地实操指南:从能跑到可控的144页技术地图

简介&#xff1a;本资源是一份面向AI初学者与NLP进阶学习者的Transformer架构系统性入门材料&#xff0c;聚焦解决“如何从零理解注意力机制与Transformer设计思想”这一核心问题。内容覆盖Transformer出现前的序列建模方法&#xff08;MLPs、1D CNNs、RNN/LSTM/GRU&#xff09…

作者头像 李华
网站建设 2026/10/11 9:45:31

ABB UPS配套后备保护与电池开关常见问题解答

Q1&#xff1a;UPS配套后备保护包括哪些回路&#xff1f; 答&#xff1a;共四大类回路&#xff1a;交流输入回路、静态/维修旁路回路、交流输出回路、直流电池回路。其中直流电池回路又分主进线与分组/支路两个层级。 Q2&#xff1a;为什么不能用交流断路器代替直流断路器&…

作者头像 李华
网站建设 2026/10/11 9:45:23

impeccable:零缺陷可验证的工程质量标准

1. “impeccable”不是一句空泛夸奖&#xff0c;而是可拆解、可验证、可复现的专业标准最近在多个技术评审会和设计交付现场&#xff0c;反复听到这个词被高频使用&#xff1a;“这个接口文档写得真impeccable”“UI动效的时序控制做到了impeccable级别”“CI/CD流水线的错误拦…

作者头像 李华
网站建设 2026/10/11 9:43:18

impeccable项目拆解:从零搭建代码质量检查工具与工程实践

1. 一个词引发的产品思维&#xff1a;为什么“impeccable”值得单独拿出来做第一次看到“impeccable”这个词被单独拎出来当作项目标题&#xff0c;我的直觉是&#xff1a;这要么是一个强迫症级别的代码规范工具&#xff0c;要么是一个追求极致体验的产品设计系统。不管是哪种&…

作者头像 李华
网站建设 2026/10/11 9:42:52

舌象诊断系统实战:基于ResNet50的中医望诊图像分类与部署

简介&#xff1a;这是一套面向中医数字化与计算机视觉研究者的舌象诊断系统源码包&#xff0c;基于深度学习方法完成舌象图像的分类识别与辅助诊断&#xff0c;适合具备一定编程和深度学习基础的学生、工程师用于复现实验、课题研究或二次开发。压缩包共182个文件&#xff0c;大…

作者头像 李华