23 KiB
教程 1:如何编写配置文件
MMClassification 主要使用 python 文件作为配置文件。其配置文件系统的设计将模块化与继承整合进来,方便用户进行各种实验。所有配置文件都放置在 configs
文件夹下,主要包含 _base_
原始配置文件夹 以及 resnet
, swin_transformer
,vision_transformer
等诸多算法文件夹。
可以使用 python tools/misc/print_config.py /PATH/TO/CONFIG
命令来查看完整的配置信息,从而方便检查所对应的配置文件。
配置文件以及权重命名规则
MMClassification 按照以下风格进行配置文件命名,代码库的贡献者需要遵循相同的命名规则。文件名总体分为四部分:算法信息,模块信息,训练信息和数据信息。逻辑上属于不同部分的单词之间用下划线 '_'
连接,同一部分有多个单词用短横线 '-'
连接。
{algorithm info}_{module info}_{training info}_{data info}.py
algorithm info
:算法信息,算法名称或者网络架构,如 resnet 等;module info
: 模块信息,因任务而异,用以表示一些特殊的 neck、head 和 pretrain 信息;training info
:一些训练信息,训练策略设置,包括 batch size,schedule 数据增强等;data info
:数据信息,数据集名称、模态、输入尺寸等,如 imagenet, cifar 等;
算法信息
指论文中的算法名称缩写,以及相应的分支架构信息。例如:
resnet50
mobilenet-v3-large
vit-small-patch32
:patch32
表示ViT
切分的分块大小seresnext101-32x4d
:SeResNet101
基本网络结构,32x4d
表示在Bottleneck
中groups
和width_per_group
分别为32和4
模块信息
指一些特殊的 neck
、head
或者 pretrain
的信息, 在分类中常见为预训练信息,比如:
in21k-pre
: 在ImageNet21k
上预训练in21k-pre-3rd-party
: 在ImageNet21k
上预训练,其权重来自其他仓库
训练信息
训练策略的一些设置,包括训练类型、 batch size
、 lr schedule
、 数据增强以及特殊的损失函数等等,比如:
Batch size 信息:
- 格式为
{gpu x batch_per_gpu}
, 如8xb32
训练类型(主要见于 transformer 网络,如 ViT
算法,这类算法通常分为预训练和微调两种模式):
ft
: Finetune config,用于微调的配置文件pt
: Pretrain config,用于预训练的配置文件
训练策略信息,训练策略以复现配置文件为基础,此基础不必标注训练策略。但如果在此基础上进行改进,则需注明训练策略,按照应用点位顺序排列,如:{pipeline aug}-{train aug}-{loss trick}-{scheduler}-{epochs}
coslr-200e
: 使用 cosine scheduler, 训练 200 个 epochautoaug-mixup-lbs-coslr-50e
: 使用了autoaug
、mixup
、label smooth
、cosine scheduler
, 训练了 50 个轮次
数据信息
in1k
:ImageNet1k
数据集,默认使用224x224
大小的图片in21k
:ImageNet21k
数据集,有些地方也称为ImageNet22k
数据集,默认使用224x224
大小的图片in1k-384px
: 表示训练的输出图片大小为384x384
cifar100
配置文件命名案例:
repvgg-D2se_deploy_4xb64-autoaug-lbs-mixup-coslr-200e_in1k.py
repvgg-D2se
: 算法信息repvgg
: 主要算法名称。D2se
: 模型的结构。
deploy
:模块信息,该模型为推理状态。4xb64-autoaug-lbs-mixup-coslr-200e
: 训练信息4xb64
: 使用4块 GPU 并且 每块 GPU 的批大小为64。autoaug
: 使用AutoAugment
数据增强方法。lbs
: 使用label smoothing
损失函数。mixup
: 使用mixup
训练增强方法。coslr
: 使用cosine scheduler
优化策略。200e
: 训练 200 轮次。
in1k
: 数据信息。 配置文件用于ImageNet1k
数据集上使用224x224
大小图片训练。
部分配置文件目前还没有遵循此命名规范,相关文件命名近期会更新。
权重命名规则
权重的命名主要包括配置文件名,日期和哈希值。
{config_name}_{date}-{hash}.pth
配置文件结构
在 configs/_base_
文件夹下有 4 个基本组件类型,分别是:
你可以通过继承一些基本配置文件轻松构建自己的训练配置文件。由来自_base_
的组件组成的配置称为 primitive。
为了帮助用户对 MMClassification 检测系统中的完整配置和模块有一个基本的了解,我们使用 ResNet50 原始配置文件 作为案例进行说明并注释每一行含义。更详细的用法和各个模块对应的替代方案,请参考 API 文档。
_base_ = [
'../_base_/models/resnet50.py', # 模型
'../_base_/datasets/imagenet_bs32.py', # 数据
'../_base_/schedules/imagenet_bs256.py', # 训练策略
'../_base_/default_runtime.py' # 默认运行设置
]
下面对这四个部分分别进行说明,仍然以上述 ResNet50 原始配置文件作为案例。
模型
模型参数 model
在配置文件中为一个 python
字典,主要包括网络结构、损失函数等信息:
type
: 分类器名称, 目前 MMClassification 只支持ImageClassifier
, 参考 API 文档。backbone
: 主干网类型,可用选项参考 API 文档。neck
: 颈网络类型,目前 MMClassification 只支持GlobalAveragePooling
, 参考 API 文档。head
: 头网络类型, 包括单标签分类与多标签分类头网络,可用选项参考 API 文档。loss
: 损失函数类型, 支持CrossEntropyLoss
,LabelSmoothLoss
等,可用选项参考 API 文档。
train_cfg
:训练配置, 支持mixup
,cutmix
等训练增强。
配置文件中的 'type' 不是构造时的参数,而是类名。
model = dict(
type='ImageClassifier', # 分类器类型
backbone=dict(
type='ResNet', # 主干网络类型
depth=50, # 主干网网络深度, ResNet 一般有18, 34, 50, 101, 152 可以选择
num_stages=4, # 主干网络状态(stages)的数目,这些状态产生的特征图作为后续的 head 的输入。
out_indices=(3, ), # 输出的特征图输出索引。越远离输入图像,索引越大
frozen_stages=-1, # 网络微调时,冻结网络的stage(训练时不执行反相传播算法),若num_stages=4,backbone包含stem 与 4 个 stages。frozen_stages为-1时,不冻结网络; 为0时,冻结 stem; 为1时,冻结 stem 和 stage1; 为4时,冻结整个backbone
style='pytorch'), # 主干网络的风格,'pytorch' 意思是步长为2的层为 3x3 卷积, 'caffe' 意思是步长为2的层为 1x1 卷积。
neck=dict(type='GlobalAveragePooling'), # 颈网络类型
head=dict(
type='LinearClsHead', # 线性分类头,
num_classes=1000, # 输出类别数,这与数据集的类别数一致
in_channels=2048, # 输入通道数,这与 neck 的输出通道一致
loss=dict(type='CrossEntropyLoss', loss_weight=1.0), # 损失函数配置信息
topk=(1, 5), # 评估指标,Top-k 准确率, 这里为 top1 与 top5 准确率
))
数据
数据参数 data
在配置文件中为一个 python
字典,主要包含构造数据集加载器(dataloader)配置信息:
samples_per_gpu
: 构建 dataloader 时,每个 GPU 的 Batch Sizeworkers_per_gpu
: 构建 dataloader 时,每个 GPU 的 线程数train | val | test
: 构造数据集type
: 数据集类型, MMClassification 支持ImageNet
、Cifar
等 ,参考API 文档data_prefix
: 数据集根目录pipeline
: 数据处理流水线,参考相关教程文档 如何设计数据处理流水线
评估参数 evaluation
也是一个字典, 为 evaluation hook
的配置信息, 主要包括评估间隔、评估指标等。
# dataset settings
dataset_type = 'ImageNet' # 数据集名称,
img_norm_cfg = dict( #图像归一化配置,用来归一化输入的图像。
mean=[123.675, 116.28, 103.53], # 预训练里用于预训练主干网络模型的平均值。
std=[58.395, 57.12, 57.375], # 预训练里用于预训练主干网络模型的标准差。
to_rgb=True) # 是否反转通道,使用 cv2, mmcv 读取图片默认为 BGR 通道顺序,这里 Normalize 均值方差数组的数值是以 RGB 通道顺序, 因此需要反转通道顺序。
# 训练数据流水线
train_pipeline = [
dict(type='LoadImageFromFile'), # 读取图片
dict(type='RandomResizedCrop', size=224), # 随机缩放抠图
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'), # 以概率为0.5随机水平翻转图片
dict(type='Normalize', **img_norm_cfg), # 归一化
dict(type='ImageToTensor', keys=['img']), # image 转为 torch.Tensor
dict(type='ToTensor', keys=['gt_label']), # gt_label 转为 torch.Tensor
dict(type='Collect', keys=['img', 'gt_label']) # 决定数据中哪些键应该传递给检测器的流程
]
# 测试数据流水线
test_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='Resize', size=(256, -1)),
dict(type='CenterCrop', crop_size=224),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img']) # test 时不传递 gt_label
]
data = dict(
samples_per_gpu=32, # 单个 GPU 的 Batch size
workers_per_gpu=2, # 单个 GPU 的 线程数
train=dict( # 训练数据信息
type=dataset_type, # 数据集名称
data_prefix='data/imagenet/train', # 数据集目录,当不存在 ann_file 时,类别信息从文件夹自动获取
pipeline=train_pipeline), # 数据集需要经过的 数据流水线
val=dict( # 验证数据集信息
type=dataset_type,
data_prefix='data/imagenet/val',
ann_file='data/imagenet/meta/val.txt', # 标注文件路径,存在 ann_file 时,不通过文件夹自动获取类别信息
pipeline=test_pipeline),
test=dict( # 测试数据集信息
type=dataset_type,
data_prefix='data/imagenet/val',
ann_file='data/imagenet/meta/val.txt',
pipeline=test_pipeline))
evaluation = dict( # evaluation hook 的配置
interval=1, # 验证期间的间隔,单位为 epoch 或者 iter, 取决于 runner 类型。
metric='accuracy') # 验证期间使用的指标。
训练策略
主要包含 优化器设置、 optimizer hook
设置、学习率策略和 runner
设置:
optimizer
: 优化器设置信息, 支持pytorch
所有的优化器,参考相关 mmcv 文档optimizer_config
:optimizer hook
的配置文件,如设置梯度限制,参考相关 mmcv 代码lr_config
: 学习率策略,支持 "CosineAnnealing"、 "Step"、 "Cyclic" 等等,参考相关 mmcv 文档runner
: 有关runner
可以参考mmcv
对于runner
介绍文档
# 用于构建优化器的配置文件。支持 PyTorch 中的所有优化器,同时它们的参数与 PyTorch 里的优化器参数一致。
optimizer = dict(type='SGD', # 优化器类型
lr=0.1, # 优化器的学习率,参数的使用细节请参照对应的 PyTorch 文档。
momentum=0.9, # 动量(Momentum)
weight_decay=0.0001) # 权重衰减系数(weight decay)。
# optimizer hook 的配置文件
optimizer_config = dict(grad_clip=None) # 大多数方法不使用梯度限制(grad_clip)。
# 学习率调整配置,用于注册 LrUpdater hook。
lr_config = dict(policy='step', # 调度流程(scheduler)的策略,也支持 CosineAnnealing, Cyclic, 等。
step=[30, 60, 90]) # 在 epoch 为 30, 60, 90 时, lr 进行衰减
runner = dict(type='EpochBasedRunner', # 将使用的 runner 的类别,如 IterBasedRunner 或 EpochBasedRunner。
max_epochs=100) # runner 总回合数, 对于 IterBasedRunner 使用 `max_iters`
运行设置
本部分主要包括保存权重策略、日志配置、训练参数、断点权重路径和工作目录等等。
# Checkpoint hook 的配置文件。
checkpoint_config = dict(interval=1) # 保存的间隔是 1,单位会根据 runner 不同变动,可以为 epoch 或者 iter。
# 日志配置信息。
log_config = dict(
interval=100, # 打印日志的间隔, 单位 iters
hooks=[
dict(type='TextLoggerHook'), # 用于记录训练过程的文本记录器(logger)。
# dict(type='TensorboardLoggerHook') # 同样支持 Tensorboard 日志
])
dist_params = dict(backend='nccl') # 用于设置分布式训练的参数,端口也同样可被设置。
log_level = 'INFO' # 日志的输出级别。
resume_from = None # 从给定路径里恢复检查点(checkpoints),训练模式将从检查点保存的轮次开始恢复训练。
workflow = [('train', 1)] # runner 的工作流程,[('train', 1)] 表示只有一个工作流且工作流仅执行一次。
work_dir = 'work_dir' # 用于保存当前实验的模型检查点和日志的目录文件地址。
继承并修改配置文件
为了精简代码、更快的修改配置文件以及便于理解,我们建议继承现有方法。
对于在同一算法文件夹下的所有配置文件,MMClassification 推荐只存在 一个 对应的 原始配置 文件。 所有其他的配置文件都应该继承 原始配置 文件,这样就能保证配置文件的最大继承深度为 3。
例如,如果在 ResNet 的基础上做了一些修改,用户首先可以通过指定 _base_ = './resnet50_8xb32_in1k.py'
(相对于你的配置文件的路径),来继承基础的 ResNet 结构、数据集以及其他训练配置信息,然后修改配置文件中的必要参数以完成继承。如想在基础 resnet50 的基础上将训练轮数由 100 改为 300 和修改学习率衰减轮数,同时修改数据集路径,可以建立新的配置文件 configs/resnet/resnet50_8xb32-300e_in1k.py
, 文件中写入以下内容:
_base_ = './resnet50_8xb32_in1k.py'
runner = dict(max_epochs=300)
lr_config = dict(step=[150, 200, 250])
data = dict(
train=dict(data_prefix='mydata/imagenet/train'),
val=dict(data_prefix='mydata/imagenet/train', ),
test=dict(data_prefix='mydata/imagenet/train', )
)
使用配置文件里的中间变量
用一些中间变量,中间变量让配置文件更加清晰,也更容易修改。
例如数据集里的 train_pipeline
/ test_pipeline
是作为数据流水线的中间变量。我们首先要定义 train_pipeline
/ test_pipeline
,然后将它们传递到 data
中。如果想修改训练或测试时输入图片的大小,就需要修改 train_pipeline
/ test_pipeline
这些中间变量。
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', size=384, backend='pillow',),
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='ToTensor', keys=['gt_label']),
dict(type='Collect', keys=['img', 'gt_label'])
]
test_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='Resize', size=384, backend='pillow'),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
]
data = dict(
train=dict(pipeline=train_pipeline),
val=dict(pipeline=test_pipeline),
test=dict(pipeline=test_pipeline))
忽略基础配置文件里的部分内容
有时,您需要设置 _delete_=True
去忽略基础配置文件里的一些域内容。 可以参照 mmcv 来获得一些简单的指导。
以下是一个简单应用案例。 如果在上述 ResNet50 案例中 使用 cosine schedule ,使用继承并直接修改会报 get unexcepected keyword 'step'
错, 因为基础配置文件 lr_config 域信息的 'step'
字段被保留下来了,需要加入 _delete_=True
去忽略基础配置文件里的 lr_config
相关域内容:
_base_ = '../../configs/resnet/resnet50_8xb32_in1k.py'
lr_config = dict(
_delete_=True,
policy='CosineAnnealing',
min_lr=0,
warmup='linear',
by_epoch=True,
warmup_iters=5,
warmup_ratio=0.1
)
引用基础配置文件里的变量
有时,您可以引用 _base_
配置信息的一些域内容,这样可以避免重复定义。 可以参照 mmcv 来获得一些简单的指导。
以下是一个简单应用案例,在训练数据预处理流水线中使用 auto augment 数据增强,参考配置文件 configs/_base_/datasets/imagenet_bs64_autoaug.py
。 在定义 train_pipeline
时,可以直接在 _base_
中加入定义 auto augment 数据增强的文件命名,再通过 {{_base_.auto_increasing_policies}}
引用变量:
_base_ = ['./pipelines/auto_aug.py']
# dataset settings
dataset_type = 'ImageNet'
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', size=224),
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
dict(type='AutoAugment', policies={{_base_.auto_increasing_policies}}),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='ToTensor', keys=['gt_label']),
dict(type='Collect', keys=['img', 'gt_label'])
]
test_pipeline = [...]
data = dict(
samples_per_gpu=64,
workers_per_gpu=2,
train=dict(..., pipeline=train_pipeline),
val=dict(..., pipeline=test_pipeline))
evaluation = dict(interval=1, metric='accuracy')
通过命令行参数修改配置信息
当用户使用脚本 "tools/train.py" 或者 "tools/test.py" 提交任务,以及使用一些工具脚本时,可以通过指定 --cfg-options
参数来直接修改所使用的配置文件内容。
-
更新配置文件内的字典
可以按照原始配置文件中字典的键的顺序指定配置选项。 例如,
--cfg-options model.backbone.norm_eval=False
将主干网络中的所有 BN 模块更改为train
模式。 -
更新配置文件内列表的键
一些配置字典在配置文件中会形成一个列表。例如,训练流水线
data.train.pipeline
通常是一个列表。 例如,[dict(type='LoadImageFromFile'), dict(type='TopDownRandomFlip', flip_prob=0.5), ...]
。如果要将流水线中的'flip_prob=0.5'
更改为'flip_prob=0.0'
,您可以这样指定--cfg-options data.train.pipeline.1.flip_prob=0.0
。 -
更新列表/元组的值。
当配置文件中需要更新的是一个列表或者元组,例如,配置文件通常会设置
workflow=[('train', 1)]
,用户如果想更改, 需要指定--cfg-options workflow="[(train,1),(val,1)]"
。注意这里的引号 " 对于列表以及元组数据类型的修改是必要的, 并且 不允许 引号内所指定的值的书写存在空格。
导入用户自定义模块
本部分仅在当将 MMClassification 当作库构建自己项目时可能用到,初学者可跳过。
在学习完后续教程 如何添加新数据集、如何设计数据处理流程 、如何增加新模块 后,您可能使用 MMClassification 完成自己的项目并在项目中自定义了数据集、模型、数据增强等。为了精简代码,可以将 MMClassification 作为一个第三方库,只需要保留自己的额外的代码,并在配置文件中导入自定义的模块。案例可以参考 OpenMMLab 算法大赛项目。
只需要在你的配置文件中添加以下代码:
custom_imports = dict(
imports=['your_dataset_class',
'your_transforme_class',
'your_model_class',
'your_module_class'],
allow_failed_imports=False)
常见问题
- 无