MMPose 使用 Python 文件作为配置文件,将模块化设计和继承设计结合到配置系统中,便于进行各种实验。
MMPose 拥有一套强大的配置系统,在注册器的配合下,用户可以通过一个配置文件来定义整个项目需要用到的所有内容,以 Python 字典形式组织配置信息,传递给注册器完成对应模块的实例化。
下面是一个常见的 Pytorch 模块定义的例子:
# 在loss_a.py中定义Loss_A类
Class Loss_A(nn.Module):
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
def forward(self, x):
return x
# 在需要的地方进行实例化
loss = Loss_A(param1=1.0, param2=True)
只需要通过一行代码对这个类进行注册:
# 在loss_a.py中定义Loss_A类
from mmpose.registry import MODELS
@MODELS.register_module() # 注册该类到 MODELS 下
Class Loss_A(nn.Module):
def __init__(self, param1, param2):
self.param1 = param1
self.param2 = param2
def forward(self, x):
return x
并在对应目录下的 __init__.py
中进行 import
:
# __init__.py of mmpose/models/losses
from .loss_a.py import Loss_A
__all__ = ['Loss_A']
我们就可以通过如下方式来从配置文件定义并进行实例化:
# 在config_file.py中定义
loss_cfg = dict(
type='Loss_A', # 通过type指定类名
param1=1.0, # 传递__init__所需的参数
param2=True
)
# 在需要的地方进行实例化
loss = MODELS.build(loss_cfg) # 等价于 loss = Loss_A(param1=1.0, param2=True)
MMPose 预定义的 Registry 在 $MMPOSE/mmpose/registry.py
中,目前支持的有:
DATASETS
:数据集
TRANSFORMS
:数据变换
MODELS
:模型模块(Backbone、Neck、Head、Loss等)
VISUALIZERS
:可视化工具
VISBACKENDS
:可视化后端
METRICS
:评测指标
KEYPOINT_CODECS
:编解码器
HOOKS
:钩子类
需要注意的是,所有新增的模块都需要使用注册器(Registry)进行注册,并在对应目录的 `__init__.py` 中进行 `import`,以便能够使用配置文件构建其实例。
具体而言,一个配置文件主要包含如下五个部分:
通用配置:与训练或测试无关的通用配置,如时间统计,模型存储与加载,可视化等相关 Hook,以及一些分布式相关的环境配置
数据配置:数据增强策略,Dataset和Dataloader相关配置
训练配置:断点恢复、模型权重加载、优化器、学习率调整、训练轮数和测试间隔等
模型配置:模型模块、参数、损失函数等
评测配置:模型性能评测指标
你可以在 $MMPOSE/configs
下找到我们提供的配置文件,配置文件之间通过继承来避免冗余。为了保持配置文件简洁易读,我们将一些必要但不常改动的配置存放到了 $MMPOSE/configs/_base_
目录下,如果希望查阅完整的配置信息,你可以运行如下指令:
python tools/analysis/print_config.py /PATH/TO/CONFIG
通用配置指与训练或测试无关的必要配置,主要包括:
默认Hook:迭代时间统计,训练日志,参数更新,checkpoint 等
环境配置:分布式后端,cudnn,多进程配置等
可视化器:可视化后端和策略设置
日志配置:日志等级,格式,打印和记录间隔等
下面是通用配置的样例说明:
# 通用配置
default_scope = 'mmpose'
default_hooks = dict(
timer=dict(type='IterTimerHook'), # 迭代时间统计,包括数据耗时和模型耗时
logger=dict(type='LoggerHook', interval=50), # 日志打印间隔
param_scheduler=dict(type='ParamSchedulerHook'), # 用于调度学习率更新
checkpoint=dict(
type='CheckpointHook', interval=1, save_best='coco/AP', # ckpt保存间隔,最优ckpt参考指标
rule='greater'), # 最优ckpt指标评价规则
sampler_seed=dict(type='DistSamplerSeedHook')) # 分布式随机种子设置
env_cfg = dict(
cudnn_benchmark=False, # cudnn benchmark开关
mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), # opencv多线程配置
dist_cfg=dict(backend='nccl')) # 分布式训练后端设置
vis_backends = [dict(type='LocalVisBackend')] # 可视化器后端设置
visualizer = dict( # 可视化器设置
type='PoseLocalVisualizer',
vis_backends=[dict(type='LocalVisBackend')],
name='visualizer')
log_processor = dict( # 训练日志格式、间隔
type='LogProcessor', window_size=50, by_epoch=True, num_digits=6)
log_level = 'INFO' # 日志记录等级
通用配置一般单独存放到$MMPOSE/configs/_base_
目录下,通过如下方式进行继承:
_base_ = ['../../../_base_/default_runtime.py'] # 以运行时的config文件位置为相对路径起点
CheckpointHook:
- save_best: `'coco/AP'` 用于 `CocoMetric`, `'PCK'` 用于 `PCKAccuracy`
- max_keep_ckpts: 最大保留ckpt数量,默认为-1,代表不限制
样例:
`default_hooks = dict(checkpoint=dict(save_best='PCK', rule='greater', max_keep_ckpts=1))`
数据配置指数据处理相关的配置,主要包括:
数据后端:数据供给后端设置,默认为本地硬盘,我们也支持从 LMDB,S3 Bucket 等加载
数据集:图像与标注文件路径
加载:加载策略,批量大小等
流水线:数据增强策略
编码器:根据标注生成特定格式的监督信息
下面是数据配置的样例说明:
backend_args = dict(backend='local') # 数据加载后端设置,默认从本地硬盘加载
dataset_type = 'CocoDataset' # 数据集类名
data_mode = 'topdown' # 算法结构类型,用于指定标注信息加载策略
data_root = 'data/coco/' # 数据存放路径
# 定义数据编解码器,用于生成target和对pred进行解码,同时包含了输入图片和输出heatmap尺寸等信息
codec = dict(
type='MSRAHeatmap', input_size=(192, 256), heatmap_size=(48, 64), sigma=2)
train_pipeline = [ # 训练时数据增强
dict(type='LoadImage', backend_args=backend_args, # 加载图片
dict(type='GetBBoxCenterScale'), # 根据bbox获取center和scale
dict(type='RandomBBoxTransform'), # 生成随机位移、缩放、旋转变换矩阵
dict(type='RandomFlip', direction='horizontal'), # 生成随机翻转变换矩阵
dict(type='RandomHalfBody'), # 随机半身增强
dict(type='TopdownAffine', input_size=codec['input_size']), # 根据变换矩阵更新目标数据
dict(
type='GenerateTarget', # 根据目标数据生成监督信息
# 监督信息类型
encoder=codec, # 传入编解码器,用于数据编码,生成特定格式的监督信息
dict(type='PackPoseInputs') # 对target进行打包用于训练
]
test_pipeline = [ # 测试时数据增强
dict(type='LoadImage', backend_args=backend_args), # 加载图片
dict(type='GetBBoxCenterScale'), # 根据bbox获取center和scale
dict(type='TopdownAffine', input_size=codec['input_size']), # 根据变换矩阵更新目标数据
dict(type='PackPoseInputs') # 对target进行打包用于训练
]
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), # 采样策略,不进行打乱
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', # 检测框标注文件,topdown方法专用
data_prefix=dict(img='val2017/'), # 图像路径
test_mode=True, # 测试模式开关
pipeline=test_pipeline # 数据流水线
))
test_dataloader = val_dataloader # 默认情况下不区分验证集和测试集,用户根据需要来自行定义
常用功能可以参考以下教程:
- [恢复训练](../common_usages/resume_training.md)
- [自动混合精度训练](../common_usages/amp_training.md)
- [设置随机种子](../common_usages/set_random_seed.md)
训练配置指训练策略相关的配置,主要包括:
从断点恢复训练
模型权重加载
训练轮数和测试间隔
学习率调整策略,如 warmup,scheduler
优化器和学习率
高级训练策略设置,如自动学习率缩放
下面是训练配置的样例说明:
resume = False # 断点恢复
load_from = None # 模型权重加载
train_cfg = dict(by_epoch=True, max_epochs=210, val_interval=10) # 训练轮数,测试间隔
param_scheduler = [
dict( # warmup策略
type='LinearLR', begin=0, end=500, start_factor=0.001, by_epoch=False),
dict( # scheduler
type='MultiStepLR',
begin=0,
end=210,
milestones=[170, 200],
gamma=0.1,
by_epoch=True)
]
optim_wrapper = dict(optimizer=dict(type='Adam', lr=0.0005)) # 优化器和学习率
auto_scale_lr = dict(base_batch_size=512) # 根据batch_size自动缩放学习率
模型配置指模型训练和推理相关的配置,主要包括:
模型结构
损失函数
数据解码策略
测试时增强策略
下面是模型配置的样例说明,定义了一个基于 HRNetw32 的 Top-down Heatmap-based 模型:
# 定义数据编解码器,如果在数据配置部分已经定义过则无需重复定义
codec = dict(
type='MSRAHeatmap', input_size=(192, 256), heatmap_size=(48, 64), sigma=2)
# 模型配置
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='HRNet',
in_channels=3,
extra=dict(
stage1=dict(
num_modules=1,
num_branches=1,
block='BOTTLENECK',
num_blocks=(4, ),
num_channels=(64, )),
stage2=dict(
num_modules=1,
num_branches=2,
block='BASIC',
num_blocks=(4, 4),
num_channels=(32, 64)),
stage3=dict(
num_modules=4,
num_branches=3,
block='BASIC',
num_blocks=(4, 4, 4),
num_channels=(32, 64, 128)),
stage4=dict(
num_modules=3,
num_branches=4,
block='BASIC',
num_blocks=(4, 4, 4, 4),
num_channels=(32, 64, 128, 256))),
init_cfg=dict(
type='Pretrained', # 预训练参数,只加载backbone权重用于迁移学习
checkpoint='https://download.openmmlab.com/mmpose'
'/pretrain_models/hrnet_w32-36af842e.pth'),
),
head=dict( # 模型头部
type='HeatmapHead',
in_channels=32,
out_channels=17,
deconv_out_channels=None,
loss=dict(type='KeypointMSELoss', use_target_weight=True), # 损失函数
decoder=codec), # 解码器,将heatmap解码成坐标值
test_cfg=dict(
flip_test=True, # 开启测试时水平翻转集成
flip_mode='heatmap', # 对heatmap进行翻转
shift_heatmap=True, # 对翻转后的结果进行平移提高精度
))
评测配置指公开数据集中关键点检测任务常用的评测指标,主要包括:
AR, AP and mAP
PCK, PCKh, tPCK
AUC
EPE
NME
下面是评测配置的样例说明,定义了一个COCO指标评测器:
val_evaluator = dict(
type='CocoMetric', # coco 评测指标
ann_file=data_root + 'annotations/person_keypoints_val2017.json') # 加载评测标注数据
test_evaluator = val_evaluator # 默认情况下不区分验证集和测试集,用户根据需要来自行定义
MMPose 配置文件命名风格如下:
{{算法信息}}_{{模块信息}}_{{训练信息}}_{{数据信息}}.py
文件名总体分为四部分:算法信息,模块信息,训练信息和数据信息。不同部分的单词之间用下划线 '_'
连接,同一部分有多个单词用短横线 '-'
连接。
算法信息:算法名称,如 topdown-heatmap
,topdown-rle
等
模块信息:按照数据流的顺序列举一些中间的模块,其内容依赖于算法任务,如 res101
,hrnet-w48
等
训练信息:训练策略的一些设置,包括 batch size
,schedule
等,如 8xb64-210e
数据信息:数据集名称、模态、输入尺寸等,如 ap10k-256x256
,zebra-160x160
等
有时为了避免文件名过长,会省略模型信息中一些强相关的模块,只保留关键信息,如RLE-based算法中的GAP
,Heatmap-based算法中的 deconv
等。
如果你希望向MMPose添加新的方法,你的配置文件同样需要遵守该命名规则。
该用法常用于隐藏一些必要但不需要修改的配置,以提高配置文件的可读性。假如有如下两个配置文件:
optimizer_cfg.py
:
optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
resnet50.py
:
_base_ = ['optimizer_cfg.py']
model = dict(type='ResNet', depth=50)
虽然我们在 resnet50.py
中没有定义 optimizer 字段,但由于我们写了 _base_ = ['optimizer_cfg.py']
,会使这个配置文件获得 optimizer_cfg.py
中的所有字段:
cfg = Config.fromfile('resnet50.py')
cfg.optimizer # ConfigDict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
对于继承过来的已经定义好的字典,可以直接指定对应字段进行修改,而不需要重新定义完整的字典:
resnet50_lr0.01.py
:
_base_ = ['optimizer_cfg.py']
model = dict(type='ResNet', depth=50)
optimizer = dict(lr=0.01) # 直接修改对应字段
这个配置文件只修改了对应字段lr
的信息:
cfg = Config.fromfile('resnet50_lr0.01.py')
cfg.optimizer # ConfigDict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)
如果不仅是需要修改某些字段,还需要删除已定义的一些字段,需要在重新定义这个字典时指定_delete_=True
,表示将没有在新定义中出现的字段全部删除:
resnet50.py
:
_base_ = ['optimizer_cfg.py', 'runtime_cfg.py']
model = dict(type='ResNet', depth=50)
optimizer = dict(_delete_=True, type='SGD', lr=0.01) # 重新定义字典
此时字典中除了 type
和 lr
以外的内容(momentum
和weight_decay
)将被全部删除:
cfg = Config.fromfile('resnet50_lr0.01.py')
cfg.optimizer # ConfigDict(type='SGD', lr=0.01)
如果你希望更深入地了解配置系统的高级用法,可以查看 [MMEngine 教程](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/config.html)。