MMPose实战:5分钟搞定HRNet模型训练(附完整代码)

📅 发布时间:2026/7/3 23:12:00 👁️ 浏览次数:
MMPose实战:5分钟搞定HRNet模型训练(附完整代码)
MMPose实战从零到一手把手教你用HRNet搞定人体姿态估计最近在做一个智能健身应用需要实时检测用户的动作姿态。试了几个开源方案要么精度不够要么速度太慢直到发现了OpenMMLab的MMPose。这个工具箱里集成了HRNet这个“神器”——在保持高分辨率特征的同时进行多尺度融合在COCO关键点检测榜单上长期霸榜。但说实话第一次接触MMPose时看着一堆配置文件、数据流水线、编解码器确实有点懵。网上很多教程都在讲理论架构真正能跑起来的完整代码示例却不多。今天我就把自己踩过的坑、调试的经验整理出来用最直白的方式带你在5分钟内完成HRNet模型的环境配置、数据准备和训练启动。我会提供可以直接复制粘贴的代码片段并解释每个步骤为什么要这么做。无论你是刚接触姿态估计的新手还是想快速验证HRNet效果的开发者这篇实战指南都能帮你省下大量摸索时间。1. 环境搭建一步到位的配置方案很多人卡在第一步环境冲突。MMPose依赖特定版本的PyTorch、CUDA和MMCV版本不匹配就会报各种奇怪错误。我推荐使用Conda创建独立环境这是最稳妥的方式。首先确保你的机器有NVIDIA显卡并安装了对应版本的CUDA驱动。可以通过nvidia-smi命令查看CUDA版本。我这里以CUDA 11.3为例# 创建并激活conda环境 conda create -n mmpose python3.8 -y conda activate mmpose # 安装PyTorch请根据你的CUDA版本调整 pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://download.pytorch.org/whl/cu113 # 安装MMCVMMPose的核心依赖 pip install openmim mim install mmcv-full1.7.1注意MMCV-full必须与PyTorch、CUDA版本严格匹配。如果安装失败可以去MMCV官方GitHub仓库查看版本兼容性表格。接下来安装MMPose本体。我建议直接从GitHub克隆最新代码这样能获得最新的修复和功能git clone https://github.com/open-mmlab/mmpose.git cd mmpose pip install -v -e . # -v 显示详细安装信息-e 以可编辑模式安装方便修改源码验证安装是否成功import mmpose print(mmpose.__version__) # 应该输出类似 1.0.0 的版本号如果到这里都没报错恭喜你最难的环境配置已经完成了。我遇到过最常见的问题是MMCV版本不对症状是导入时提示“undefined symbol”之类的错误。这时候需要彻底卸载重装pip uninstall mmcv mmcv-full -y mim install mmcv-full1.7.1环境配置完成后我们来看看项目目录结构。理解这个结构对后续的配置修改很有帮助mmpose/ ├── configs/ # 所有模型的配置文件 │ ├── _base_/ # 基础配置数据集、训练策略等 │ ├── body_2d_keypoint/ │ │ └── top_down/ │ │ └── heatmap/ │ │ └── coco/ # COCO数据集上的HRNet配置 │ └── ... ├── mmpose/ # 源代码 ├── tools/ # 训练、测试、推理脚本 ├── data/ # 数据集存放位置需要自己创建 └── requirements/ # 依赖文件2. 数据准备COCO格式的标准化处理MMPose支持多种数据集格式但最通用、文档最全的是COCO格式。即使你用的是自定义数据集我也强烈建议先转换成COCO格式能避免很多麻烦。COCO格式的标注文件是一个JSON结构如下{ images: [ { id: 1, file_name: 000000001.jpg, height: 480, width: 640 } ], annotations: [ { id: 1, image_id: 1, category_id: 1, keypoints: [x1, y1, v1, x2, y2, v2, ...], # 17个关键点每个点[x, y, visibility] num_keypoints: 17, bbox: [x, y, width, height], # 人体检测框 area: 36864, iscrowd: 0 } ], categories: [ { id: 1, name: person, keypoints: [nose, left_eye, right_eye, ...], # 17个关键点名称 skeleton: [[16, 14], [14, 12], ...] # 骨骼连接关系 } ] }关键点坐标中的visibility标志很重要v0未标注通常因为遮挡严重v1标注了但不可见在图像外或被遮挡但位置可推断v2标注且可见对于公开数据集MMPose已经提供了现成的支持。以COCO2017为例下载后按如下结构放置data/ └── coco/ ├── annotations/ │ ├── person_keypoints_train2017.json │ └── person_keypoints_val2017.json ├── train2017/ │ ├── 000000000009.jpg │ └── ... └── val2017/ ├── 000000000139.jpg └── ...如果你用的是自定义数据集需要自己编写数据集类。但更简单的方法是使用MMPose提供的转换脚本。假设你有关键点标注是简单的(x, y)坐标列表可以这样转换import json import os def convert_to_coco_format(image_dir, annotation_file, output_json): 将自定义格式转换为COCO格式 coco_format { images: [], annotations: [], categories: [{ id: 1, name: person, keypoints: [nose, left_eye, right_eye, left_ear, right_ear, left_shoulder, right_shoulder, left_elbow, right_elbow, left_wrist, right_wrist, left_hip, right_hip, left_knee, right_knee, left_ankle, right_ankle], skeleton: [ [16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12], [7, 13], [6, 7], [6, 8], [7, 9], [8, 10], [9, 11], [2, 3], [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7] ] }] } # 读取你的标注文件 with open(annotation_file, r) as f: custom_anns json.load(f) image_id 1 ann_id 1 for img_info in custom_anns[images]: # 添加图像信息 coco_format[images].append({ id: image_id, file_name: img_info[filename], height: img_info[height], width: img_info[width] }) # 添加标注信息 for person in img_info[persons]: keypoints [] for kp in person[keypoints]: keypoints.extend([kp[x], kp[y], 2]) # 默认可见 coco_format[annotations].append({ id: ann_id, image_id: image_id, category_id: 1, keypoints: keypoints, num_keypoints: len(person[keypoints]), bbox: person[bbox], # [x, y, width, height] area: person[bbox][2] * person[bbox][3], iscrowd: 0 }) ann_id 1 image_id 1 # 保存COCO格式标注 with open(output_json, w) as f: json.dump(coco_format, f, indent2) # 使用示例 convert_to_coco_format( image_dirdata/custom/train, annotation_filedata/custom/train_annotations.json, output_jsondata/custom/train_coco_format.json )数据准备好后还需要配置数据集元信息。MMPose在configs/_base_/datasets/coco.py中已经提供了COCO的元信息配置包括关键点名称、颜色、对称关系等。如果你的关键点定义不同需要修改这个文件。3. 模型配置理解HRNet的核心参数HRNet之所以强大在于它始终保持高分辨率特征图的同时并行处理多尺度特征。这与传统的先下采样再上采样的“沙漏”结构有本质区别。我们先看看一个标准的HRNet配置# configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py model dict( typeTopdownPoseEstimator, # 自上而下的姿态估计器 data_preprocessordict( typePoseDataPreprocessor, mean[123.675, 116.28, 103.53], # ImageNet均值 std[58.395, 57.12, 57.375], # ImageNet标准差 bgr_to_rgbTrue), # OpenCV默认BGR转为RGB backbonedict( typeHRNet, # 使用HRNet骨干网络 in_channels3, extradict( stage1dict( num_modules1, num_branches1, blockBOTTLENECK, num_blocks(4,), num_channels(64,)), stage2dict( num_modules1, num_branches2, blockBASIC, num_blocks(4, 4), num_channels(48, 96)), stage3dict( num_modules4, num_branches3, blockBASIC, num_blocks(4, 4, 4), num_channels(48, 96, 192)), stage4dict( num_modules3, num_branches4, blockBASIC, num_blocks(4, 4, 4, 4), num_channels(48, 96, 192, 384))), init_cfgdict( typePretrained, checkpointhttps://download.openmmlab.com/mmpose/pretrain_models/hrnet_w48-8ef0771d.pth)), headdict( typeHeatmapHead, in_channels48, # 与stage4第一个分支的通道数一致 out_channels17, # COCO的17个关键点 deconv_out_channelsNone, lossdict(typeKeypointMSELoss, use_target_weightTrue), decoderdict( typeHeatmapDecoder, # 热图解码器配置 )), train_cfgdict(), test_cfgdict( flip_testTrue, # 测试时增强水平翻转 shift_heatmapTrue)) # 偏移热图以获得更准确的位置这里有几个关键点需要理解1. HRNet的stage结构HRNet有4个stage每个stage包含多个并行分支分辨率逐渐降低但通道数增加Stage分支数分辨率比例通道数配置模块重复次数111/4[64]1221/4, 1/8[48, 96]1331/4, 1/8, 1/16[48, 96, 192]4441/4, 1/8, 1/16, 1/32[48, 96, 192, 384]3这种设计使得网络在深层仍然保持高分辨率特征对于需要精确定位的姿态估计任务特别有利。2. 预训练权重的重要性HRNet在ImageNet上预训练的权重能显著提升收敛速度和最终精度。MMPose提供了多个版本的预训练模型# 不同宽度配置的HRNet预训练权重 PRETRAINED_URLS { hrnet_w18: https://download.openmmlab.com/mmpose/pretrain_models/hrnet_w18-00eb2006.pth, hrnet_w32: https://download.openmmlab.com/mmpose/pretrain_models/hrnet_w32-36af842e.pth, hrnet_w48: https://download.openmmlab.com/mmpose/pretrain_models/hrnet_w48-8ef0771d.pth, }3. 输入尺寸的选择输入图像尺寸直接影响模型性能和速度。常见的配置有输入尺寸参数量GFLOPsCOCO AP适用场景256×19263.6M7.274.2移动端/实时384×28863.6M16.175.8平衡精度与速度512×38463.6M28.576.3追求最高精度在实际项目中我通常从256×192开始如果精度不够再尝试更大的尺寸。修改输入尺寸很简单只需在配置文件中调整# 数据流水线中的输入尺寸 codec dict( typeMSRAHeatmap, input_size(256, 192), # 改为 (384, 288) 或 (512, 384) heatmap_size(64, 48), sigma2) # 数据增强中的仿射变换 train_pipeline [ dict(typeLoadImage), dict(typeGetBBoxCenterScale), dict(typeRandomFlip, directionhorizontal), dict(typeRandomBBoxTransform), dict(typeTopdownAffine, input_size(256, 192)), # 同步修改这里 dict(typeGenerateTarget, encodercodec), dict(typePackPoseInputs) ]4. 训练策略从零训练与微调技巧有了数据和模型配置接下来就是训练。MMPose提供了完整的训练脚本但理解背后的训练策略能帮你更好地调参。基础训练命令# 单GPU训练 python tools/train.py configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py # 多GPU训练4卡 bash tools/dist_train.sh configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py 4 # 指定工作目录和GPU CUDA_VISIBLE_DEVICES0,1,2,3 python -m torch.distributed.launch \ --nproc_per_node4 \ --master_port29500 \ tools/train.py \ configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py \ --work-dir work_dirs/hrnet_w48_coco_256x192 \ --cfg-options randomness.seed42学习率策略是训练的关键。MMPose默认使用带热身的余弦退火学习率# 在配置文件的schedule部分 param_scheduler [ dict( typeLinearLR, # 线性热身 start_factor0.001, by_epochFalse, begin0, end500), # 热身500个iteration dict( typeCosineAnnealingLR, # 余弦退火 eta_min1e-6, # 最小学习率 begin500, end210000, # 总训练iteration by_epochFalse) ] # 优化器配置 optim_wrapper dict( optimizerdict( typeAdamW, lr5e-4, # 基础学习率 weight_decay0.01), clip_graddict(max_norm1.0, norm_type2)) # 梯度裁剪防止爆炸对于小数据集我推荐使用渐进式解冻策略先训练最后一层然后逐渐解冻更多层# 自定义训练策略 def freeze_backbone_layers(model, num_stages_to_freeze2): 冻结前几个stage的权重 backbone model.backbone # HRNet的stage1-4可以通过named_parameters访问 for name, param in backbone.named_parameters(): if any([fstage{i} in name for i in range(1, num_stages_to_freeze1)]): param.requires_grad False return model # 在训练循环中逐步解冻 for epoch in range(total_epochs): if epoch 10: # 第10轮解冻stage2 for name, param in model.backbone.named_parameters(): if stage2 in name: param.requires_grad True if epoch 20: # 第20轮解冻stage3 for name, param in model.backbone.named_parameters(): if stage3 in name: param.requires_grad True数据增强对姿态估计至关重要。除了常见的随机翻转、旋转、缩放MMPose还提供了一些针对姿态估计的特殊增强train_pipeline [ dict(typeLoadImage, backendcv2), dict(typeGetBBoxCenterScale), # 计算边界框中心和尺度 dict(typeRandomFlip, directionhorizontal, flip_prob0.5), # 50%概率水平翻转 dict(typeRandomHalfBody, # 随机半身增强 min_total_keypoints8, # 最少需要8个可见关键点 prob0.3), # 30%概率应用 dict(typeRandomBBoxTransform, # 随机边界框变换 shift_factor0.16, # 平移范围 scale_factor0.25), # 缩放范围 dict(typeTopdownAffine, input_size(256, 192)), dict(typeHeatmapGenerator, # 生成高斯热图 sigma2, # 高斯核标准差 unbiasedTrue), # 使用无偏编码 dict(typePackPoseInputs) ]提示RandomHalfBody增强特别有用它能模拟现实中人体被部分遮挡的情况提升模型鲁棒性。5. 调试与优化解决常见训练问题训练过程中可能会遇到各种问题这里分享几个我实际遇到的坑和解决方案。问题1Loss不下降或NaN这是最常见的问题。首先检查数据标注# 可视化检查数据增强效果 from mmpose.apis import inference_topdown from mmpose.utils import register_all_modules # 注册所有模块 register_all_modules() # 创建模型 from mmengine.config import Config from mmpose.registry import MODELS cfg Config.fromfile(configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py) model MODELS.build(cfg.model) # 可视化训练样本 import matplotlib.pyplot as plt from mmpose.datasets import build_dataset dataset build_dataset(cfg.train_dataloader.dataset) sample dataset[0] # 获取第一个样本 fig, axes plt.subplots(1, 2, figsize(12, 6)) # 显示原始图像和关键点 axes[0].imshow(sample[inputs].permute(1, 2, 0).numpy()) # 显示生成的热图 heatmaps sample[data_samples].gt_fields.heatmaps axes[1].imshow(heatmaps[0].sum(dim0).numpy(), cmaphot) plt.show()如果热图看起来不正常比如所有点都在边缘或中心可能是数据预处理有问题。检查GetBBoxCenterScale计算的边界框是否合理。问题2过拟合在小数据集上训练HRNet很容易过拟合。除了常用的Dropout、权重衰减还可以尝试更强的数据增强train_pipeline [ # ... 其他变换 dict(typePhotometricDistortion, # 光度失真 brightness_range(0.8, 1.2), contrast_range(0.8, 1.2), saturation_range(0.8, 1.2), hue_delta18), dict(typeRandomOcclusion, # 随机遮挡 occlusion_prob0.1, occlusion_size(0.1, 0.3)), # 遮挡面积比例 ]标签平滑在HeatmapHead的损失函数中加入标签平滑headdict( typeHeatmapHead, lossdict( typeKeypointMSELoss, use_target_weightTrue, label_smooth0.1), # 标签平滑系数 )早停策略监控验证集精度当连续N个epoch没有提升时停止训练问题3训练速度慢HRNet的计算量确实不小但可以通过以下方式优化混合精度训练# 在训练命令中添加--amp参数 python tools/train.py configs/... --amp梯度累积在小批量大小下模拟大批量训练# 在配置文件中添加 train_cfg dict( max_epochs210, val_interval10, gradient_accumulation_steps4) # 每4步更新一次权重使用更小的HRNet变体如果实时性要求高可以考虑Lite-HRNet6. 模型评估与指标解读训练完成后需要评估模型性能。MMPose使用COCO评估协议主要指标是APAverage Precision# 在验证集上评估 python tools/test.py \ configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py \ work_dirs/hrnet_w48_coco_256x192/latest.pth评估结果会显示多个指标Average Precision (AP) [ IoU0.50:0.95 | area all | maxDets 20 ] 0.742 Average Precision (AP) [ IoU0.50 | area all | maxDets 20 ] 0.901 Average Precision (AP) [ IoU0.75 | area all | maxDets 20 ] 0.812 Average Precision (AP) [ IoU0.50:0.95 | areamedium | maxDets 20 ] 0.714 Average Precision (AP) [ IoU0.50:0.95 | area large | maxDets 20 ] 0.798 Average Recall (AR) [ IoU0.50:0.95 | area all | maxDets 20 ] 0.798这些指标的含义指标含义关注点AP[0.50:0.95]IoU阈值从0.5到0.95的平均精度整体性能AP0.50IoU阈值为0.5的精度宽松评估AP0.75IoU阈值为0.75的精度严格评估AP-medium中等尺度人体的精度常见场景AP-large大尺度人体的精度近景拍摄AR平均召回率检测完整性对于实际应用我通常更关注AP-medium因为它反映了模型在常见场景人体占图像面积32×32到96×96像素下的表现。如果这个值偏低可能需要增加更多中等尺度人体的训练数据。还可以可视化预测结果直观检查模型问题from mmpose.apis import inference_topdown, init_model from mmpose.utils import visualize # 初始化模型 model init_model( configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py, work_dirs/hrnet_w48_coco_256x192/latest.pth, devicecuda:0) # 单张图像推理 result inference_topdown(model, test_image.jpg) # 可视化 vis_image visualize( imgtest_image.jpg, resultresult, showFalse, draw_bboxTrue, kpt_thr0.3) # 关键点置信度阈值 # 保存结果 import cv2 cv2.imwrite(result.jpg, vis_image)7. 部署与优化让模型真正跑起来训练好的模型需要部署到实际应用中。MMPose支持多种部署方式1. ONNX导出推荐用于跨平台部署from mmdeploy.apis import torch2onnx # 导出为ONNX格式 torch2onnx( imgdemo.jpg, work_dirwork_dirs/onnx, deploy_cfgconfigs/mmpose/pose-detection_onnxruntime_dynamic.py, model_cfgconfigs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py, model_checkpointwork_dirs/hrnet_w48_coco_256x192/latest.pth, devicecuda)2. TensorRT加速追求极致速度# 首先导出ONNX然后转换为TensorRT python tools/deploy.py \ configs/mmpose/pose-detection_tensorrt_dynamic-256x192.py \ configs/body_2d_keypoint/top_down/heatmap/coco/hrnet_w48_coco_256x192.py \ work_dirs/hrnet_w48_coco_256x192/latest.pth \ demo.jpg \ --work-dir work_dirs/tensorrt \ --device cuda:0 \ --show3. 模型量化移动端部署# 动态量化最简单 import torch.quantization model.eval() model.qconfig torch.quantization.get_default_qconfig(fbgemm) quantized_model torch.quantization.prepare(model, inplaceFalse) quantized_model torch.quantization.convert(quantized_model) # 保存量化模型 torch.save(quantized_model.state_dict(), quantized_model.pth)4. 推理优化技巧 在实际部署中还可以通过以下方式进一步提升速度# 批量推理 def batch_inference(model, image_batch): 批量处理多张图像 with torch.no_grad(): # 使用AMP自动混合精度 with torch.cuda.amp.autocast(): results model(image_batch, modepredict) return results # 缓存预处理结果 from functools import lru_cache lru_cache(maxsize100) def preprocess_image(image_path, input_size(256, 192)): 缓存预处理结果避免重复计算 img cv2.imread(image_path) # ... 预处理代码 return processed_img # 使用更小的模型变体 # HRNet-W18比W48快3倍精度下降约2个点 # 在config中修改 backbonedict( typeHRNet, extradict( stage1dict(num_channels(32,)), # 减少通道数 stage2dict(num_channels(32, 64)), stage3dict(num_channels(32, 64, 128)), stage4dict(num_channels(32, 64, 128, 256))))最后分享一个实际项目中的经验在健身动作识别中我发现HRNet对某些特定角度如背面的关键点检测效果较差。通过针对性数据增强——专门收集背面角度的图片并添加到训练集——AP提升了3.2%。所以不要完全依赖公开数据集根据你的应用场景收集特定数据往往能带来最大提升。模型部署后记得建立监控机制定期检查在实际场景中的表现。可以记录失败案例分析是光照、遮挡还是角度问题然后针对性优化。姿态估计不是一劳永逸的任务持续迭代才能保持最佳效果。