YOLOv7姿态估计实战:从推理训练到ONNX部署与评估 简介基于YOLOv7的人体姿态估计示例工程面向正在学习目标检测与关键点识别的Python开发者涵盖预训练模型加载与关键点推理示例。压缩包内含可运行的pose-estimate.py脚本及配套模块其中utils目录封装了数据增强、损失计算、锚框生成、激活函数、指标评估等工具逻辑models目录定义网络结构与推理组件并配有requirements.txt依赖清单和README说明无需额外调试即可跑通流程。包内还附带了足球场景的测试视频便于直接观察姿态估计效果。资源共46个文件主体为19个Python源码及23个编译缓存pyc文件另含模型说明、许可证与说明文档整体压缩包约3.73MB轻量易部署。已有215人学习适合希望快速上手YOLOv7姿态估计、了解工程目录组织方式的入门者通过阅读源码和运行示例可理清从图像输入到关键点输出的完整流程并基于utils与models模块进一步替换数据集或调整网络结构开展自定义姿态识别实验。1. yolov7 做姿态估计的核心换取把检测输出换成关键点张量做过视频动作分析的人都知道OpenPose 那套自底向上管线部署起来有多折腾。换个角度想如果模型能先给出可靠的人体框再接一个关键点分支整个流程会顺很多。yolov7 的 pose 分支就是这个思路的工程化产物训练时仍然使用检测损失推理时却能拿到 17 个关键点和置信度。这篇文章就用示例代码把这条路走通同时说明预训练模型的落地细节。适合接下来要接触姿态估计、想复用现有检测训练流水线、或者需要快速验证某个动作识别思路的开发者。读完你能独立完成推理、训练、ONNX 导出和关键点准确率验证。2. 原理和选型yolov7-pose 为什么值得用来做姿态估计2.1 自顶向下路径里yolov7 提供了最顺手的检测主干姿态估计在许多工程场景中并不需要从零训练一个网络。自顶向下的方法先把人检测出来然后对每个检测框做关键点回归。YOLOv7 在目标检测上的部署生态相对成熟TensorRT 转换、量化工具链、各种边缘设备的适配教程都比较齐全这些积累可以直接迁移到姿态估计任务上。相比 HRNet 这类专门做姿态估计的网络yolov7-pose 的优势在于整个推理管线更短不需要单独部署一个检测器再花时间把检测框裁剪后送进第二个模型。它的缺点同样明显如果画面中人脸密集遮挡回归效果通常不如 top-down 方法。在实际项目里我会先跑一遍 yolov7-pose 看基线再决定是否需要升级到两阶段方案。2.2 网络输出维度的变化和损失函数设计从检测模型到姿态模型yolov7 并没有做出结构性改动关键是输出张量维度变了。原版 yolov7 输出包含4 个边界框坐标、1 个目标置信度、以及 80 个分类概率。而 pose 版本把分类部分替换成了关键点回归。以输入尺寸 960x960、单尺度测试为例模型有三个输出头特征图分别下采 8、16、32 倍对应 120x120、60x60、30x30 三组网格总预测框数量是 120*120 60*60 30*30 18900 个。每个预测框携带的通道数是 4 1 17*3 56。其中 51 维是 17 个关键点的 x、y 坐标和可见性置信度。注意这里的坐标是在输入图像坐标系下的绝对偏移值而不是归一化后的比例值。关键点损失部分采用的是 OKS 感知的回归方式模型先预测每个点的坐标计算该坐标与真实标注之间的欧氏距离再用该距离除以该目标的尺度归一化因子经过高斯核映射后得到一个 0 到 1 的相似度分数最后用 MSE 让网络去逼近这个分数。这样做的好处是损失大小和目标大小解耦网络在远近不同的目标上回归难度是接近的。2.3 预训练模型包含什么以及如何选择权重文件标题里提到的预训练模型通常是指官方在 COCO 训练集上训练好的权重文件大约 170MB包含整个网络的权重而不是单独的检测头或关键点头。使用它有两种方式一是直接加载后做推理适用于人体框和关键点效果都还不错的场景二是作为预训练权重在自己的数据集上继续微调。选择权重的原则并不复杂显存充足就选输入尺寸更大的权重精度会好一些但推理速度会明显下降。自己在 CPU 上做验证时应该把推理脚本里所有与 CUDA 相关的逻辑都做降级处理确保没有 GPU 也能跑通流程。官方随机初始化的权重使用 detect 分支的权重来初始化 keypoint 分支中的共享层需要把检测相关的参数过滤掉这里建议直接采用官方的预训练权重避免自己处理网络结构不匹配的问题。3. 环境准备与最小推理示例下载预训练模型到跑通第一张图的完整命令3.1 用 conda 搭一个能跑 torch 和 opencv 的干净环境考虑到 yolov7 仓库相对稳定的运行环境是 Python 3.8 配合 PyTorch 1.13 左右的版本建议不要直接使用最新版本 PyTorch容易遇到算子和接口兼容问题。示例代码如下conda create -n yolo_pose python3.8 -y conda activate yolo_pose pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117 pip install opencv-python pillow numpy matplotlib pyyaml tqdm tensorboard这里刻意把 torch 指定到 cu117 版本是因为该版本与大部分 30 系、40 系显卡驱动的兼容性很好。如果机器上没有 GPU去掉 cu117 后缀安装纯 CPU 版本即可推理代码不需要改动只会慢一些。3.2 下载预训练权重并确认完整性将官方仓库克隆到本地后从 Releases 页面下载 yolov7-w6-pose.pt 权重文件。下载完成后先校验文件大小是否符合预期再用 torch.load 加载验证import torch ckpt torch.load(yolov7-w6-pose.pt, map_locationcpu) print(ckpt.keys()) print(ckpt[model].type)这段代码用于确认权重文件没有被截断打印出的 model 类型是 yolo 模型封装类说明权重结构正常。不要跳过这一步很多人下载到一半断了加载时报错后才意识到文件损坏。3.3 推理示例代码解析输出张量的关键操作yolov7-pose 的原始 repo 中自带 detect 脚本但里面包含太多命令行参数和保存逻辑不利于理解。下面给出一个最小化的推理脚本只依赖 torch、cv2 和 numpyimport cv2 import numpy as np import torch from models.experimental import attempt_load from utils.general import non_max_suppression_kpt def infer_pose(image_path, weightsyolov7-w6-pose.pt): device torch.device(cuda:0 if torch.cuda.is_available() else cpu) model attempt_load(weights, map_locationdevice) model.eval() img0 cv2.imread(image_path) img cv2.resize(img0, (960, 960))[:, :, ::-1].transpose(2, 0, 1) img np.ascontiguousarray(img) img_tensor torch.from_numpy(img).float() / 255.0 img_tensor img_tensor.unsqueeze(0).to(device) with torch.no_grad(): pred model(img_tensor)[0] output non_max_suppression_kpt( pred, conf_thres0.25, iou_thres0.45, ncmodel.yaml[nc], nkptmodel.yaml[nkpt] ) return output, img0 out, img0 infer_pose(test.jpg)non_max_suppression_kpt是仓库自带的 NMS 函数它和普通目标检测的 NMS 不同之处在于处理关键点的逻辑每个检测框附带 51 维关键点数据在对置信度排序和 IoU 计算时只依赖前 6 维4 个坐标 1 个置信度 1 个类别分数但最终输出时要把完整的关键点张量一并保留下来否则后面的姿态可视化会拿不到点坐标。3.4 常见报错与处理方式第一个常见报错是加载模型时提示某些层名称不匹配通常是 torch 版本问题导致的。yolov7 仓库在 PyTorch 1.13 下运行最稳定attempt_load会自动处理权重键值对中的前缀差异但如果手动使用torch.load再自己拼模型结构会频繁踩中Missing key(s)的坑。因此建议始终使用仓库自带的加载入口。第二个报错是显存不足。输入 960x960 情况下推理显存接近 1.8GBbatch size 为 1 时需要注意。老显卡跑不动就把输入尺寸降到 640输出维度中的网格数会按照缩放比例衰减不会影响正常解析只是小目标的检测精度会下降一些。4. 训练自己的姿态数据集从标注格式到训练命令再到显存占用换算4.1 关键点标注格式就是你熟悉的 COCO-keypoints jsonyolov7-pose 的训练数据格式沿用 COCO keypoints 的 json 结构核心字段包括annotations里的keypoints和num_keypoints。每个标注对象的关键点数据是长度为 17*3 的数组按顺序排列 17 个点的 x、y 和可见性标志0 表示不在图像范围内1 表示遮挡但位置存在2 表示可见。有一个细节容易被忽略info字段中的data_created和images字段中的license即使为空也要保留因为官方数据加载脚本在解析时会直接索引这些 key。此外类别 id 必须从 1 开始且固定为 personyolov7-pose 的配置中不带多类别姿态分类。4.2 data 和 hyp 文件的参数调整清单官方仓库提供data/coco_kpt.yaml需要修改的是train、val路径和关键点的元信息。以下是通常需要调整的参数参数默认值调整建议kpt_shape[17, 3]改成自定义数据集的点数第 2 维固定为 3flip_idx17 维数组水平翻转时关键点的对应索引必须正确配对否则 loss 会在翻转增强时失控hflip0.5需要关键点语义对应关系不适合直接关闭lr0.001微调用 0.0005冻结 backbone 时还可以更低kpt_labelTrue训练时必须保持打开否则 keypoint 分支不计算损失flip_idx是姿态训练里最容易被忽略的地方。如果标注的是左眼为索引 1、右眼为索引 2开启 hflip 增强之后如果没有交换索引模型看到的左眼坐标实际上是原图右眼位置损失函数直接爆炸。自定义数据集时要在 yaml 里仔细填写这个数组。4.3 训练命令与参数说明官方仓库提供train_aux.py可以从预训练模型继续训练python train_aux.py \ --data data/coco_kpt.yaml \ --weights yolov7-w6-pose.pt \ --img 960 640 \ --batch-size 16 \ --epochs 300 \ --device 0,1 \ --cfg cfg/yolov7-w6-pose.yaml \ --workers 4 \ --name kpt_custom参数含义如下--img是训练和验证时的输入尺寸多尺度训练时不断随机切换--device指定训练显卡多卡训练会自动按显存分配 batch--name控制输出目录权重会保存到runs/train/kpt_custom下。中间缩率图保存在同一个目录中可以直接打开对比每个 epoch 的走势。显存方面以 16GB 显存为例输入 960、batch size 8 大约占 14GB 显存加上额外开销刚好卡在边缘。如果报 out of memory优先把--img改成 640不要强行降低 batch size 到 4 以下不然 BN 层的统计量波动太大训练不稳定。4.4 训练过程中怎么判断是否过拟合姿态估计任务的 loss 由检测 loss 和关键点 loss 两部分组成更应该关注验证集上每个关键点的平均检测率也就是 OKS 指标。只盯着 total loss 下降会掩盖一个问题检测部分收敛了但关键点部分还存在系统性偏移比如所有点整体偏向左下方。判断是否过拟合的实用做法是每隔固定 epoch 用验证集跑一次推理把预测结果可视化保存下来。建议保存并对比验证集中三个类别的图像正常直立的正面、侧面转身、以及多人场景。当训练 loss 进一步下降而验证集这些图像的骨骼点开始跳变时就需要减少 epoch 或提高关键点损失的权重。5. 部署和验证ONNX 导出加 OKS 评估一个脚本解决两个痛点5.1 导出 ONNX 时注意动态尺寸和 NMS 后处理训练完成后将模型导出为 ONNX 是工程化中常规做法。导出时可以带上 keypoint 相关配置以下是一个可用的示例代码片段import torch from models.experimental import attempt_load model attempt_load(best.pt, map_locationcpu) model.eval() img_size [960, 960] fake_input torch.zeros(1, 3, img_size[0], img_size[1]) torch.onnx.export( model, fake_input, yolo_pose.onnx, opset_version11, input_names[images], output_names[output], dynamic_axes{images: {0: batch}} )ONNX 文件不包含 NMS 运算。因此通常需要自己实现一个朴素的 NMS将输出 reshape 成 [batch, 18900, 56]提取前 6 维做普通目标检测 NMS再根据保留的索引取出对应关键点。在纯 numpy 实现下960x960 输入的关键点 decode 大约耗时 8ms不会成为推理瓶颈。5.2 用 OKS 代替简单像素距离判断关键点准确率很多人验证关键点会用欧氏距离但不同目标大小差异很大直接用像素阈值评估对全身比例大的目标有利。正确的做法是计算 OKSimport numpy as np def compute_oks(gt_keypoints, pred_keypoints, bbox_area, sigmas): dist np.linalg.norm(gt_keypoints - pred_keypoints, axis1) oks_per_point np.exp(-(dist ** 2) / (2 * (bbox_area * sigmas ** 2))) return oks_per_point.mean()bbox_area对应目标框面积除以 10000sigmas是 COCO 官方提供的 17 个关键点标准差常量。计算输出时要注意ok 的点才参与统计遮挡点不能直接剔除否则指标会虚高。最终报告应该在每个分辨范围内分开统计。5.3 最后 100 行给骨骼按顺序上色观察误差方向做完整体 OKS 后还要关注每个关键点的个体表现。常见的做法是把 17 个点分成头部、躯干、左右臂、左右腿五组分别计算平均 OKS按得分从低到高找出最容易出错的部位。与此同时在推理结果可视化时给不同肢体涂上不同颜色能直观看出失误集中在哪些部位——比如左脚踝经常外漂就可以检查自己的标注里左脚踝坐标是否存在系统性的偏差。这里有一个实用技巧把关键点的预测坐标减去真实标注坐标得到每个点的偏移向量然后绘制成箭头图。箭头的方向表示偏移方向长度表示偏移力度。这种方法比只看指标更容易定位问题。一般连续看 50 张验证图的偏移方向就能确认是标注系统性问题还是网络本身拟合不足。此时可以针对这些点增加训练数据因为问题通常不在模型结构上。本文还有配套的精品资源点击获取