MMPose 中的 CPM 卷积姿态机:COCO 自上而下热图模型配置、训练与实现原理全解析
2026/9/16 13:05:46 网站建设 项目流程

MMPose 中的 CPM 卷积姿态机:COCO 自上而下热图模型配置、训练与实现原理全解析

【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose

导读

本文围绕 MMPose 仓库中 CPM(Convolutional Pose Machines,卷积姿态机)在 COCO 人体关键点数据集上的自上而下(Top-Down)热图配置展开,完整介绍configs/body_2d_keypoint/topdown_heatmap/coco/cpm_coco.md收录的两套官方训练配置、其 COCO val2017 上的评估结果,以及CPM骨干网络与CPMHead多阶段热图头的源码级实现原理。读完本文,你将掌握如何在 MMPose 中复现 CPM 的 COCO 训练、读懂其每层网络结构、理解多阶段中间监督与 MSRA 热图编解码机制,并学会独立修改配置以获得不同输入分辨率下的行为。

一、CPM 配置文档速览:官方成果表与引用信息

1.1 算法与数据集出处

configs/body_2d_keypoint/topdown_heatmap/coco/cpm_coco.md在头部以<details>折叠块的形式引用了两个文献:

  • 算法:CPM 论文 "Convolutional Pose Machines",Wei, Shih-En 等人,发表于 CVPR 2016;
  • 数据集:COCO "Microsoft COCO: Common Objects in Context",Lin 等人,发表于 ECCV 2014。

两个条目都附带了可直接复制的 BibTeX 引用,方便你在论文中正确引用该模型与数据来源。该 md 文件同时是 模型索引 的元数据来源,后者登记了两套配置的 Architecture、Training Data、Task、各项指标与权重下载地址。

1.2 COCO val2017 官方评测结果

文档记录了在检测器人体 AP 为 56.4的前提下(即使用COCO_val2017_detections_AP_H_56_person.json提供的检测框),CPM 在 COCO val2017 上的评估指标,两套配置分别对应 256×192 与 384×288 两种输入尺寸:

ArchInput SizeAPAP^50AP^75ARAR^50ckptlog
cpm256x1920.6270.8620.7090.6890.906ckptlog
cpm384x2880.6520.8650.7300.7100.907ckptlog

对比可见,输入分辨率从 256×192 提升到 384×288 后,AP 由 0.627 提升至 0.652(+2.5 AP),但训练批次从 8×64 调整为 8×32,以换取更大的显存开销。这也是选择配置时最核心的权衡点:分辨率越高精度越好,但显存与训练成本也越高

二、配置文件逐段解析:td-hm_cpm_8xb64-210e_coco-256x192.py

CPM 在 COCO 上的训练配置位于configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_cpm_8xb64-210e_coco-256x192.py(另一套 384×288 配置为configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_cpm_8xb32-210e_coco-384x288.py,两者除分辨率、批大小与sigma外完全一致)。下面以 256×192 版本为线索逐段拆解。

2.1 基础运行时与训练总览

_base_ = ['../../../_base_/default_runtime.py'] # runtime train_cfg = dict(max_epochs=210, val_interval=10)
  • 继承自 configs/base/default_runtime.py,其中包含了默认钩子(LoggerHook、CheckpointHook 每 10 个 epoch 保存一次、PoseVisualizationHook 默认关闭、BadCaseAnalysisHook 默认关闭)、SyncBuffersHook、本地可视化后端LocalVisBackendenv_cfgmp_start_method='fork')等公共运行时配置。
  • 训练总时长210 个 epoch每 10 个 epoch 做一次验证

2.2 优化器与学习率调度

# optimizer optim_wrapper = dict(optimizer=dict( type='Adam', lr=5e-4, )) # learning policy param_scheduler = [ dict( type='LinearLR', begin=0, end=500, start_factor=0.001, by_epoch=False), # warm-up dict( type='MultiStepLR', begin=0, end=210, milestones=[170, 200], gamma=0.1, by_epoch=True) ] # automatically scaling LR based on the actual training batch size auto_scale_lr = dict(base_batch_size=512)
  • 使用Adam 优化器,初始学习率5e-4
  • 学习率策略分为两段:前500 个 iteration使用LinearLR做 warm-up,起始学习率为 0.001 倍;之后切换为MultiStepLR,在第 170 与 200 个 epoch处各衰减 0.1 倍。
  • auto_scale_lr = dict(base_batch_size=512)表示基准批大小为 512,实际训练时若批大小与 512 不同,MMEngine 会自动按比例缩放学习率,便于在单卡/多卡环境下复现官方指标。

2.3 关键点编解码器:MSRAHeatmap

# codec settings codec = dict( type='MSRAHeatmap', input_size=(192, 256), heatmap_size=(24, 32), sigma=2)

CPM 配置采用MSRA 高斯热图编解码器,其实现位于 mmpose/codecs/msra_heatmap.py:

  • input_size=(192, 256):模型输入图像尺寸(宽 192,高 256);
  • heatmap_size=(24, 32):输出热图尺寸,为输入的 1/8;
  • sigma=2:高斯核标准差。

在编码阶段(encode),MSRAHeatmap将关键点坐标除以scale_factor(输入尺寸与热图尺寸之比)后,通过generate_gaussian_heatmaps生成 (K, H, W) 的高斯热图与 (N, K) 的 keypoint_weights(源码);在解码阶段(decode),则对热图取最大值位置,再经refine_keypoints(一阶梯度精修)得到亚像素坐标,最后乘以scale_factor还原到输入图像空间(源码)。

细节:当unbiased=True时该编解码器会走 DarkPose 的generate_unbiased_gaussian_heatmapsrefine_keypoints_dark分支;CPM 的官方配置未开启该选项,保持unbiased=False的经典 MSRA 方式。

2.4 模型组装:TopdownPoseEstimator + CPM + CPMHead

# model settings model = dict( type='TopdownPoseEstimator', data_preprocessor=dict( type='PoseDataPreprocessor', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], bgr_to_rgb=True), backbone=dict( type='CPM', in_channels=3, out_channels=17, feat_channels=128, num_stages=6), head=dict( type='CPMHead', in_channels=17, out_channels=17, num_stages=6, deconv_out_channels=None, final_layer=None, loss=dict(type='KeypointMSELoss', use_target_weight=True), decoder=codec), test_cfg=dict( flip_test=True, flip_mode='heatmap', shift_heatmap=True, ))

关键字段说明:

  • TopdownPoseEstimator:自上而下姿态估计器,先由检测器给出人体框,再对每个框内的人体做关键点回归;
  • PoseDataPreprocessor:ImageNet 均值和标准差归一化,bgr_to_rgb=True表示输入图像按 BGR 读取、送入网络前转换为 RGB;
  • backboneCPM,输入 3 通道、输出 17 通道(COCO 17 个关键点)、特征通道 128、共6 个 stage
  • headCPMHeadin_channels=17out_channels=17num_stages=6deconv_out_channels=Nonefinal_layer=None表示不额外添加反卷积上采样层与最后的 1×1 卷积层(因为 CPM 骨干自身已直接输出热图),每个 stage 均使用KeypointMSELoss(use_target_weight=True)计算损失,decoder直接复用上文的codec
  • test_cfg:开启flip test(水平翻转测试增强)flip_mode='heatmap'表示对热图做翻转融合,shift_heatmap=True表示翻转后按关键点对称索引进行偏移校正(实现见 CPMHead.predict)。

2.5 数据流水线(pipeline)

# pipelines train_pipeline = [ dict(type='LoadImage'), dict(type='GetBBoxCenterScale'), dict(type='RandomFlip', direction='horizontal'), dict(type='RandomHalfBody'), dict(type='RandomBBoxTransform'), dict(type='TopdownAffine', input_size=codec['input_size']), dict(type='GenerateTarget', encoder=codec), dict(type='PackPoseInputs') ] val_pipeline = [ dict(type='LoadImage'), dict(type='GetBBoxCenterScale'), dict(type='TopdownAffine', input_size=codec['input_size']), dict(type='PackPoseInputs') ]
  • 训练流水线在LoadImageGetBBoxCenterScale之后依次应用:水平RandomFlipRandomHalfBody(有概率只保留一半身体的关键点,增强局部遮挡鲁棒性)、RandomBBoxTransform(对检测框做随机缩放/平移/旋转)、TopdownAffine(按输入尺寸做仿射变换)、GenerateTarget(用codec生成热图标签)、PackPoseInputs(打包为模型输入)。
  • 验证流水线不含任何随机增强,且GenerateTargetPackPoseInputs取代——标签只在训练时需要。

2.6 数据加载器与评测器

train_dataloader = dict( batch_size=64, num_workers=2, persistent_workers=True, sampler=dict(type='DefaultSampler', shuffle=True), dataset=dict( type=dataset_type, data_root=data_root, data_mode=data_mode, ann_file='annotations/person_keypoints_train2017.json', data_prefix=dict(img='train2017/'), pipeline=train_pipeline, )) val_dataloader = dict( batch_size=32, num_workers=2, persistent_workers=True, drop_last=False, sampler=dict(type='DefaultSampler', shuffle=False, round_up=False), dataset=dict( type=dataset_type, data_root=data_root, data_mode=data_mode, ann_file='annotations/person_keypoints_val2017.json', bbox_file='data/coco/person_detection_results/' 'COCO_val2017_detections_AP_H_56_person.json', data_prefix=dict(img='val2017/'), test_mode=True, pipeline=val_pipeline, )) test_dataloader = val_dataloader val_evaluator = dict( type='CocoMetric', ann_file=data_root + 'annotations/person_keypoints_val2017.json') test_evaluator = val_evaluator
  • 数据根目录为data/coco/,使用CocoDatasetdata_mode='topdown'
  • 训练集批大小 64(8 卡即 8×64),验证集批大小 32;
  • 验证/测试使用COCO_val2017_detections_AP_H_56_person.json作为人体检测框来源——这正是文档中"检测器人体 AP 为 56.4"那句说明的出处;
  • 指标由CocoMetric计算,输出 COCO 的 AP/AR 系列指标;配置文件顶部还设置了default_hooks = dict(checkpoint=dict(save_best='coco/AP', rule='greater')),即以验证集 AP 为准保存最优权重

三、源码级原理:CPM 骨干网络的 6 阶段结构

CPM骨干网络实现在 mmpose/models/backbones/cpm.py,由三部分组成:

3.1 Stem(第一阶段网络)

self.stem = nn.Sequential( ConvModule(in_channels, 128, 9, padding=4, norm_cfg=norm_cfg), nn.MaxPool2d(kernel_size=3, stride=2, padding=1), ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg), nn.MaxPool2d(kernel_size=3, stride=2, padding=1), ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg), nn.MaxPool2d(kernel_size=3, stride=2, padding=1), ConvModule(128, 32, 5, padding=2, norm_cfg=norm_cfg), ConvModule(32, 512, 9, padding=4, norm_cfg=norm_cfg), ConvModule(512, 512, 1, padding=0, norm_cfg=norm_cfg), ConvModule(512, out_channels, 1, padding=0, act_cfg=None))
  • 以 9×9/5×5/1×1 卷积配合三次 3×3 stride=2 的 MaxPooling 下采样,最终在stem末尾直接输出第一个 stage 的 17 通道热图(act_cfg=None表示最后一层无激活);
  • 网络结构与论文一致:初始阶段即可给出一个粗糙的关键点预测。

3.2 Middle 分支(中继特征提取)

self.middle = nn.Sequential( ConvModule(in_channels, 128, 9, padding=4, norm_cfg=norm_cfg), nn.MaxPool2d(kernel_size=3, stride=2, padding=1), ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg), nn.MaxPool2d(kernel_size=3, stride=2, padding=1), ConvModule(128, 128, 9, padding=4, norm_cfg=norm_cfg), nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

middle分支从原始输入单独提取与stem同空间分辨率的特征(同样三次下采样),用于在后续 stage 中与前一 stage 的热图拼接,为网络补充原始图像信息。

3.3 多阶段循环(Stage 2 ~ 6)

self.cpm_stages = nn.ModuleList([ CpmBlock( middle_channels + out_channels, channels=[feat_channels, feat_channels, feat_channels], kernels=[11, 11, 11], norm_cfg=norm_cfg) for _ in range(num_stages - 1) ]) self.middle_conv = nn.ModuleList([ nn.Sequential(ConvModule(128, middle_channels, 5, padding=2, norm_cfg=norm_cfg)) for _ in range(num_stages - 1) ]) self.out_convs = nn.ModuleList([ nn.Sequential( ConvModule(feat_channels, feat_channels, 1, padding=0, norm_cfg=norm_cfg), ConvModule(feat_channels, out_channels, 1, act_cfg=None)) for _ in range(num_stages - 1) ])

forward中每个 stage 的执行逻辑为(源码):

inp_feat = torch.cat([out_feats[-1], self.middle_convind], 1) cpm_feat = single_stage(inp_feat) out_feat = out_conv(cpm_feat) out_feats.append(out_feat)

即:把上一个 stage 的输出热图与降维后的middle特征拼接(维度为middle_channels + out_channels),经CpmBlock(3 层 11×11 卷积,源码)与out_convs(1×1 卷积)输出新的 17 通道热图。每个 stage 的输入都显式包含上一阶段的热图,这正是"姿态机"(Pose Machine)通过结构化上下文逐步精修预测的核心思想,也是它对相邻关键点空间关系建模的基础。

最终forward返回一个包含6 张热图的列表(shape 均为 (1, 17, H/8, W/8)),每个 stage 一张,全部送入CPMHead参与损失计算。

四、源码级原理:CPMHead 的多阶段中间监督

CPMHead实现在 mmpose/models/heads/heatmap_heads/cpm_head.py,它接收骨干网络输出的多张 stage 热图并完成两件事:训练时的多阶段损失计算与测试时的关键点解码。

4.1 前向与测试时的翻转增强

forward断言特征数量等于num_stages,并对每个 stage 依次施加可选的反卷积层与最终卷积层(本配置两者均为None,退化为恒等映射,直接透传骨干输出)。测试阶段predict的核心逻辑为:

  • 开启flip_test时,将原图与水平翻转图的热图按flip_indices对称翻转后取平均(shift_heatmap=True校正偏移);
  • 只取最后一个 stage的热图multi_stage_heatmaps[-1]进行decode(早期 stage 只参与训练监督,不参与最终预测);
  • output_heatmaps=True,还会以PixelData形式返回热图。

4.2 多阶段损失叠加

loss方法对每个 stage 的热图分别与同一个 GT 热图计算KeypointMSELoss并累加(源码):

for i in range(self.num_stages): loss_i = loss_func(multi_stage_pred_heatmaps[i], gt_heatmaps, keypoint_weights) if 'loss_kpt' not in losses: losses['loss_kpt'] = loss_i else: losses['loss_kpt'] += loss_i
  • 这种多阶段中间监督(Intermediate Supervision)是 CPM 的训练关键:即使前几个 stage 预测不够准,其热图也会被强制对齐到 GT,避免深层网络梯度消失并加速收敛;
  • 每个 stage 可共享同一个损失模块(默认),也可通过传入长度等于num_stagesloss列表为不同 stage 配置不同损失(源码中显式校验了长度一致性);
  • 损失函数为 KeypointMSELoss,use_target_weight=True表示不同关键点按其可见性权重加权,遮挡/未标注关键点的梯度贡献被压低;
  • 损失之外,loss方法还会基于最后一个 stage 的热图与 GT 计算pose_pck_accuracy作为训练中的acc_pose指标。

4.3 关于 deconv 与 final_layer 的灵活设计

CPMHead的构造函数支持:

  • deconv_out_channelsNone时各 stage 使用nn.Identity()做反卷积上采样(本配置即如此,因为 CPM 骨干的输出分辨率已是 1/8 且空间信息足够);传元组(如(32, 32))时则为每个 stage 构建kernel=4/3/2、stride=2 的反卷积栈(源码支持 kernel 4→padding 1、kernel 3→output_padding 1、kernel 2→padding 0 三种映射);
  • final_layerNone时同样退化为恒等;为 dict(如dict(kernel_size=1))时构建 1×1 卷积映射到out_channels

单元测试 tests/test_models/test_heads/test_heatmap_heads/test_cpm_head.py 分别验证了 w/o deconv、w/ deconv、w/o final layer、w/ decoder 及多 stage 损失叠加等分支,可作为理解这些选项行为的最小示例。

五、COCO 数据准备与训练、测试实战

5.1 数据准备

训练需要按 COCO 官方目录结构放置数据:

data/coco/ ├── annotations/ │ ├── person_keypoints_train2017.json │ └── person_keypoints_val2017.json ├── train2017/ ├── val2017/ └── person_detection_results/ └── COCO_val2017_detections_AP_H_56_person.json
  • 关键点标注来自annotations/,图像来自train2017/val2017/
  • 验证/测试必需的人体检测框结果文件COCO_val2017_detections_AP_H_56_person.json(对应文档中"detector having human AP of 56.4")需要按 数据准备指南(中文见 docs/zh_cn/user_guides/prepare_datasets.md)预先准备。

5.2 训练

单机多卡训练使用 tools/dist_train.sh:

bash tools/dist_train.sh configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_cpm_8xb64-210e_coco-256x192.py 8

如需 384×288 配置,将配置路径替换为configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_cpm_8xb32-210e_coco-384x288.py。单卡可用 tools/train.py,以 Slurm 集群运行时则用 tools/slurm_train.sh。训练时auto_scale_lr会按实际批大小相对 512 自动缩放学习率,因此单卡(batch=64)训练时学习率会被相应调小。

5.3 测试与指标复现

bash tools/dist_test.sh configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_cpm_8xb64-210e_coco-256x192.py \ work_dirs/td-hm_cpm_8xb64-210e_coco-256x192/best_coco_AP_epoch_210.pth 8
  • 测试默认在 COCO val2017 上计算CocoMetric的 AP/AR 指标;
  • 由于配置了save_best='coco/AP',训练后最优权重会以best_coco_AP_epoch_*.pth形式保存在 work_dirs 下,用它进行测试即可复现文档表格中的指标。

5.4 推理与部署

下载文档表格中提供的 ckpt 后,可使用 demo/inferencer_demo.py 快速推理:

python demo/inferencer_demo.py tests/data/coco/000000000785.jpg \ td-hm_cpm_8xb64-210e_coco-256x192 \ --draw-heatmap --pred-out-dir vis_results

其中td-hm_cpm_8xb64-210e_coco-256x192是 模型索引 中登记的模型名,推理器会自动解析配置并加载对应权重;--draw-heatmap可同时可视化最后 stage 的热图。端到端的人体检测+关键点流程可参考 demo/docs/en/2d_human_pose_demo.md。

六、两种分辨率配置对比与调参建议

配置输入尺寸批大小sigmaAPAP^50AP^75
td-hm_cpm_8xb64-210e_coco-256x192.py256×1926420.6270.8620.709
td-hm_cpm_8xb32-210e_coco-384x288.py384×2883230.6520.8650.730

值得注意的是两套配置中sigma并非不变:256×192 对应sigma=2,384×288 对应sigma=3。这是因为高斯核的绝对尺寸应随热图分辨率(24×32 vs 36×48)等比放大,编码器在 mmpose/codecs/msra_heatmap.py 中按scale_factor = input_size / heatmap_size缩放坐标,而 sigma 需要手工按分辨率匹配。修改输入分辨率时,应遵循以下经验:

  1. input_sizeheatmap_size保持 8:1 的比例(如 256×192→32×24,384×288→48×36);
  2. 按分辨率增大适度调大sigma(官方 256×192→2、384×288→3 即为基准);
  3. 分辨率提升后显存需求显著上升,需按显存余量下调 batch size 或卡数;
  4. 保持flip_testshift_heatmapRandomHalfBody等数据与推理策略不变,可稳定获得与官方一致的精度收益。

七、从源码结构看 CPM 与同类算法的定位

从仓库结构看,CPM 属于 configs/body_2d_keypoint/topdown_heatmap/coco 目录下自上而下热图法家族的一员,与 Hourglass、HRNet、MSPN 等并列。它的特点可以概括为:

  • 多阶段串行精修:6 个 stage 逐级细化热图,每一级都显式拼接上一级预测,梯度由中间监督直达浅层(backbone);
  • 无需反卷积上采样:骨干末尾直接输出 1/8 分辨率热图,CPMHead在本配置中保持恒等映射,整体结构简洁、参数量少;
  • 轻量实用:以 256×192 输入即取得 COCO val2017 AP 0.627 的可用精度,适合作为基线模型验证数据增强、损失设计等改进手段(仓库内 hrnet_augmentation_coco、resnet_dark_coco 等即展示了同框架下其他算法的同类实验)。

如需在生产环境中追求更高精度,可横向对比仓库内 HRNet(hrnet_coco)、Lite-HRNet、ViTPose 等方案;如需在边缘设备部署,可参考 部署指南(中文见 docs/zh_cn/user_guides/how_to_deploy.md)对 CPM 这类轻量模型做转换加速。

结语

本文完整还原了 configs/body_2d_keypoint/topdown_heatmap/coco/cpm_coco.md 中 CPM 在 COCO 上的官方成果,并从配置、骨干网络、多阶段头、编解码器、训练测试全链路给出源码级解析。CPM 虽然诞生于 2016 年,但其多阶段姿态机思想在 MMPose 的现代工程化实现中依旧清晰可读,是理解自上而下热图法、中间监督训练与翻转测试增强的上手模型。基于上述配置与源码路径,你可以直接复现官方指标,并在此基础上进一步扩展实验。

【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询