PaddleDetection 关键点检测配置完全指南:以 tinypose_256x192.yml 为例解析 Top-Down 姿态估计全流程

原创2026-09-22 15:24:2068 阅读
文章标签:人工智能深度学习计算机视觉

PaddleDetection 关键点检测配置完全指南:以 tinypose_256x192.yml 为例解析 Top-Down 姿态估计全流程

导读

本文是 PaddleDetection 关键点检测(KeyPoint Detection)配置体系的实战指南,以仓库中 configs/keypoint/tiny_pose/tinypose_256x192.yml 这一真实配置文件为蓝本,逐段讲解 Top-Down 姿态估计任务的完整 YAML 配置——从全局训练参数、模型结构、优化器,到数据集定义与数据增强流水线。读完本文,你将掌握 PaddleDetection 关键点模型每个配置项的语义、取值边界与底层实现原理,能够独立读懂、修改并上手训练(如 PP-TinyPose 系列)关键点检测模型。

一、配置文件的整体骨架

PaddleDetection 采用"约定式配置 + 注册机制"的设计:YAML 文件描述模型的组装方式与训练流程,ppdet/core/workspace.py 负责解析,各模块通过 @register 装饰器向全局注册表登记,配置中的字符串类名(如 TopDownHRNetLiteHRNetKeyPointMSELoss)会被动态实例化为真实对象。

一份关键点配置文件通常由以下五个部分组成,这与通用检测配置(可参考 configs/runtime.yml)保持一致:

# 1. 全局训练参数(use_gpu / epoch / weights / save_dir 等)
# 2. model 模型结构(architecture + backbone + head + loss + post_process)
# 3. optimizer 优化器(LearningRate 调度 + OptimizerBuilder)
# 4. data 数据部分(TrainDataset / EvalDataset / TestDataset + 三个 Reader)
# 5. 其他公共定义(worker_num、global_mean、global_std 等)

其中大量参数通过 YAML 锚点(&name)与引用(*name)复用,例如 num_joints: &num_joints 17 定义一次,后续在模型、数据集、损失中统一引用,避免多处维护导致不一致。理解这一点是读懂本配置的关键。

二、全局训练参数详解

2.1 训练控制类参数

配置项 示例值 含义与说明
use_gpu true 是否使用 GPU 训练
log_iter 5 每 5 个 iter 打印一次日志
save_dir output 模型保存目录
snapshot_epoch 10 每训练 10 个 epoch 保存一次快照
weights output/tinypose_256x192/model_final 加载的权重路径,不含 .pdparams 后缀;用于断点续训或评估
epoch 420 总训练轮数

2.2 任务核心语义参数

num_joints: &num_joints 17      # 关键点数量(COCO 人体 17 点)
pixel_std: &pixel_std 200       # 标准化像素长度,用于由 bbox 计算尺度
metric: KeyPointTopDownCOCOEval  # 评估指标
num_classes: 1                   # 类别数(检测任务字段,关键点任务不使用)
train_height: &train_height 256  # 模型输入高度
train_width: &train_width 192    # 模型输入宽度
trainsize: &trainsize [*train_width, *train_height]  # 输入尺寸 [w, h]
hmsize: &hmsize [48, 64]         # 输出热图尺寸 [w, h]
flip_perm: &flip_perm [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12], [13, 14], [15, 16]]

逐一说明:

  • num_joints:决定输出通道数。COCO 人体关键点共 17 个,其索引定义可参见 ppdet/data/source/keypoint_coco.py 中的注释:0 鼻子、1/2 左右眼、3/4 左右耳、5/6 左右肩、7/8 左右肘、9/10 左右腕、11/12 左右髋、13/14 左右膝、15/16 左右踝。
  • pixel_std:文档注释为"don't care",实际它在数据加载中承担重要作用——KeypointTopDownCocoDataset._box2csscale = [w / pixel_std, h / pixel_std] 将 bbox 尺度归一化到模型坐标系(见下文数据集章节),因此应保持默认值 200 并与其他模块保持一致。
  • hmsize:输出热图尺寸,通常为输入尺寸的 1/4(256×192 输入 → 64×48 热图)。注意 trainsize[w, h](即 [192, 256]),而 hmsize[48, 64]——前者宽在前,后者高在前,是两个维度顺序约定不同的列表,配置时切勿混淆。
  • flip_perm:左右对称关键点配对表。进行水平翻转数据增强或 flip-test 时,左腕(id=9)会变成右腕,因此需要将左右对应的关键点互换;<a href="https://link.gitcode.com/i/81c2f226733015db21a63a4c2c7b59a5" target="_blank">1,2] 表示左眼(1)与右眼(2)成对。该配对在 [ppdet/modeling/architectures/keypoint_hrnet.py 的 flip_back 方法中被用于把翻转后的热图映射回原坐标系。

三、model 模型模块

3.1 TopDownHRNet 架构

architecture: TopDownHRNet     # 模型架构

TopDownHRNet:
  backbone: LiteHRNet          # 骨干网络
  post_process: HRNetPostProcess
  flip_perm: *flip_perm        # 引用全局 flip_perm
  num_joints: *num_joints      # 关键点数(即输出通道数)
  width: &width 40             # backbone 输出通道数
  loss: KeyPointMSELoss        # 损失函数
  use_dark: true               # 后处理是否使用 DarkPose 坐标解码

TopDownHRNet 的完整实现位于 ppdet/modeling/architectures/keypoint_hrnet.py,核心前向逻辑为:

  1. backbone(self.inputs) 提取特征 feats
  2. 通过 final_conv = L.Conv2d(width, num_joints, 1, 1, 0, bias=True)(1×1 卷积)将 width 通道映射为 num_joints 个通道,即生成每张热图;
  3. 训练阶段直接返回热图交给 KeyPointMSELoss 计算损失;
  4. 推理阶段若 flip=True,将图像水平翻转再前向一次,用 flip_back 还原左右关键点并做 shift_heatmap 平移修正,最后与原热图取平均(即 flip-test 测试时增强);
  5. 后处理由 HRNetPostProcess 完成,将热图坐标映射回原图坐标系。

use_dark: true 对应 HRNetPostProcess(use_dark),即采用 DARK(Distribution-Aware coordinate Representation of KeyPoints,CVPR 2020)坐标解码:先对热图做高斯模糊、取对数,再对峰值附近做二阶泰勒展开修正坐标,得到亚像素精度的关键点位置。该逻辑在 keypoint_hrnet.pydark_parse / dark_postprocess / get_final_preds 中实现;若关闭 DARK,则退化为"峰值 + 0.25 像素偏移"的常规解码。在低分辨率热图(如本配置 64×48)下,DARK 能显著提升精度。

3.2 LiteHRNet 骨干网络

LiteHRNet:
  network_type: wider_naive   # 网络类型
  freeze_at: -1               # 冻结分支:-1 表示不冻结
  freeze_norm: false          # 是否冻结归一化层权重
  return_idx: [0]             # 取第 0 个分支的特征

LiteHRNet 源码在 ppdet/modeling/backbones/lite_hrnet.py

  • network_type 可选 ["lite_18", "lite_30", "naive", "wider_naive"]wider_naive 将 naive 结构各 stage 通道加宽为 [40,80] / [40,80,160] / [40,80,160,320],是 PP-TinyPose 为移动端优化的轻量选择;
  • freeze_at:冻结指定阶段不参与反向传播,-1 表示全部参与训练;
  • freeze_norm:是否冻结 BN 等归一化层参数(迁移学习冷启动时常用);
  • return_idx:返回哪个 stage 的特征给检测头。配置为 [0],即只取第一级输出,对应 TopDownHRNet._forward 中的 feats[0]

3.3 损失函数

KeyPointMSELoss:
  use_target_weight: true   # 是否使用目标权重(按关键点可见性加权)
  loss_scale: 1.0           # 损失缩放系数,final_loss = loss * loss_scale

KeyPointMSELoss 实现在 ppdet/modeling/losses/keypoint_loss.py:底层为 nn.MSELoss(reduction='mean')use_target_weight=True 时,热图损失按每个关键点的可见性权重(由 ToHeatmapsTopDown_DARK 生成的 target_weight)进行加权,遮挡/未标注的关键点不参与梯度贡献。

四、optimizer 优化器模块

4.1 学习率调度

LearningRate:
  base_lr: 0.002                     # 基础学习率
  schedulers:
  - !PiecewiseDecay                  # 分段衰减
    milestones: [380, 410]           # 衰减发生的 epoch
    gamma: 0.1                       # 衰减系数,new_lr = lr * gamma
  - !LinearWarmup                    # 线性预热
    start_factor: 0.001              # 起始学习率为 base_lr 的 0.001 倍
    steps: 500                       # 预热持续 500 个 iter

epoch: 420milestones: [380, 410] 配合:训练前 380 个 epoch 保持 base_lr,380 与 410 处各衰减为原来的 0.1 倍,即学习率路径为 0.002 → 0.0002 → 0.00002,后段用小学习率精细收敛。LinearWarmup 在前 500 个 iter 将学习率从 0.002 × 0.001 线性升至 0.002,避免训练初期梯度震荡。

4.2 优化器构建

OptimizerBuilder:
  optimizer:
    type: Adam            # 优化器类型
  regularizer:
    factor: 0.0           # 正则化权重
    type: L2              # 正则化类型 L2 / L1

关键点任务通常使用 Adam + 无权重衰减(factor: 0.0)。需要提醒的是:若修改 GPU 数量或 batch size,应根据 configs/keypoint/tiny_pose/README_en.md 中 Model Zoo 的说明参考 docs/tutorials/FAQ/README.md 按线性缩放规则调整 base_lr

五、data 数据模块

5.1 三个数据集的定义

TrainDataset:
  !KeypointTopDownCocoDataset
    image_dir: ""                                # 图片目录(相对 dataset_dir)
    anno_path: aic_coco_train_cocoformat.json    # 训练标注(COCO 格式,相对 dataset_dir)
    dataset_dir: dataset                         # 数据集根目录
    num_joints: *num_joints
    trainsize: *trainsize
    pixel_std: *pixel_std
    use_gt_bbox: True                            # 是否使用 GT bbox

EvalDataset:
  !KeypointTopDownCocoDataset
    image_dir: val2017
    anno_path: annotations/person_keypoints_val2017.json
    dataset_dir: dataset/coco
    num_joints: *num_joints
    trainsize: *trainsize
    pixel_std: *pixel_std
    use_gt_bbox: True
    image_thre: 0.5          # 检测框置信度阈值(use_gt_bbox=False 时生效)

TestDataset:
  !ImageFolder
    anno_path: dataset/coco/keypoint_imagelist.txt   # 图片列表文件

要点解析:

  • KeypointTopDownCocoDataset 是 Top-Down 关键点数据集加载器,实现在 ppdet/data/source/keypoint_coco.py。Top-Down 范式是"先检测人框、再对每人体关键点":数据集中每条样本对应"一张图片中的一个人框 + 该人的关键点标注"。
  • use_gt_bbox: True 时,调用 _load_coco_keypoint_annotations() 直接从标注解析 bbox 并计算 center/scale;use_gt_bbox: False 时调用 _load_coco_person_detection_results()bbox_file(检测器输出的 JSON)读取人框,此时 image_thre 用于过滤低置信度框,模拟真实部署时"检测器+关键点"级联的评测场景。文档注释"commonly used in eval"即指后者。
  • _box2cskeypoint_coco.py)是 Top-Down 的关键预处理:由 bbox 中心得到 center,并按 trainsize 宽高比修正后除以 pixel_std 得到 scale,再统一乘以 1.25 留出裁剪余量,确保姿态不会贴边被截断。
  • 训练标注 aic_coco_train_cocoformat.json 是 COCO 与 AI Challenger 数据集的合并标注(统一为 COCO 格式),这一对齐细节与训练命令可参见 configs/keypoint/tiny_pose/README_en.md 的 Model Training 章节。
  • TestDataset 使用 ImageFolder:无标注推理时仅通过图片列表文件加载图像。

5.2 训练数据增强流水线(TrainReader)

worker_num: 2                                   # 数据加载进程数
global_mean: &global_mean [0.485, 0.456, 0.406] # 归一化均值
global_std: &global_std [0.229, 0.224, 0.225]   # 归一化标准差

TrainReader:
  sample_transforms:
    - RandomFlipHalfBodyTransform:               # 随机翻转 + 随机半身变换
        scale: 0.25                              # 尺寸缩放幅度上限
        rot: 30                                  # 最大旋转角度
        num_joints_half_body: 8                  # 可见关键点少于该值时跳过半身变换
        prob_half_body: 0.3                      # 半身变换概率
        pixel_std: *pixel_std
        trainsize: *trainsize
        upper_body_ids: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # 上半身关键点 id
        flip_pairs: *flip_perm                   # 左右配对(翻转时交换)
    - AugmentationbyInformantionDropping:        # AID 信息丢弃增强
        prob_cutout: 0.5                         # 执行 cutout 的概率
        offset_factor: 0.05                      # 遮挡中心抖动偏移(相对输入宽的百分比)
        num_patch: 1                             # 遮挡区域数量
        trainsize: *trainsize
    - TopDownAffine:                             # 仿射变换到模型输入尺寸
        trainsize: *trainsize
        use_udp: true                            # 是否使用 UDP 无偏数据处理
    - ToHeatmapsTopDown_DARK:                    # 生成 GT 高斯热图
        hmsize: *hmsize
        sigma: 2                                 # 高斯核标准差
  batch_transforms:
    - NormalizeImage:
        mean: *global_mean
        std: *global_std
        is_scale: true                           # 像素值从 [0,255] 缩放到 [0,1]
    - Permute: {}                                # 通道 HWC → CHW
  batch_size: 128                                # 训练 batch size
  shuffle: true
  drop_last: false                               # 是否丢弃不足一个 batch 的尾部数据

各增强算子的底层实现位于 ppdet/data/transform/keypoint_operators.py

  • RandomFlipHalfBodyTransform(L511-L629):综合了随机缩放、随机旋转(rot_prob=0.6 概率触发)、随机水平翻转与半身变换。半身变换在可见关键点总数 > num_joints_half_body 且概率命中 prob_half_body 时,以 upper_body_ids 划分上下半身,随机选取一半身体重新计算 center/scale 并放大 1.5 倍,模拟只拍到半身的场景;翻转时按 flip_pairs 交换左右关键点(flip_joints)。
  • AugmentationbyInformantionDropping(AID,L633-L688):以 prob_cutout 概率随机选中一个可见关键点,在其周围(位置叠加 offset_factor 比例的随机抖动)以 num_patch 个圆形区域进行像素置零,模拟关键点被遮挡的情况,提升模型对遮挡的鲁棒性。
  • TopDownAffine(L820 起):根据 centerscale、旋转角计算仿射变换矩阵(UDP 模式下使用 get_warp_matrix 无偏版本),将人体裁剪区变换到 trainsizeuse_udp: true 采用 UDP(Unbiased Data Processing)编码,避免传统仿射变换因整数取整引入的坐标偏差,配合 flip-test 使用。
  • ToHeatmapsTopDown_DARK(L1149 起):将关键点坐标按 sigma=2 的高斯核散布到 hmsize 热图上生成训练目标,同时产出 target_weight 可见性权重供 KeyPointMSELoss 使用。
  • NormalizeImage / Permute 为批处理变换:前者按 global_mean/global_std 归一化(is_scale=true 时先除以 255),实现见 ppdet/data/transform/operators.py;后者将图像从 HWC 变为 CHW。

5.3 评估与测试 Reader

EvalReader:
  sample_transforms:
    - TopDownAffine:          # 评估仅做仿射变换,无随机增强
        trainsize: *trainsize
        use_udp: true
  batch_transforms:
    - NormalizeImage:         # 均值/方差必须与 TrainReader 一致
        mean: *global_mean
        std: *global_std
        is_scale: true
    - Permute: {}
  batch_size: 16

TestReader:
  inputs_def:
    image_shape: [3, *train_height, *train_width]   # 模型输入维度 CHW
  sample_transforms:
    - Decode: {}               # 加载并解码图片
    - TopDownEvalAffine:       # 评估用仿射变换(center 取图像中心)
        trainsize: *trainsize
    - NormalizeImage:
        mean: *global_mean
        std: *global_std
        is_scale: true
    - Permute: {}
  batch_size: 1
  fuse_normalize: false        # 导出模型时是否将归一化融合进模型
  • EvalReader 与 TrainReader 的差别:去掉随机增强,仅保留仿射变换与归一化,且 batch_size 较小(16)。
  • TestReader 面向无 GT 的部署推理:Decode 负责解码图片(实现在 ppdet/data/transform/operators.py);TopDownEvalAffinekeypoint_operators.py)以整图中心为 center、整图尺寸为 scale 做仿射,适用于检测器已给出人框、或整图单人推理场景。
  • fuse_normalize: false:导出模型时若置为 true,归一化算子会被融合进模型内部,推理时省去预处理中的归一化计算以加速——PP-TinyPose 移动端部署文档明确建议导出时开启(见 configs/keypoint/tiny_pose/README_en.md 的 Deployment 章节)。

六、评估指标与验证闭环

配置中的 metric: KeyPointTopDownCOCOEval 对应 ppdet/metrics/keypoint_metrics.py 中的 KeyPointTopDownCOCOEval:评估时收集每张图的预测关键点 <a href="https://link.gitcode.com/i/82a522736dfe2372209d240def1393ad" target="_blank">x, y, score]、center、scale 与 im_id,写入 keypoints_results.json,最终调用 pycocotoolsCOCOevaliou_type='keypoints')按 OKS(Object Keypoint Similarity)指标计算 AP/AR。其中 oks_thre=0.9in_vis_thre=0.2 为默认评估阈值,相关后处理工具(oks_nms 等)定义于 [ppdet/modeling/keypoint_utils.py。

七、从配置到训练:完整使用流程

基于本配置启动训练(多卡)与评估:

# 训练(-c 指定配置文件)
python3 -m paddle.distributed.launch tools/train.py -c configs/keypoint/tiny_pose/tinypose_256x192.yml

# 评估(-o 覆盖配置,加载训练好的权重)
python3 tools/eval.py -c configs/keypoint/tiny_pose/tinypose_256x192.yml \
    -o weights=output/tinypose_256x192/model_final

# 导出部署模型
python3 tools/export_model.py -c configs/keypoint/tiny_pose/tinypose_256x192.yml \
    --output_dir=output_inference -o weights=output/tinypose_256x192/model_final \
    TestReader.fuse_normalize=true

训练数据需准备 dataset/ 目录下的图片与 aic_coco_train_cocoformat.json 合并标注、dataset/coco/annotations/person_keypoints_val2017.json 验证标注,布局与配置中的 dataset_dir/image_dir/anno_path 对应。若在移动端部署,还需配合 Paddle-Lite 将导出的模型转换为 .nb 格式,完整流程见 deploy/lite/README.md

八、快速修改指南

  • 换输入分辨率:同时修改 train_height/train_widthtrainsizehmsize(约为输入 1/4)三处锚点定义,其余引用自动跟随;参考同目录 tinypose_128x96.yml 的做法。
  • 换数据集:将 TrainDataset/EvalDatasetdataset_dirimage_diranno_path 指向自有数据(需转为 COCO 格式,可参考 tools/x2coco.py),并将 num_joints 改为实际关键点数,同时更新 flip_perm 左右配对与 upper_body_ids 上半身 id 列表。
  • 换骨干网络:将 backbone 改为其他已注册骨干(如 HRNet),并同步调整 width 输出通道。
  • 调整精度/速度权衡:关闭 use_dark(更快)、调小输入分辨率(更快)、减小 hmsizesigma(影响热图精度),各参数取舍可对照 configs/keypoint/KeypointBenchmark.md 的评测数据判断。

结语

PaddleDetection 的关键点配置文件以 YAML 锚点体系实现了"一次定义、全局复用",本文从 tinypose_256x192.yml 出发,逐层拆解了全局参数、模型、优化器、数据加载与增强流水线,并下钻到 TopDownHRNet 前向逻辑、DARK 解码、UDP 仿射、AID 增强等源码实现。掌握这份配置的语义,你便能举一反三地读懂 configs/keypoint 下 HRNet、Lite-HRNet、HigherHRNet、ViTPose 等全部关键点模型的配置,并据此定制自己的姿态估计方案。

登录后查看全文
PaddleDetection