MMDetection 之外:用 MMPose 在 WFLW 上训练 ResNet-50 + WingLoss 人脸关键点回归模型
MMDetection 之外用 MMPose 在 WFLW 上训练 ResNet-50 WingLoss 人脸关键点回归模型【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose导读本文以 MMPose 仓库中 configs/face_2d_keypoint/topdown_regression/wflw/resnet_wingloss_wflw.md 文档为核心完整剖析在 WFLW 人脸关键点数据集上使用「Top-Down 回归 ResNet-50 Wing Loss」训练 98 点人脸对齐模型的完整配置、原理与实测流程。读完本文你将掌握 MMPose 回归式人脸关键点方案的代码结构、Wing Loss 的分段损失设计、从数据处理到训练评估的全链路配置方法以及如何在 WFLW 测试集上复现 NME 4.67 的基准结果。1. 文档与模型概览MMPose 的 face 2D keypoint 回归分支在configs/face_2d_keypoint/topdown_regression/wflw/目录下提供了一套基于 WFLW 数据集的 ResNet-50 WingLoss 训练配置。该配置对应文档 resnet_wingloss_wflw.md 中登记的基准结果ModelInput SizeNMEckptlogResNet-50 WingLoss256x2564.67ckptlog从文档和元数据文件 resnet_wingloss_wflw.yml 可以看到该模型由三大构件组成算法DeepPoseCVPR2014即最早提出的基于深度神经网络的直接坐标回归式姿态估计方法骨干网络ResNet-50CVPR2016损失函数Wing LossCVPR2018专门针对人脸关键点定位设计的鲁棒损失数据集WFLWCVPR2018包含 98 个人脸关键点标注。模型仅在 WFLW 训练集上训练对应the model is trained on WFLW train set的说明输入分辨率为 256×256在 WFLW 测试集上报告 NMENormalized Mean Error归一化平均误差为4.67。与其同目录的其他变体对照可帮助理解差异不换损失时 ResNet-50 回归 baseline NME 为 4.88见 resnet_wflw.md换成 SoftWingLoss 后 NME 降至 4.44见 resnet_softwingloss_wflw.md可见损失函数设计对人脸对齐精度的直接影响。2. 核心配置文件逐段拆解完整配置位于 td-reg_res50_wingloss_8xb64-210e_wflw-256x256.py文件名本身已经概括了全部关键信息td-regTop-Down Regression自顶向下回归、res50ResNet-50、wingloss、8xb648 卡 × batch size 64、210e210 个 epoch、wflw-256x256WFLW 数据集256×256 输入。2.1 训练调度与优化器_base_ [../../../_base_/default_runtime.py] # runtime train_cfg dict(max_epochs210, val_interval10) # optimizer optim_wrapper dict(optimizerdict( typeAdam, lr5e-4, )) # learning policy param_scheduler [ dict( typeLinearLR, begin0, end500, start_factor0.001, by_epochFalse), # warm-up dict( typeMultiStepLR, begin0, end210, milestones[170, 200], gamma0.1, by_epochTrue) ] # automatically scaling LR based on the actual training batch size auto_scale_lr dict(base_batch_size512)调度设计要点使用Adam优化器学习率 5e-4共训练210 个 epoch每 10 个 epoch 在验证集上评估一次学习率策略是两段式前 500 个 iteration 使用LinearLR warm-upstart_factor0.001即从 1e-3 × 5e-4 5e-7 线性爬升到 5e-4随后切换到MultiStepLR在第 170、200 epoch 各衰减一次gamma0.1auto_scale_lr dict(base_batch_size512)表示以 512 为基准 batch size 自动缩放学习率。配置中标注单卡 batch 64、8 卡并行时恰好对应基准 512若你使用不同的总 batch size例如单卡 256MMPose 会自动按比例调整初始学习率。2.2 Codec回归标签的编解码codec dict(typeRegressionLabel, input_size(256, 256))MMPose 的 codec 承担「坐标空间 ⇄ 网络输出」之间的双向转换。这里的 RegressionLabel 实现非常直观encode训练标签生成把输入图像坐标系下的关键点坐标(x, y)除以输入尺寸(w, h)归一化到[0, 1]区间得到keypoint_labels同时根据坐标是否落在图像有效范围且keypoints_visible 0.5生成keypoint_weights可见为 1.0否则 0.0即 mmpose/codecs/regression_label.py#L43-L74decode推理输出反算把网络输出的归一化坐标乘回(w, h)还原为图像坐标系坐标并返回各点置信度分数见 mmpose/codecs/regression_label.py#L76-L108。归一化坐标的好处是让网络输出的数值范围稳定且与输入分辨率解耦——这也是回归式头部把最后一层设计为直接输出 98×2196 个数值的前提。2.3 模型TopdownPoseEstimator 的三段式组装model dict( typeTopdownPoseEstimator, data_preprocessordict( typePoseDataPreprocessor, mean[123.675, 116.28, 103.53], std[58.395, 57.12, 57.375], bgr_to_rgbTrue), backbonedict( typeResNet, depth50, init_cfgdict(typePretrained, checkpointtorchvision://resnet50), ), neckdict(typeGlobalAveragePooling), headdict( typeRegressionHead, in_channels2048, num_joints98, lossdict(typeWingLoss, use_target_weightTrue), decodercodec), train_cfgdict(), test_cfgdict( flip_testTrue, shift_coordsTrue, ))按数据流方向拆解为四层PoseDataPreprocessor使用 ImageNet 统计量mean/std归一化输入bgr_to_rgbTrue与常规 OpenCV 读图BGR兼容ResNet-50 骨干输出 2048 维特征使用torchvision://resnet50预训练权重初始化GlobalAveragePooling 颈部把骨干输出的空间特征图池化为固定长度的 2048 维向量这正是回归式头部不需要空间信息的体现与 heatmap 类方法形成鲜明对比RegressionHead见 mmpose/models/heads/regression_heads/regression_head.py其核心只有一个nn.Linear(2048, 98 * 2)全连接层regression_head.py#L62将特征直接回归为 98 个关键点的二维坐标输出(B, 98, 2)head 默认以std0.01, bias0的正态分布初始化该 Linear 层。推理阶段的测试时增强TTAtest_cfg中flip_testTrue表示推理时同时前向原始图像与水平翻转图像利用 WFLW 的 98 点标注中左右对称点见下文 metainfo 的swap字段对翻转结果做关键点互换后将两组坐标取平均作为最终输出shift_coordsTrue负责翻转后坐标偏移的修正。这一逻辑实现于 regression_head.py#L87-L100通常能带来稳定的小幅精度提升。2.4 损失函数Wing LossWing Loss 的提出动机是人脸关键点误差分布高度集中在小误差区域而 L1/L2 损失对小误差的梯度特性不理想。其定义在 mmpose/models/losses/regression_loss.py#L222-L266 中实现def __init__(self, omega10.0, # 宽度划分线性/非线性区域 epsilon2.0, # 曲率控制非线性区域形状 use_target_weightFalse, loss_weight1.): # 连接分段线性部分与对数部分的常数 self.C self.omega * (1.0 - math.log(1.0 self.omega / self.epsilon)) def criterion(self, pred, target): delta (target - pred).abs() losses torch.where( delta self.omega, self.omega * torch.log(1.0 delta / self.epsilon), delta - self.C) return torch.mean(torch.sum(losses, dim[1, 2]), dim0)其分段函数可概括为当误差delta omega时使用omega * ln(1 delta / epsilon)——这是对 L1 损失的平滑化Wing改造对小误差给予更陡峭的梯度从而在关键点接近但不够准的典型人脸对齐场景中加速收敛当delta omega时退化为线性损失delta - C其中常数C omega * (1 - ln(1 omega/epsilon))保证两段在delta omega处连续避免对遮挡、标注噪声等大误差样本的过激惩罚。默认超参omega10.0, epsilon2.0本配置通过lossdict(typeWingLoss, use_target_weightTrue)开启逐关键点加权训练时用 codec 生成的keypoint_weights屏蔽不可见/越界关键点对损失的贡献实现见 regression_loss.py#L268-L279 与 regression_head.py#L115-L128。2.5 数据管线与加载器dataset_type WFLWDataset data_mode topdown data_root data/wflw/ train_pipeline [ dict(typeLoadImage), dict(typeGetBBoxCenterScale), dict(typeRandomFlip, directionhorizontal), dict( typeRandomBBoxTransform, scale_factor[0.75, 1.25], rotate_factor60), dict(typeTopdownAffine, input_sizecodec[input_size]), dict(typeGenerateTarget, encodercodec), dict(typePackPoseInputs) ] val_pipeline [ dict(typeLoadImage), dict(typeGetBBoxCenterScale), dict(typeTopdownAffine, input_sizecodec[input_size]), dict(typePackPoseInputs) ]训练与验证管线在TopdownAffine之前的差异正是数据增强的全部RandomFlip水平翻转配合 98 点标注的对称映射使用RandomBBoxTransform对检测框做尺度扰动scale_factor[0.75, 1.25]与旋转扰动rotate_factor60度TopdownAffine根据中心/尺度信息将人脸裁剪区域仿射变换到 256×256GenerateTarget调用RegressionLabelcodec 的 encode 生成归一化回归标签验证/测试阶段不做随机翻转与随机框变换只做仿射归一化保证评估的可复现性。数据加载部分的关键细节train_dataloader dict( batch_size64, num_workers2, persistent_workersTrue, samplerdict(typeDefaultSampler, shuffleTrue), datasetdict( typedataset_type, data_rootdata_root, data_modedata_mode, ann_fileannotations/face_landmarks_wflw_train.json, data_prefixdict(imgimages/), pipelinetrain_pipeline, )) val_dataloader dict( batch_size32, ... ann_fileannotations/face_landmarks_wflw_test.json, data_prefixdict(imgimages/), test_modeTrue, pipelineval_pipeline, ) test_dataloader val_dataloaderWFLWDataset继承自BaseCocoStyleDataset实现位于 mmpose/datasets/datasets/face/wflw_dataset.py遵循 COCO 风格的 JSON 标注格式标注采用 WFLW 官方 98 点标记规范训练用face_landmarks_wflw_train.json验证/测试用face_landmarks_wflw_test.json图片存放于data/wflw/images/数据集需按 docs/zh_cn/user_guides/prepare_datasets.md 中的指引提前下载并组织目录WFLWDataset还支持通过bbox_file参数在评估阶段用外部检测器输出替换 GT bbox仅test_modeTrue时生效方便端到端评测检测-对齐流水线。2.6 评估指标与保存策略default_hooks dict(checkpointdict(save_bestNME, ruleless)) val_evaluator dict( typeNME, norm_modekeypoint_distance, ) test_evaluator val_evaluator指标使用NME归一化平均误差实现于 mmpose/evaluation/metrics/keypoint_2d_metrics.py#L722。norm_modekeypoint_distance表示用一对基准关键点的欧氏距离做归一化对 WFLW 数据集默认取索引 [60, 72] 即左右眼外角点距离作为归一化因子见该类的DEFAULT_KEYPOINT_INDICES字典keypoint_2d_metrics.py#L760-L774save_bestNME, ruleless训练过程中保存 NME 最小的检查点NME 越低越好。3. 训练、测试与推理实战3.1 单卡训练python tools/train.py configs/face_2d_keypoint/topdown_regression/wflw/td-reg_res50_wingloss_8xb64-210e_wflw-256x256.py3.2 多卡分布式训练bash tools/dist_train.sh configs/face_2d_keypoint/topdown_regression/wflw/td-reg_res50_wingloss_8xb64-210e_wflw-256x256.py 8第二个参数为 GPU 数量文件名中的8xb64即对应 8 卡 × 每卡 64 的设定。若 GPU 数量不足建议同步调整batch_size并依赖auto_scale_lr自动修正学习率。3.3 测试与复现基准python tools/test.py configs/face_2d_keypoint/topdown_regression/wflw/td-reg_res50_wingloss_8xb64-210e_wflw-256x256.py \ checkpoint.pth --out work_dirs/result.pkl使用文档中提供的预训练权重即可在 WFLW 测试集上复现NME 4.67。评估过程由 tools/test.py 驱动数据经 val_pipeline 预处理 →RegressionHead.predict前向推理含水平翻转 TTA→RegressionLabel.decode还原坐标 →NME指标计算。3.4 端到端推理推理可借助 demo 脚本例如python demo/topdown_demo_with_mmdet.py \ det_config det_checkpoint \ configs/face_2d_keypoint/topdown_regression/wflw/td-reg_res50_wingloss_8xb64-210e_wflw-256x256.py \ pose_checkpoint \ --input image_or_video --output-root vis_results/人脸检测器配置可从 demo/mmdetection_cfg 目录中选择例如基于 Faster R-CNN 或 RTMDet 的人脸检测配置。Top-Down 流程的完整链路为检测器先框出人脸 → 裁剪并仿射归一化到 256×256 → 回归头部直接输出 98 点坐标 → 映射回原图可视化。4. 从源码结构理解方案设计思路4.1 回归 vs 热图为何人脸对齐常用回归对比同一仓库中 face 分支下的 topdown_heatmap 方案热图法把每个关键点编码为二维高斯热图输出分辨率受限、显存开销大而回归法DeepPose 路线通过全连接层直接输出坐标无空间分辨率限制。人脸关键点数量多WFLW 达 98 点且点间距小回归式头部加上为小误差设计的 Wing Loss能在紧凑的人脸局部区域获得精确且内存友好的定位结果——这正是本配置选择该技术路线的核心原因。4.2 数据集元信息对称关键点映射的支撑数据集元信息定义在 configs/base/datasets/wflw.py包含 98 个关键点的完整定义keypoint_info中每个点都配置了swap字段表示水平翻转后的对应点如kpt-0 ↔ kpt-32、kpt-60 ↔ kpt-72。这份映射是RandomFlip数据增强与flip_testTrue测试时增强得以正确实施的前提翻转后的坐标必须按 swap 关系交换左右对称点模型输出的坐标才能与原始语义对齐。5. 同目录变体与进阶参考在同一配置目录下MMPose 还提供了对比实验配置非常适合做消融研究配置损失NMEtd-reg_res50_8xb64-210e_wflw-256x256.pySmoothL1默认4.88td-reg_res50_wingloss_8xb64-210e_wflw-256x256.pyWingLoss4.67td-reg_res50_softwingloss_8xb64-210e_wflw-256x256.pySoftWingLoss4.44三份配置仅 loss 不同、其余完全一致直观展示了损失函数对人脸对齐精度的提升路径从基线 4.88 → Wing Loss 4.67 → SoftWingLoss 4.44。若想进一步掌握回归式关键点方法的通用设计可结合 docs/zh_cn/advanced_guides/codecs.mdcodec 机制详解与 docs/zh_cn/user_guides/train_and_test.md训练/测试/调参指南继续深入。【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考