MMPose 实时人脸关键点检测实战:RTMPose 在 COCO-WholeBody-Face、WFLW 与 LaPa 数据集上的配置、训练与原理解析
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
本文以 configs/face_2d_keypoint/rtmpose/README.md 为骨架,系统讲解 OpenMMLab MMPose 中 RTMPose 人脸 2D 关键点检测的模型库、配置文件逐段解析、训练/测试/推理全流程,以及 SimCC 编码、RTMCCHead 头部与 NME 评估的源码级原理。读完本文,你将能够独立复现 RTMPose-m 在三个经典人脸对齐基准上的结果,并掌握将 68/98/106 点标注统一迁移到实时人脸关键点模型上的完整方法论。
RTMPose 与实时人脸关键点检测
传统 2D 姿态估计在公开基准上表现优异,但在工业落地中长期受困于模型参数过大、推理延迟过高。RTMPose 正是为弥合这一差距而提出的高性能实时多人姿态估计框架——它基于 MMPose,从范式(paradigm)、骨干网络(backbone)、定位算法(localization algorithm)、训练策略(training strategy)与部署推理(deployment inference)五个维度系统研究了影响多人姿态估计算法性能的因素。
原文档给出的核心性能数据(来源 configs/face_2d_keypoint/rtmpose/README.md):
- RTMPose-m 在 COCO 上达到75.8% AP,在 Intel i7-11700 CPU 上90+ FPS,在 NVIDIA GTX 1660 Ti GPU 上430+ FPS;
- RTMPose-l 在 COCO-WholeBody 上达到67.0% AP,130+ FPS;
- 作者还在移动端部署场景下进一步验证了 RTMPose 在关键实时应用中的能力。
人脸的 2D 关键点检测(人脸对齐)本质上是一个单物体、高精度、强实时性要求的关键点回归任务,天然适合 RTMPose 的轻量高吞吐特性。在本仓库中,RTMPose 人脸模型共覆盖三个经典基准数据集,并在 face6 方案中实现了六大数据集联合训练。
模型库与基准结果
COCO-WholeBody-Face 数据集
COCO-WholeBody-Face 是 COCO-WholeBody(ECCV'2020)中人脸部分的关键点子集,评测指标为 NME(归一化平均误差),数值越小越好。在 val 集上的结果如下:
| 模型 | 输入尺寸 | NME | 详情与下载 |
|---|---|---|---|
| RTMPose-m | 256x256 | 0.0466 | rtmpose_coco_wholebody_face.md |
对应的完整结果(含 ckpt 与训练 log 下载地址)见 configs/face_2d_keypoint/rtmpose/coco_wholebody_face/rtmpose_coco_wholebody_face.md:
| Arch | Input Size | NME | ckpt | log |
|---|---|---|---|---|
| pose_rtmpose_m | 256x256 | 0.0466 | ckpt | log |
该模型输出68 个人脸关键点,在 COCO-WholeBody 训练集上训练 60 epoch,并以前缀pt-aic-coco的预训练权重初始化骨干。
WFLW 数据集
WFLW(CVPR'2018)是含 98 个关键点的野外人脸对齐基准,包含姿态、表情、光照、遮挡等 7 个子集,难度较高。模型在 WFLW train 上训练,评测结果如下:
| 模型 | 输入尺寸 | NME | 详情与下载 |
|---|---|---|---|
| RTMPose-m | 256x256 | 4.01 | rtmpose_wflw.md |
完整结果(含 ckpt 与 log)见 configs/face_2d_keypoint/rtmpose/wflw/rtmpose_wflw.md:
| Arch | Input Size | NME | ckpt | log |
|---|---|---|---|---|
| pose_rtmpose_m | 256x256 | 4.01 | ckpt | log |
LaPa 数据集
LaPa(AAAI'2020)是面向人脸解析(face parsing)标注的大规模人脸数据集,其关键点标注含 106 个点。RTMPose-m 在 LaPa val 集上的结果如下:
| 模型 | 输入尺寸 | NME | 详情与下载 |
|---|---|---|---|
| RTMPose-m | 256x256 | 1.29 | rtmpose_lapa.md |
完整结果见 configs/face_2d_keypoint/rtmpose/lapa/rtmpose_lapa.md:
| Arch | Input Size | NME | ckpt | log |
|---|---|---|---|---|
| pose_rtmpose_m | 256x256 | 1.29 | ckpt | log |
配置文件深度解析:以 COCO-WholeBody-Face 为例
以 rtmpose-m_8xb32-60e_coco-wholebody-face-256x256.py 为例,逐步拆解 RTMPose 人脸配置的每一层。WFLW 与 LaPa 配置结构完全一致,仅在数据集、关键点数量、训练轮数上不同,可对照阅读。
运行时与优化器
_base_ = ['../../../_base_/default_runtime.py'] # runtime max_epochs = 60 stage2_num_epochs = 10 base_lr = 4e-3 train_cfg = dict(max_epochs=max_epochs, val_interval=1) randomness = dict(seed=21) # optimizer optim_wrapper = dict( type='OptimWrapper', optimizer=dict(type='AdamW', lr=base_lr, weight_decay=0.05), paramwise_cfg=dict( norm_decay_mult=0, bias_decay_mult=0, bypass_duplicate=True))要点:
- 优化器采用AdamW,基础学习率
4e-3,权重衰减0.05;norm_decay_mult=0与bias_decay_mult=0表示归一化层与偏置不做权重衰减,这是大规模训练中稳定 BN/偏置的常见做法; - 随机种子固定为 21,保证实验可复现;
- LaPa 配置将
max_epochs调整为 120(见 rtmpose-m_8xb64-120e_lapa-256x256.py)。
学习率调度与自动缩放
param_scheduler = [ dict( type='LinearLR', start_factor=1.0e-5, by_epoch=False, begin=0, end=1000), dict( type='CosineAnnealingLR', eta_min=base_lr * 0.05, begin=max_epochs // 2, end=max_epochs, T_max=max_epochs // 2, by_epoch=True, convert_to_iter_based=True), ] # automatically scaling LR based on the actual training batch size auto_scale_lr = dict(base_batch_size=512)- 前 1000 次迭代用LinearLR 线性预热(从
1e-5起步),随后切换为CosineAnnealingLR余弦退火至base_lr * 0.05; auto_scale_lr = dict(base_batch_size=512):当你的实际 batch size 与 512 不同时,MMPose 会自动按线性缩放学习率,无需手动调参。
SimCC 编码器(codec)
codec = dict( type='SimCCLabel', input_size=(256, 256), sigma=(5.66, 5.66), simcc_split_ratio=2.0, normalize=False, use_dark=False)RTMPose 不再输出二维热图,而是采用SimCC(Simple Coordinate Classification)方案,把人脸关键点定位转换为两个一维坐标分类问题。对应源码见 mmpose/codecs/simcc_label.py:
input_size:模型输入尺寸(w, h) = (256, 256);simcc_split_ratio=2.0:标签分辨率是输入分辨率的 2 倍,即 x/y 方向各生成256*2=512长的分类向量;sigma=(5.66, 5.66):一维高斯标签的方差(源码默认 6.0,此处为 RTMPose 人脸任务调优值)。注意:由于normalize=False,编码时不除以sigma * sqrt(2π)做归一化;use_dark=False:不使用 DARK 亚像素细化(源码中decode()内refine_simcc_dark分支被跳过);smoothing_type默认为'gaussian',即对每个关键点在 x/y 轴分别生成一维高斯分布标签(见_generate_gaussian),标签形状为(N, K, W)与(N, K, H),其中W = w * simcc_split_ratio;- 解码时通过
get_simcc_maximum取最大值位置,再除以simcc_split_ratio还原到输入图像空间。
模型结构:CSPNeXt 骨干 + RTMCCHead 头部
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( _scope_='mmdet', type='CSPNeXt', arch='P5', expand_ratio=0.5, deepen_factor=0.67, widen_factor=0.75, out_indices=(4, ), channel_attention=True, norm_cfg=dict(type='SyncBN'), act_cfg=dict(type='SiLU'), init_cfg=dict( type='Pretrained', prefix='backbone.', checkpoint='https://download.openmmlab.com/mmpose/v1/projects/' 'rtmposev1/cspnext-m_udp-aic-coco_210e-256x192-f2f7d6f6_20230130.pth' )), head=dict( type='RTMCCHead', in_channels=768, out_channels=68, input_size=codec['input_size'], in_featuremap_size=tuple([s // 32 for s in codec['input_size']]), simcc_split_ratio=codec['simcc_split_ratio'], final_layer_kernel_size=7, gau_cfg=dict( hidden_dims=256, s=128, expansion_factor=2, dropout_rate=0., drop_path=0., act_fn='SiLU', use_rel_bias=False, pos_enc=False), loss=dict( type='KLDiscretLoss', use_target_weight=True, beta=10., label_softmax=True), decoder=codec), test_cfg=dict(flip_test=True, ))逐项说明:
- Backbone:CSPNeXt(来自 RTMDet),通过
_scope_='mmdet'复用 MMDetection 注册的实现。deepen_factor=0.67、widen_factor=0.75对应m 规格;arch='P5'指 P5 层级结构,out_indices=(4,)只取最后一层特征,输入 256x256 图像经过 32 倍下采样得到 8x8 特征图; - 预训练:
init_cfg加载 AIC+COCO 上训练 210 epoch 的cspnext-m_udp-aic-coco骨干权重(prefix='backbone.')。WFLW、LaPa 配置使用同一预训练权重;而 face6 配置改用 ImageNet-1K RSB 预训练权重cspnext-m_8xb256-rsb-a1-600e_in1k(见 rtmpose-m_8xb256-120e_face6-256x256.py); - Head:RTMCCHead(源码见 mmpose/models/heads/coord_cls_heads/rtmcc_head.py),由**大核卷积层 + 全连接层 + 门控注意力单元(Gated Attention Unit, GAU)**组成,从低分辨率特征图生成一维 SimCC 表示。关键参数:
in_channels=768:CSPNeXt-m 最后一层输出通道数;out_channels:关键点数量,COCO-WholeBody-Face 为 68,WFLW 为 98,LaPa/face6 为 106;final_layer_kernel_size=7:大核卷积用于扩大感受野;gau_cfg:GAU 模块配置,hidden_dims=256、s=128(分割维度)、expansion_factor=2、激活SiLU;loss=KLDiscretLoss:KL 散度离散回归损失,beta=10.、label_softmax=True;decoder=codec:解码器即前面定义的 SimCCLabel;
test_cfg=dict(flip_test=True):测试时启用水平翻转 TTA,对原图与翻转图的结果取平均,可稳定提升精度。
数据管道:两阶段训练策略
训练管道(阶段一):
train_pipeline = [ dict(type='LoadImage', backend_args=backend_args), dict(type='GetBBoxCenterScale'), dict(type='RandomFlip', direction='horizontal'), # dict(type='RandomHalfBody'), dict( type='RandomBBoxTransform', scale_factor=[0.6, 1.4], rotate_factor=80), dict(type='TopdownAffine', input_size=codec['input_size']), dict(type='mmdet.YOLOXHSVRandomAug'), dict( type='Albumentation', transforms=[ dict(type='Blur', p=0.1), dict(type='MedianBlur', p=0.1), dict( type='CoarseDropout', max_holes=1, max_height=0.4, max_width=0.4, min_holes=1, min_height=0.2, min_width=0.2, p=1.0), ]), dict(type='GenerateTarget', encoder=codec), dict(type='PackPoseInputs') ]数据增强要点:
RandomBBoxTransform:尺度扰动scale_factor=[0.6, 1.4]、旋转扰动rotate_factor=80°(LaPa 配置放宽到[0.5, 1.5]并开启RandomHalfBody);YOLOXHSVRandomAug:HSV 色彩空间增强;Albumentation:Blur/MedianBlur 轻度模糊(p=0.1)与 CoarseDropout 随机块遮挡(用于提升遮挡鲁棒性);GenerateTarget:利用 codec 将关键点坐标编码为 SimCC 标签;face6 配置额外传入use_dataset_keypoint_weights=True。
验证管道则只保留必要的仿射变换,不做任何增强:
val_pipeline = [ dict(type='LoadImage', backend_args=backend_args), dict(type='GetBBoxCenterScale'), dict(type='TopdownAffine', input_size=codec['input_size']), dict(type='PackPoseInputs') ]两阶段训练策略是 RTMPose 的核心技巧之一:配置通过mmdet.PipelineSwitchHook在最后stage2_num_epochs=10个 epoch 切换到更强的阶段二管道train_pipeline_stage2——该阶段收紧尺度扰动为[0.75, 1.25]、旋转角为 60°、CoarseDropout 概率降为 0.5,模拟接近测试时的分布,帮助模型"精修":
custom_hooks = [ dict( type='EMAHook', ema_type='ExpMomentumEMA', momentum=0.0002, update_buffers=True, priority=49), dict( type='mmdet.PipelineSwitchHook', switch_epoch=max_epochs - stage2_num_epochs, switch_pipeline=train_pipeline_stage2) ]同时配置EMA(指数移动平均):ExpMomentumEMA,momentum=0.0002、update_buffers=True,用滑动平均权重提升收敛稳定性与最终精度。
数据加载与评测
train_dataloader = dict( batch_size=32, num_workers=10, persistent_workers=True, sampler=dict(type='DefaultSampler', shuffle=True), dataset=dict( type=dataset_type, # 'CocoWholeBodyFaceDataset' data_root=data_root, # 'data/coco/' data_mode='topdown', ann_file='annotations/coco_wholebody_train_v1.0.json', data_prefix=dict(img='train2017/'), pipeline=train_pipeline, ))- 训练 batch_size 为 32(WFLW 为 64,face6 为 256);
data_mode='topdown'表明使用自顶向下范式,需要检测器提供人脸框; - 对应数据集类源码见 mmpose/datasets/datasets/face/coco_wholebody_face_dataset.py、mmpose/datasets/datasets/face/wflw_dataset.py、mmpose/datasets/datasets/face/lapa_dataset.py;
- 评测器使用NME 指标(源码见 mmpose/evaluation/metrics/keypoint_2d_metrics.py 中的
NME类):
val_evaluator = dict( type='NME', norm_mode='keypoint_distance', ) test_evaluator = val_evaluatornorm_mode='keypoint_distance'表示用**两眼瞳孔间距(inter-ocular distance)**作为归一化因子——NME类内置的DEFAULT_KEYPOINT_INDICES中,coco_wholebody_face取[36, 45]、wflw取[60, 72](均为最左/最右眼关键点);LaPa 同理。checkpoint hook 以save_best='NME', rule='less'保存最佳权重,NME 越小越好。
训练、测试与推理实战
准备数据与安装
按 docs/zh_cn/user_guides/prepare_datasets.md 与 docs/zh_cn/installation.md 准备数据集(COCO-WholeBody 需按data/coco/布局放置 annotations 与图片,WFLW 需转换为 COCO 格式,转换脚本见 tools/dataset_converters/wflw2coco.py)并安装 MMPose 及其依赖。
训练
python tools/train.py configs/face_2d_keypoint/rtmpose/coco_wholebody_face/rtmpose-m_8xb32-60e_coco-wholebody-face-256x256.pyWFLW 与 LaPa 对应命令:
python tools/train.py configs/face_2d_keypoint/rtmpose/wflw/rtmpose-m_8xb64-60e_wflw-256x256.py python tools/train.py configs/face_2d_keypoint/rtmpose/lapa/rtmpose-m_8xb64-120e_lapa-256x256.py训练入口为 tools/train.py。多卡分布式训练可改用 tools/dist_train.sh:
bash tools/dist_train.sh <CONFIG> <GPU_NUM>单卡训练可用--cfg-options覆盖配置,例如调整 batch size 以适配显存:
python tools/train.py <CONFIG> --cfg-options train_dataloader.batch_size=16测试
使用 tools/test.py 评测已训练模型,--out保存结果文件,--work-dir指定工作目录:
python tools/test.py <CONFIG> <CHECKPOINT> --out <RESULT_FILE> --work-dir <WORK_DIR>例如:
python tools/test.py configs/face_2d_keypoint/rtmpose/coco_wholebody_face/rtmpose-m_8xb32-60e_coco-wholebody-face-256x256.py \ <CHECKPOINT_PATH> --out results.pkl推理与可视化
对于单张人脸图片的 top-down 推理,可直接使用 demo/topdown_demo_with_mmdet.py,其通过 MMDetection 检测器提供人脸框、MMPose 模型完成关键点定位:
python demo/topdown_demo_with_mmdet.py \ <MMDET_CONFIG> <MMDET_CKPT> \ <POSE_CONFIG> <POSE_CKPT> \ --input <IMAGE_OR_VIDEO> \ --output-root <OUTPUT_DIR> \ --draw-heatmap检测器配置可复用 demo/mmdetection_cfg 中的人脸检测配置。若已有人脸框标注,也可用 demo/image_demo.py 直接推理。
face6:六大数据集联合训练的统一 106 点人脸关键点模型
除了三个单数据集模型,仓库还提供了face6方案(见 configs/face_2d_keypoint/rtmpose/face6/rtmpose_face6.md),在一个统一的 106 关键点定义下联合训练:
- COCO-Wholebody-Face
- WFLW
- 300W
- COFW
- Halpe
- LaPa
| Config | Input Size | NME(LaPa) | FLOPS(G) | Download |
|---|---|---|---|---|
| RTMPose-t* | 256x256 | 1.67 | 0.652 | Model |
| RTMPose-s* | 256x256 | 1.59 | 1.119 | Model |
| RTMPose-m* | 256x256 | 1.44 | 2.852 | Model |
其中Face6和*表示模型在 6 个公开数据集上联合训练,三个规格(t/s/m)的 FLOPS 分别为 0.652/1.119/2.852 G,LaPa 上的 NME 依次为 1.67/1.59/1.44。模型元数据见 configs/face_2d_keypoint/rtmpose/face6/rtmpose_face6.yml,其中 RTMPose-m 还带有Alias: face,可直接通过mmpose工具链的别名引用。
face6 的训练配置是理解"多数据集统一"的关键,其核心机制:
- 关键点统一(KeypointConverter):6 个数据集的标注点数各不相同(WFLW 98 点、COFW 29 点、Halpe/300W/COCO-WholeBody-Face 68 点等),配置中通过
KeypointConverter与手工定义的映射表(如kpt_68_to_106、mapping_wflw、mapping_halpe、mapping_cofw)将各类标注映射到统一的106 点定义。例如mapping_cofw中(0, 33)表示 COFW 第 0 号点对应 106 点定义中的第 33 号点; - 多源拼接(CombinedDataset):训练时用
CombinedDataset将dataset_lapa、dataset_coco、dataset_wflw、dataset_300w、dataset_cofw、dataset_halpe六个子数据集拼接,metainfo以 configs/base/datasets/lapa.py 为准,且各子数据集pipeline=[],数据增强统一由外层train_pipeline施加; - 测试集组合:测试阶段同样通过
CombinedDataset组合 6 个 val 子集,在统一的 106 点空间上评估; - 更深的调度:
eta_min=base_lr * 0.005,余弦退火在 epoch 30 后开始;增加clip_grad=dict(max_norm=35, norm_type=2)梯度裁剪以稳定大 batch(256)训练;骨干改用 ImageNet-1K RSB 预训练权重。
源码级原理小结
结合仓库源码,RTMPose 人脸关键点链路可以概括为三条主线:
- 编码:SimCCLabel 将 2D 关键点拆解为 x/y 两个一维高斯分类标签,标签长度 = 输入尺寸 ×
simcc_split_ratio,_generate_gaussian按 3-sigma 规则截断高斯范围并跳过不可见点(keypoints_visible < 0.5); - 网络:RTMCCHead 在 CSPNeXt 输出的 8×8 低分辨率特征上,用大核卷积 + FC + GAU 直接回归两组一维向量,配合
KLDiscretLoss(beta=10.、label_softmax=True)训练,解码时经get_simcc_maximum取 argmax 还原坐标; - 评估:NME 以两眼间距(
keypoint_distance归一化模式,内置各数据集的默认眼关键点索引)为归一化因子,对人脸任务比 AP 类指标更贴合对齐精度的工程语义。
这种"一维分类替代二维热图 + 低分辨率特征 + 轻量头部"的组合,正是 RTMPose 在保证精度的同时获得极高推理吞吐的关键,也让其成为实时人脸关键点(美颜、人脸特效、姿态辅助)场景的高性价比选择。
参考资源
- 模型库总览:configs/face_2d_keypoint/rtmpose/README.md
- 各数据集详情:COCO-WholeBody-Face / WFLW / LaPa / Face6 的 md 与 py 配置文件(见上文各节链接)
- 训练与测试入口:tools/train.py、tools/test.py
- 数据集准备与安装:docs/zh_cn/user_guides/prepare_datasets.md、docs/zh_cn/installation.md
- 相关源码:mmpose/codecs/simcc_label.py、mmpose/models/heads/coord_cls_heads/rtmcc_head.py、mmpose/evaluation/metrics/keypoint_2d_metrics.py
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考