mmpretrain/docs/zh_CN/tutorials/config.md

418 lines
23 KiB
Markdown
Raw Normal View History

# 教程 1如何编写配置文件
MMClassification 主要使用 python 文件作为配置文件。其配置文件系统的设计将模块化与继承整合进来,方便用户进行各种实验。所有配置文件都放置在 `configs` 文件夹下,主要包含 `_base_` 原始配置文件夹 以及 `resnet`, `swin_transformer``vision_transformer` 等诸多算法文件夹。
可以使用 `python tools/misc/print_config.py /PATH/TO/CONFIG` 命令来查看完整的配置信息,从而方便检查所对应的配置文件。
<!-- TOC -->
- [配置文件以及权重命名规则](#配置文件以及权重命名规则)
- [配置文件结构](#配置文件结构)
- [继承并修改配置文件](#继承并修改配置文件)
- [使用配置文件里的中间变量](#使用配置文件里的中间变量)
- [忽略基础配置文件里的部分内容](#忽略基础配置文件里的部分内容)
- [引用基础配置文件里的变量](#引用基础配置文件里的变量)
- [通过命令行参数修改配置信息](#通过命令行参数修改配置信息)
- [导入用户自定义模块](#导入用户自定义模块)
- [常见问题](#常见问题)
<!-- TOC -->
## 配置文件以及权重命名规则
MMClassification 按照以下风格进行配置文件命名,代码库的贡献者需要遵循相同的命名规则。文件名总体分为四部分:算法信息,模块信息,训练信息和数据信息。逻辑上属于不同部分的单词之间用下划线 `'_'` 连接,同一部分有多个单词用短横线 `'-'` 连接。
```
{algorithm info}_{module info}_{training info}_{data info}.py
```
- `algorithm info`:算法信息,算法名称或者网络架构,如 resnet 等;
- `module info` 模块信息,因任务而异,用以表示一些特殊的 neck、head 和 pretrain 信息;
- `training info`:一些训练信息,训练策略设置,包括 batch sizeschedule 数据增强等;
- `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 个 epoch
- `autoaug-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` 大小图片训练。
```{note}
部分配置文件目前还没有遵循此命名规范,相关文件命名近期会更新。
```
### 权重命名规则
权重的命名主要包括配置文件名,日期和哈希值。
```
{config_name}_{date}-{hash}.pth
```
## 配置文件结构
`configs/_base_` 文件夹下有 4 个基本组件类型,分别是:
- [模型(model)](https://github.com/open-mmlab/mmclassification/tree/master/configs/_base_/models)
- [数据(data)](https://github.com/open-mmlab/mmclassification/tree/master/configs/_base_/datasets)
- [训练策略(schedule)](https://github.com/open-mmlab/mmclassification/tree/master/configs/_base_/schedules)
- [运行设置(runtime)](https://github.com/open-mmlab/mmclassification/blob/master/configs/_base_/default_runtime.py)
你可以通过继承一些基本配置文件轻松构建自己的训练配置文件。由来自`_base_` 的组件组成的配置称为 _primitive_
为了帮助用户对 MMClassification 检测系统中的完整配置和模块有一个基本的了解,我们使用 [ResNet50 原始配置文件](https://github.com/open-mmlab/mmclassification/blob/master/configs/resnet/resnet50_8xb32_in1k.py) 作为案例进行说明并注释每一行含义。更详细的用法和各个模块对应的替代方案,请参考 API 文档。
```python
_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 文档](https://mmclassification.readthedocs.io/zh_CN/latest/api/models.html#classifier)。
- `backbone` 主干网类型,可用选项参考 [API 文档](https://mmclassification.readthedocs.io/zh_CN/latest/api/models.html#backbones)。
- `neck` 颈网络类型,目前 MMClassification 只支持 `GlobalAveragePooling` 参考 [API 文档](https://mmclassification.readthedocs.io/zh_CN/latest/api/models.html#necks)。
- `head` 头网络类型, 包括单标签分类与多标签分类头网络,可用选项参考 [API 文档](https://mmclassification.readthedocs.io/zh_CN/latest/api/models.html#heads)。
- `loss` 损失函数类型, 支持 `CrossEntropyLoss`, [`LabelSmoothLoss`](https://github.com/open-mmlab/mmclassification/blob/master/configs/_base_/models/resnet50_label_smooth.py) 等,可用选项参考 [API 文档](https://mmclassification.readthedocs.io/zh_CN/latest/api/models.html#losses)。
- `train_cfg` :训练配置, 支持 [`mixup`](https://github.com/open-mmlab/mmclassification/blob/master/configs/_base_/models/resnet50_mixup.py), [`cutmix`](https://github.com/open-mmlab/mmclassification/blob/master/configs/_base_/models/resnet50_cutmix.py) 等训练增强。
```{note}
配置文件中的 'type' 不是构造时的参数,而是类名。
```
```python
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=4backbone包含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 Size
- `workers_per_gpu` : 构建 dataloader 时,每个 GPU 的 线程数
- `train val test` : 构造数据集
- `type` : 数据集类型, MMClassification 支持 `ImageNet``Cifar` 等 ,参考[API 文档](https://mmclassification.readthedocs.io/zh_CN/latest/api/datasets.html)
- `data_prefix` : 数据集根目录
- `pipeline` : 数据处理流水线,参考相关教程文档 [如何设计数据处理流水线](https://mmclassification.readthedocs.io/zh_CN/latest/tutorials/data_pipeline.html)
评估参数 `evaluation` 也是一个字典, 为 `evaluation hook` 的配置信息, 主要包括评估间隔、评估指标等。
```python
# 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](https://mmcv.readthedocs.io/zh_CN/latest/_modules/mmcv/runner/optimizer/default_constructor.html#DefaultOptimizerConstructor) 文档
- `optimizer_config` : `optimizer hook` 的配置文件,如设置梯度限制,参考相关 [mmcv](https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/optimizer.py#L8) 代码
- `lr_config` : 学习率策略,支持 "CosineAnnealing"、 "Step"、 "Cyclic" 等等,参考相关 [mmcv](https://mmcv.readthedocs.io/zh_CN/latest/_modules/mmcv/runner/hooks/lr_updater.html#LrUpdaterHook) 文档
- `runner` : 有关 `runner` 可以参考 `mmcv` 对于 [`runner`](https://mmcv.readthedocs.io/zh_CN/latest/understand_mmcv/runner.html) 介绍文档
```python
# 用于构建优化器的配置文件。支持 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`
```
### 运行设置
本部分主要包括保存权重策略、日志配置、训练参数、断点权重路径和工作目录等等。
```python
# 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` 文件中写入以下内容:
```python
_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` 这些中间变量。
```python
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](https://mmcv.readthedocs.io/zh_CN/latest/understand_mmcv/config.html#inherit-from-base-config-with-ignored-fields) 来获得一些简单的指导。
以下是一个简单应用案例。 如果在上述 ResNet50 案例中 使用 cosine schedule ,使用继承并直接修改会报 `get unexcepected keyword 'step'` 错, 因为基础配置文件 lr_config 域信息的 `'step'` 字段被保留下来了,需要加入 `_delete_=True` 去忽略基础配置文件里的 `lr_config` 相关域内容:
```python
_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](https://mmcv.readthedocs.io/zh_CN/latest/understand_mmcv/config.html#reference-variables-from-base) 来获得一些简单的指导。
以下是一个简单应用案例,在训练数据预处理流水线中使用 auto augment 数据增强,参考配置文件 [`configs/_base_/datasets/imagenet_bs64_autoaug.py`](https://github.com/open-mmlab/mmclassification/blob/master/configs/_base_/datasets/imagenet_bs64_autoaug.py)。 在定义 `train_pipeline` 时,可以直接在 `_base_` 中加入定义 auto augment 数据增强的文件命名,再通过 `{{_base_.auto_increasing_policies}}` 引用变量:
```python
_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)]"`。注意这里的引号 " 对于列表以及元组数据类型的修改是必要的,
并且 **不允许** 引号内所指定的值的书写存在空格。
## 导入用户自定义模块
```{note}
本部分仅在当将 MMClassification 当作库构建自己项目时可能用到,初学者可跳过。
```
在学习完后续教程 [如何添加新数据集](https://mmclassification.readthedocs.io/zh_CN/latest/tutorials/new_dataset.html)、[如何设计数据处理流程](https://mmclassification.readthedocs.io/zh_CN/latest/tutorials/data_pipeline.html) 、[如何增加新模块](https://mmclassification.readthedocs.io/zh_CN/latest/tutorials/new_modules.html) 后,您可能使用 MMClassification 完成自己的项目并在项目中自定义了数据集、模型、数据增强等。为了精简代码,可以将 MMClassification 作为一个第三方库,只需要保留自己的额外的代码,并在配置文件中导入自定义的模块。案例可以参考 [OpenMMLab 算法大赛项目](https://github.com/zhangrui-wolf/openmmlab-competition-2021)。
只需要在你的配置文件中添加以下代码:
```python
custom_imports = dict(
imports=['your_dataset_class',
'your_transforme_class',
'your_model_class',
'your_module_class'],
allow_failed_imports=False)
```
## 常见问题
-