CodeCamp #144 [Doc] Chinese version of config tutorial (2371)

* [Doc]Translate the 1_config.md and modify a wrong statement in 1_config.md

* Translate the 1_config.md and modify a wrong statement in 1_config.md

* Modify some expressions

* Apply suggestions from code review
pull/2448/head
pofengdenihong 2022-12-02 16:46:41 +08:00 committed by MeowZheng
parent 750bb4f180
commit 665fa4c736
2 changed files with 462 additions and 262 deletions

View File

@ -575,7 +575,7 @@ Config (path: demo_config.py): {'backbone': {'type': 'ResNetV1c', 'depth': 101,
In addition, if the original type is tuple, it will be automatically converted to list after this way.
```shell
python demo_script.py demo_config.py --cfg-options backbone.strides="(1, 1, 1, 1)"
python demo_script.py demo_config.py --cfg-options backbone.strides=1,1,1,1
```
```shell
@ -583,6 +583,6 @@ Config (path: demo_config.py): {'backbone': {'type': 'ResNetV1c', 'depth': 101,
```
```{note}
This modification of only supports modifying configuration items of string, int, float, boolean, None, list and tuple types.
This modification method only supports modifying configuration items of string, int, float, boolean, None, list and tuple types.
More specifically, for list and tuple types, the elements inside them must also be one of the above seven types.
```

View File

@ -1,266 +1,266 @@
# 学习配置文件(待更新)
# 教程1了解配置文件
我们整合了模块和继承设计到我们的配置里,这便于做很多实验。如果您想查看配置文件,您可以运行 `python tools/print_config.py /PATH/TO/CONFIG` 去查看完整的配置文件。您还可以传递参数
`--cfg-options xxx.yyy=zzz` 去查看更新的配置。
我们将模块化和继承性设计融入到我们的配置文件系统中,方便进行各种实验。如果您想查看配置文件,你可以运行 `python tools/misc/print_config.py /PATH/TO/CONFIG` 来查看完整的配置文件。你也可以通过传递参数 `--cfg-options xxx.yyy=zzz` 来查看更新的配置信息。
## 配置文件的结构
`config/_base_` 文件夹下面有4种基本组件类型 数据集(dataset),模型(model),训练策略(schedule)和运行时的默认设置(default runtime)。许多方法都可以方便地通过组合这些组件进行实现。
这样,像 DeepLabV3, PSPNet 这样的模型可以容易地被构造。被来自 `_base_` 下的组件来构建的配置叫做 _原始配置 (primitive)_
`config/_base_ ` 文件夹下面有4种基本组件类型 数据集(dataset),模型(model),训练策略(schedule)和运行时的默认设置(default runtime)。许多模型都可以很容易地通过组合这些组件进行实现,比如 DeepLabV3PSPNet。使用 `_base_` 下的组件构建的配置信息叫做原始配置 (primitive)。
对于所有在同一个文件夹下的配置文件,推荐**只有一个**对应的**原始配置**文件。所有其他的配置文件都应该继承自这个**原始配置**文件。这样就能保证配置文件的最大继承深度为 3。
对于同一个文件夹下的所有配置文件,建议**只有一个**对应的**原始配置文件**。所有其他的配置文件都应该继承自这个原始配置文件,从而保证每个配置文件的最大继承深度为 3。
为了便于理解,我们推荐社区贡献者继承已有的方法配置文件。
例如,如果一些修改是基于 DeepLabV3使用者首先首先应该通过指定 `_base_ = ../deeplabv3/deeplabv3_r50_512x1024_40ki_cityscapes.py`来继承基础 DeepLabV3 结构,再去修改配置文件里其他内容以完成继承。
为了便于理解,我们建议社区贡献者从现有的方法继承。例如,如果您在 DeepLabV3 基础上进行了一些修改,用户可以先通过指定 `_base_ = ../deeplabv3/deeplabv3_r50-d8_4xb2-40k_cityscapes-512x1024.py` 继承基本的 DeepLabV3 结构,然后在配置文件中修改必要的字段。
如果您正在构建一个完整的新模型,它完全没有和已有的方法共享一些结构,您可能需要在 `configs` 下面创建一个文件夹 `xxxnet`
更详细的文档,请参照 [mmcv](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html) 。
如果你正在构建一个全新的方法,它不与现有的任何方法共享基本组件,您可以在`config`下创建一个新的文件夹`xxxnet` ,详细文档请参考[mmengine](https://mmengine.readthedocs.io/en/latest/tutorials/config.html)。
## 配置文件命名风格
我们按照下面的风格去命名配置文件,社区贡献者被建议使用同样的风格。
我们遵循以下格式来命名配置文件,建议社区贡献者遵循相同的风格。
```
{model}_{backbone}_[misc]_[gpu x batch_per_gpu]_{resolution}_{iterations}_{dataset}
```text
{algorithm name}_{model component names [component1]_[component2]_[...]}_{training settings}_{training dataset information}_{testing dataset information}
```
`{xxx}` 是被要求的文件 `[yyy]` 是可选的
配置文件的文件名分为五个部分,组成文件名每一个部分和组件之间都用`_`连接,每个部分或组件中的每个单词都要用`-`连接
- `{model}`: 模型种类,例如 `psp` `deeplabv3` 等等
- `{backbone}`: 主干网络种类,例如 `r50` (ResNet-50) `x101` (ResNeXt-101)
- `[misc]`: 模型中各式各样的设置/插件,例如 `dconv` `gcb` `attention` `mstrain`
- `[gpu x batch_per_gpu]`: GPU数目 和每个 GPU 的样本数, 默认为 `8x2`
- `{iterations}`: 训练迭代轮数,如`160k`
- `{dataset}`: 数据集,如 `cityscapes` `voc12aug` `ade`
- `{algorithm name}`: 算法的名称,如 `deeplabv3`, `pspnet` 等。
- `{model component names}`: 算法中使用的组件名称,如主干(backbone)、解码头(head)等。例如,`r50-d8 `表示使用ResNet50主干网络并使用主干网络的8倍下采样输出作为下一级的输入。
- `{training settings}`: 训练时的参数设置,如 `batch size`、数据增强(augmentation)、损失函数(loss)、学习率调度器(learning rate scheduler)和训练轮数(epochs/iterations)。例如: `4xb4-ce-linearlr-40K` 意味着使用4个gpu每个gpu4个图像使用交叉熵损失函数(CrossEntropy)线性学习率调度程序训练40K iterations。
一些缩写:
- `{gpu x batch_per_gpu}`: GPU数量和每个GPU的样本数。`bN ` 表示每个GPU的batch size为N如 `8xb2` 为8个gpu x 每个gpu2张图像的缩写。如果未提及则默认使用 `4xb4 `
- `{schedule}`: 训练计划,选项有`20k``40k`等。`20k ` 和 `40k` 分别表示20000次迭代(iterations)和40000次迭代(iterations)。
- `{training dataset information}`: 训练数据集名称,如 `cityscapes ` `ade20k ` 等,以及输入分辨率。例如: `cityscapes-768x768 `表示使用 `cityscapes` 数据集进行训练,输入分辨率为`768x768 `。
- `{testing dataset information}` (可选): 测试数据集名称。当您的模型在一个数据集上训练但在另一个数据集上测试时,请将测试数据集名称添加到此处。如果没有这一部分,则意味着模型是在同一个数据集上进行训练和测试的。
## PSPNet 的一个例子
为了帮助使用者熟悉这个流行的语义分割框架的完整配置文件和模块,我们在下面对使用 ResNet50V1c 的 PSPNet 的配置文件做了详细的注释说明。
更多的详细使用和其他模块的替代项请参考 API 文档。
为了帮助用户熟悉对这个现代语义分割系统的完整配置文件和模块我们对使用ResNet50V1c作为主干网络的PSPNet的配置文件作如下的简要注释和说明。要了解更详细的用法和每个模块对应的替换方法请参阅API文档。
```python
_base_ = [
'../_base_/models/pspnet_r50-d8.py', '../_base_/datasets/cityscapes.py',
'../_base_/default_runtime.py', '../_base_/schedules/schedule_40k.py'
] # 我们可以在基本配置文件的基础上 构建新的配置文件
crop_size = (512, 1024)
data_preprocessor = dict(size=crop_size)
model = dict(data_preprocessor=data_preprocessor)
```
`_base_/models/pspnet_r50-d8.py`是使用ResNet50V1c作为主干网络的PSPNet的基本模型配置文件。
```python
# 模型设置
norm_cfg = dict(type='SyncBN', requires_grad=True) # 分割框架通常使用 SyncBN
data_preprocessor = dict( # 数据预处理的配置项,通常包括图像的归一化和增强
type='SegDataPreProcessor', # 数据预处理的类型
mean=[123.675, 116.28, 103.53], # 用于归一化输入图像的平均值
std=[58.395, 57.12, 57.375], # 用于归一化输入图像的标准差
bgr_to_rgb=True, # 是否将图像从 BGR 转为 RGB
pad_val=0, # 图像的填充值
seg_pad_val=255) # 'gt_seg_map'的填充值
model = dict(
type='EncoderDecoder', # 分割器(segmentor)的名字
pretrained='open-mmlab://resnet50_v1c', # 将被加载的 ImageNet 预训练主干网络
data_preprocessor=data_preprocessor,
pretrained='open-mmlab://resnet50_v1c', # 加载使用 ImageNet 预训练的主干网络
backbone=dict(
type='ResNetV1c', # 主干网络的类别。 可用选项请参考 mmseg/models/backbones/resnet.py
depth=50, # 主干网络的深度。通常为 50 和 101。
num_stages=4, # 主干网络状态(stages)的数目,这些状态产生的特征图作为后续的 head 的输入。
out_indices=(0, 1, 2, 3), # 每个状态产生的特征图输出的索引。
dilations=(1, 1, 2, 4), # 每一层(layer)的空心率(dilation rate)。
strides=(1, 2, 1, 1), # 每一层(layer)的步长(stride)。
norm_cfg=dict( # 归一化层(norm layer)的配置项。
type='SyncBN', # 归一化层的类别。通常是 SyncBN。
requires_grad=True), # 是否训练归一化里的 gamma 和 beta。
norm_eval=False, # 是否冻结 BN 里的统计项。
style='pytorch', # 主干网络的风格,'pytorch' 意思是步长为2的层为 3x3 卷积, 'caffe' 意思是步长为2的层为 1x1 卷积。
contract_dilation=True), # 当空洞 > 1, 是否压缩第一个空洞层。
type='ResNetV1c', # 主干网络的类别,更多细节请参考 mmseg/models/backbones/resnet.py
depth=50, # 主干网络的深度,通常为 50 和 101
num_stages=4, # 主干网络状态(stages)的数目
out_indices=(0, 1, 2, 3), # 每个状态(stage)产生的特征图输出的索引
dilations=(1, 1, 2, 4), # 每一层(layer)的空心率(dilation rate)
strides=(1, 2, 1, 1), # 每一层(layer)的步长(stride)
norm_cfg=norm_cfg, # 归一化层(norm layer)的配置项
norm_eval=False, # 是否冻结 BN 里的统计项
style='pytorch', # 主干网络的风格,'pytorch' 意思是步长为2的层为 3x3 卷积, 'caffe' 意思是步长为2的层为 1x1 卷积
contract_dilation=True), # 当空洞率 > 1, 是否压缩第一个空洞层
decode_head=dict(
type='PSPHead', # 解码头(decode head)的类别。 可用选项请参 mmseg/models/decode_heads
in_channels=2048, # 解码头的输入通道数
in_index=3, # 被选择特征图(feature map)的索引
channels=512, # 解码头中间态(intermediate)的通道数
pool_scales=(1, 2, 3, 6), # PSPHead 平均池化(avg pooling)的规模(scales)。 细节请参考文章内容
dropout_ratio=0.1, # 进入最后分类层(classification layer)之前的 dropout 比例
num_classes=19, # 分割前景的种类数目。 通常情况下cityscapes 为19VOC为21ADE20k 为150
norm_cfg=dict(type='SyncBN', requires_grad=True), # 归一化层的配置项。
align_corners=False, # 解码里调整大小(resize)的 align_corners 参数。
loss_decode=dict( # 解码头(decode_head)里的损失函数的配置项
type='CrossEntropyLoss', # 在分割里使用的损失函数的类别。
use_sigmoid=False, # 在分割里是否使用 sigmoid 激活。
loss_weight=1.0)), # 解码头里损失的权重。
type='PSPHead', # 解码头(decode head)的类别。可用选项请参 mmseg/models/decode_heads
in_channels=2048, # 解码头的输入通道数
in_index=3, # 被选择特征图(feature map)的索引
channels=512, # 解码头中间态(intermediate)的通道数
pool_scales=(1, 2, 3, 6), # PSPHead 平均池化(avg pooling)的规模(scales)。 细节请参考文章内容
dropout_ratio=0.1, # 进入最后分类层(classification layer)之前的 dropout 比例
num_classes=19, # 分割前景的种类数目。 通常情况下cityscapes 为19VOC为21ADE20k 为150
norm_cfg=norm_cfg, # 归一化层的配置项
align_corners=False, # 解码过程中调整大小(resize)的 align_corners 参数
loss_decode=dict( # 解码头(decode_head)里的损失函数的配置项
type='CrossEntropyLoss', # 分割时使用的损失函数的类别
use_sigmoid=False, # 分割时是否使用 sigmoid 激活
loss_weight=1.0)), # 解码头的损失权重
auxiliary_head=dict(
type='FCNHead', # 辅助头(auxiliary head)的种类。可用选项请参考 mmseg/models/decode_heads。
in_channels=1024, # 辅助头的输入通道数。
in_index=2, # 被选择的特征图(feature map)的索引。
channels=256, # 辅助头中间态(intermediate)的通道数。
num_convs=1, # FCNHead 里卷积(convs)的数目. 辅助头里通常为1。
concat_input=False, # 在分类层(classification layer)之前是否连接(concat)输入和卷积的输出。
dropout_ratio=0.1, # 进入最后分类层(classification layer)之前的 dropout 比例。
num_classes=19, # 分割前景的种类数目。 通常情况下cityscapes 为19VOC为21ADE20k 为150。
norm_cfg=dict(type='SyncBN', requires_grad=True), # 归一化层的配置项。
align_corners=False, # 解码里调整大小(resize)的 align_corners 参数。
loss_decode=dict( # 辅助头(auxiliary head)里的损失函数的配置项。
type='CrossEntropyLoss', # 在分割里使用的损失函数的类别。
use_sigmoid=False, # 在分割里是否使用 sigmoid 激活。
loss_weight=0.4))) # 辅助头里损失的权重。默认设置为0.4。
train_cfg = dict() # train_cfg 当前仅是一个占位符。
test_cfg = dict(mode='whole') # 测试模式, 选项是 'whole' 和 'sliding'. 'whole': 整张图像全卷积(fully-convolutional)测试。 'sliding': 图像上做滑动裁剪窗口(sliding crop window)。
dataset_type = 'CityscapesDataset' # 数据集类型,这将被用来定义数据集。
data_root = 'data/cityscapes/' # 数据的根路径。
img_norm_cfg = dict( # 图像归一化配置,用来归一化输入的图像。
mean=[123.675, 116.28, 103.53], # 预训练里用于预训练主干网络模型的平均值。
std=[58.395, 57.12, 57.375], # 预训练里用于预训练主干网络模型的标准差。
to_rgb=True) # 预训练里用于预训练主干网络的图像的通道顺序。
type='FCNHead', # 辅助头(auxiliary head)的种类。可用选项请参考 mmseg/models/decode_heads
in_channels=1024, # 辅助头的输入通道数
in_index=2, # 被选择的特征图(feature map)的索引
channels=256, # 辅助头中间态(intermediate)的通道数
num_convs=1, # FCNHead 里卷积(convs)的数目辅助头中通常为1
concat_input=False, # 在分类层(classification layer)之前是否连接(concat)输入和卷积的输出
dropout_ratio=0.1, # 进入最后分类层(classification layer)之前的 dropout 比例
num_classes=19, # 分割前景的种类数目。 通常情况下cityscapes 为19VOC为21ADE20k 为150
norm_cfg=norm_cfg, # 归一化层的配置项
align_corners=False, # 解码过程中调整大小(resize)的 align_corners 参数
loss_decode=dict( # 辅助头(auxiliary head)里的损失函数的配置项
type='CrossEntropyLoss', # 分割时使用的损失函数的类别
use_sigmoid=False, # 分割时是否使用 sigmoid 激活
loss_weight=0.4)), # 辅助头损失的权重默认设置为0.4
# 模型训练和测试设置项
train_cfg=dict(), # train_cfg 当前仅是一个占位符
test_cfg=dict(mode='whole')) # 测试模式,可选参数为 'whole' 和 'slide'. 'whole': 在整张图像上全卷积(fully-convolutional)测试。 'slide': 在输入图像上做滑窗预测
```
`_base_/datasets/cityscapes.py`是数据集的基本配置文件。
```python
# 数据集设置
dataset_type = 'CityscapesDataset' # 数据集类型,这将被用来定义数据集
data_root = 'data/cityscapes/' # 数据的根路径
crop_size = (512, 1024) # 训练时的裁剪大小
train_pipeline = [ #训练流程
dict(type='LoadImageFromFile'), # 第1个流程从文件路径里加载图像。
dict(type='LoadAnnotations'), # 第2个流程对于当前图像加载它的注释信息。
dict(type='Resize', # 变化图像和其注释大小的数据增广的流程。
img_scale=(2048, 1024), # 图像的最大规模。
ratio_range=(0.5, 2.0)), # 数据增广的比例范围。
dict(type='RandomCrop', # 随机裁剪当前图像和其注释大小的数据增广的流程。
crop_size=(512, 1024), # 随机裁剪图像生成 patch 的大小。
cat_max_ratio=0.75), # 单个类别可以填充的最大区域的比例。
dict(
type='RandomFlip', # 翻转图像和其注释大小的数据增广的流程。
flip_ratio=0.5), # 翻转图像的概率
dict(type='PhotoMetricDistortion'), # 光学上使用一些方法扭曲当前图像和其注释的数据增广的流程。
dict(
type='Normalize', # 归一化当前图像的数据增广的流程。
mean=[123.675, 116.28, 103.53], # 这些键与 img_norm_cfg 一致,因为 img_norm_cfg 被
std=[58.395, 57.12, 57.375], # 用作参数。
to_rgb=True),
dict(type='Pad', # 填充当前图像到指定大小的数据增广的流程。
size=(512, 1024), # 填充的图像大小。
pad_val=0, # 图像的填充值。
seg_pad_val=255), # 'gt_semantic_seg'的填充值。
dict(type='DefaultFormatBundle'), # 流程里收集数据的默认格式捆。
dict(type='Collect', # 决定数据里哪些键被传递到分割器里的流程。
keys=['img', 'gt_semantic_seg'])
train_pipeline = [ # 训练流程
dict(type='LoadImageFromFile'), # 第1个流程从文件路径里加载图像
dict(type='LoadAnnotations'), # 第2个流程对于当前图像加载它的标注图像
dict(type='RandomResize', # 调整输入图像大小(resize)和其标注图像的数据增广流程
scale=(2048, 1024), # 图像裁剪的大小
ratio_range=(0.5, 2.0), # 数据增广的比例范围
keep_ratio=True), # 调整图像大小时是否保持纵横比
dict(type='RandomCrop', # 随机裁剪当前图像和其标注图像的数据增广流程
crop_size=crop_size, # 随机裁剪的大小
cat_max_ratio=0.75), # 单个类别可以填充的最大区域的比
dict(type='RandomFlip', # 翻转图像和其标注图像的数据增广流程
prob=0.5), # 翻转图像的概率
dict(type='PhotoMetricDistortion'), # 光学上使用一些方法扭曲当前图像和其标注图像的数据增广流程
dict(type='PackSegInputs') # 打包用于语义分割的输入数据
]
test_pipeline = [
dict(type='LoadImageFromFile'), # 第1个流程从文件路径里加载图像。
dict(
type='MultiScaleFlipAug', # 封装测试时数据增广(test time augmentations)。
img_scale=(2048, 1024), # 决定测试时可改变图像的最大规模。用于改变图像大小的流程。
flip=False, # 测试时是否翻转图像。
transforms=[
dict(type='Resize', # 使用改变图像大小的数据增广。
keep_ratio=True), # 是否保持宽和高的比例,这里的图像比例设置将覆盖上面的图像规模大小的设置。
dict(type='RandomFlip'), # 考虑到 RandomFlip 已经被添加到流程里,当 flip=False 时它将不被使用。
dict(
type='Normalize', # 归一化配置项,值来自 img_norm_cfg。
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', # 将图像转为张量
keys=['img']),
dict(type='Collect', # 收集测试时必须的键的收集流程。
keys=['img'])
])
dict(type='LoadImageFromFile'), # 第1个流程从文件路径里加载图像
dict(type='Resize', # 使用调整图像大小(resize)增强
scale=(2048, 1024), # 图像缩放的大小
keep_ratio=True), # 在调整图像大小时是否保留长宽比
# 在' Resize '之后添加标注图像
# 不需要做调整图像大小(resize)的数据变换
dict(type='LoadAnnotations'), # 加载数据集提供的语义分割标注
dict(type='PackSegInputs') # 打包用于语义分割的输入数据
]
data = dict(
samples_per_gpu=2, # 单个 GPU 的 Batch size
workers_per_gpu=2, # 单个 GPU 分配的数据加载线程数
train=dict( # 训练数据集配置
type='CityscapesDataset', # 数据集的类别, 细节参考自 mmseg/datasets/。
data_root='data/cityscapes/', # 数据集的根目录。
img_dir='leftImg8bit/train', # 数据集图像的文件夹。
ann_dir='gtFine/train', # 数据集注释的文件夹。
pipeline=[ # 流程, 由之前创建的 train_pipeline 传递进来。
dict(type='LoadImageFromFile'),
dict(type='LoadAnnotations'),
dict(
type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)),
dict(type='RandomCrop', crop_size=(512, 1024), cat_max_ratio=0.75),
dict(type='RandomFlip', flip_ratio=0.5),
dict(type='PhotoMetricDistortion'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='Pad', size=(512, 1024), pad_val=0, seg_pad_val=255),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
]),
val=dict( # 验证数据集的配置
type='CityscapesDataset',
data_root='data/cityscapes/',
img_dir='leftImg8bit/val',
ann_dir='gtFine/val',
pipeline=[ # 由之前创建的 test_pipeline 传递的流程。
dict(type='LoadImageFromFile'),
dict(
type='MultiScaleFlipAug',
img_scale=(2048, 1024),
flip=False,
transforms=[
dict(type='Resize', keep_ratio=True),
dict(type='RandomFlip'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
])
]),
test=dict(
type='CityscapesDataset',
data_root='data/cityscapes/',
img_dir='leftImg8bit/val',
ann_dir='gtFine/val',
pipeline=[
dict(type='LoadImageFromFile'),
dict(
type='MultiScaleFlipAug',
img_scale=(2048, 1024),
flip=False,
transforms=[
dict(type='Resize', keep_ratio=True),
dict(type='RandomFlip'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
])
]))
log_config = dict( # 注册日志钩 (register logger hook) 的配置文件。
interval=50, # 打印日志的间隔
hooks=[
# dict(type='TensorboardLoggerHook') # 同样支持 Tensorboard 日志
dict(type='TextLoggerHook', by_epoch=False)
])
dist_params = dict(backend='nccl') # 用于设置分布式训练的参数,端口也同样可被设置。
log_level = 'INFO' # 日志的级别。
load_from = None # 从一个给定路径里加载模型作为预训练模型,它并不会消耗训练时间。
resume_from = None # 从给定路径里恢复检查点(checkpoints),训练模式将从检查点保存的轮次开始恢复训练。
workflow = [('train', 1)] # runner 的工作流程。 [('train', 1)] 意思是只有一个工作流程而且工作流程 'train' 仅执行一次。根据 `runner.max_iters` 工作流程训练模型的迭代轮数为40000次。
cudnn_benchmark = True # 是否是使用 cudnn_benchmark 去加速,它对于固定输入大小的可以提高训练速度。
optimizer = dict( # 用于构建优化器的配置文件。支持 PyTorch 中的所有优化器同时它们的参数与PyTorch里的优化器参数一致。
type='SGD', # 优化器种类,更多细节可参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/optimizer/default_constructor.py#L13。
lr=0.01, # 优化器的学习率,参数的使用细节请参照对应的 PyTorch 文档。
momentum=0.9, # 动量 (Momentum)
weight_decay=0.0005) # SGD 的衰减权重 (weight decay)。
optimizer_config = dict() # 用于构建优化器钩 (optimizer hook) 的配置文件,执行细节请参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/optimizer.py#L8。
lr_config = dict(
policy='poly', # 调度流程的策略,同样支持 Step, CosineAnnealing, Cyclic 等. 请从 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/lr_updater.py#L9 参考 LrUpdater 的细节。
power=0.9, # 多项式衰减 (polynomial decay) 的幂。
min_lr=0.0001, # 用来稳定训练的最小学习率。
by_epoch=False) # 是否按照每个 epoch 去算学习率。
runner = dict(
type='IterBasedRunner', # 将使用的 runner 的类别 (例如 IterBasedRunner 或 EpochBasedRunner)。
max_iters=40000) # 全部迭代轮数大小,对于 EpochBasedRunner 使用 `max_epochs`
checkpoint_config = dict( # 设置检查点钩子 (checkpoint hook) 的配置文件。执行时请参考 https://github.com/open-mmlab/mmcv/blob/master/mmcv/runner/hooks/checkpoint.py。
by_epoch=False, # 是否按照每个 epoch 去算 runner。
interval=4000) # 保存的间隔
evaluation = dict( # 构建评估钩 (evaluation hook) 的配置文件。细节请参考 mmseg/core/evaluation/eval_hook.py。
interval=4000, # 评估的间歇点
metric='mIoU') # 评估的指标
train_dataloader = dict( # 训练数据加载器(dataloader)的配置
batch_size=2, # 每一个GPU的batch size大小
num_workers=2, # 为每一个GPU预读取数据的进程个数
persistent_workers=True, # 在一个epoch结束后关闭worker进程可以加快训练速度
sampler=dict(type='InfiniteSampler', shuffle=True), # 训练时进行随机洗牌(shuffle)
dataset=dict( # 训练数据集配置
type=dataset_type, # 数据集类型详见mmseg/datassets/
data_root=data_root, # 数据集的根目录
data_prefix=dict(
img_path='leftImg8bit/train', seg_map_path='gtFine/train'), # 训练数据的前缀
pipeline=train_pipeline)) # 数据处理流程它通过之前创建的train_pipeline传递。
val_dataloader = dict(
batch_size=1, # 每一个GPU的batch size大小
num_workers=4, # 为每一个GPU预读取数据的进程个数
persistent_workers=True, # 在一个epoch结束后关闭worker进程可以加快训练速度
sampler=dict(type='DefaultSampler', shuffle=False), # 训练时不进行随机洗牌(shuffle)
dataset=dict( # 测试数据集配置
type=dataset_type, # 数据集类型详见mmseg/datassets/
data_root=data_root, # 数据集的根目录
data_prefix=dict(
img_path='leftImg8bit/val', seg_map_path='gtFine/val'), # 测试数据的前缀
pipeline=test_pipeline)) # 数据处理流程它通过之前创建的test_pipeline传递。
test_dataloader = val_dataloader
# 精度评估方法,我们在这里使用 IoUMetric 进行评估
val_evaluator = dict(type='IoUMetric', iou_metrics=['mIoU'])
test_evaluator = val_evaluator
```
`_base_/schedules/schedule_40k.py`
```python
# optimizer
optimizer = dict(type='SGD', # 优化器种类,更多细节可参考 https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/optimizer/default_constructor.py
lr=0.01, # 优化器的学习率,参数的使用细节请参照对应的 PyTorch 文档
momentum=0.9, # 动量大小 (Momentum)
weight_decay=0.0005) # SGD 的权重衰减 (weight decay)
optim_wrapper = dict(type='OptimWrapper', # 优化器包装器(Optimizer wrapper)为更新参数提供了一个公共接口
optimizer=optimizer, # 用于更新模型参数的优化器(Optimizer)
clip_grad=None) # 如果 'clip_grad' 不是None它将是 ' torch.nn.utils.clip_grad' 的参数。
# 学习策略
param_scheduler = [
dict(
type='PolyLR', # 调度流程的策略,同样支持 Step, CosineAnnealing, Cyclic 等. 请从 https://github.com/open-mmlab/mmengine/blob/main/mmengine/optim/scheduler/lr_scheduler.py 参考 LrUpdater 的细节
eta_min=1e-4, # 训练结束时的最小学习率
power=0.9, # 多项式衰减 (polynomial decay) 的幂
begin=0, # 开始更新参数的时间步(step)
end=40000, # 停止更新参数的时间步(step)
by_epoch=False) # 是否按照 epoch 计算训练时间
]
# 40k iteration 的训练计划
train_cfg = dict(type='IterBasedTrainLoop', max_iters=40000, val_interval=4000)
val_cfg = dict(type='ValLoop')
test_cfg = dict(type='TestLoop')
# 默认钩子(hook)配置
default_hooks = dict(
timer=dict(type='IterTimerHook'), # 记录迭代过程中花费的时间
logger=dict(type='LoggerHook', interval=50, log_metric_by_epoch=False), # 从'Runner'的不同组件收集和写入日志
param_scheduler=dict(type='ParamSchedulerHook'), # 更新优化器中的一些超参数,例如学习率
checkpoint=dict(type='CheckpointHook', by_epoch=False, interval=4000), # 定期保存检查点(checkpoint)
sampler_seed=dict(type='DistSamplerSeedHook')) # 用于分布式训练的数据加载采样器
```
in `_base_/default_runtime.py`
```python
# 将注册表的默认范围设置为mmseg
default_scope = 'mmseg'
# environment
env_cfg = dict(
cudnn_benchmark=True,
mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
dist_cfg=dict(backend='nccl'),
)
log_level = 'INFO'
log_processor = dict(by_epoch=False)
load_from = None # 从文件中加载检查点(checkpoint)
resume = False # 是否从已有的模型恢复
```
这些都是用于训练和测试PSPNet的配置文件要加载和解析它们我们可以使用[MMEngine](https://github.com/open-mmlab/mmengine)实现的[Config](https://mmengine.readthedocs.io/en/latest/tutorials/config.html)。
```python
from mmengine.config import Config
cfg = Config.fromfile('configs/pspnet/pspnet_r50-d8_4xb2-40k_cityscapes-512x1024.py')
print(cfg.train_dataloader)
```
```shell
{'batch_size': 2,
'num_workers': 2,
'persistent_workers': True,
'sampler': {'type': 'InfiniteSampler', 'shuffle': True},
'dataset': {'type': 'CityscapesDataset',
'data_root': 'data/cityscapes/',
'data_prefix': {'img_path': 'leftImg8bit/train',
'seg_map_path': 'gtFine/train'},
'pipeline': [{'type': 'LoadImageFromFile'},
{'type': 'LoadAnnotations'},
{'type': 'RandomResize',
'scale': (2048, 1024),
'ratio_range': (0.5, 2.0),
'keep_ratio': True},
{'type': 'RandomCrop', 'crop_size': (512, 1024), 'cat_max_ratio': 0.75},
{'type': 'RandomFlip', 'prob': 0.5},
{'type': 'PhotoMetricDistortion'},
{'type': 'PackSegInputs'}]}}
```
`cfg `是`mmengine.config.Config `的一个实例。它的接口与dict对象相同也允许将配置值作为属性访问。更多信息请参见[MMEngine](https://github.com/open-mmlab/mmengine)中的[config tutorial](https://mmengine.readthedocs.io/en/latest/tutorials/config.html)。
## FAQ
### 忽略基础配置文件里的一些域内容。
### 忽略基础配置文件里的一些字段
有时,您也许会设置 `_delete_=True` 去忽略基础配置文件里的一些域内容。
您也许可以参照 [mmcv](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html#inherit-from-base-config-with-ignored-fields) 来获得一些简单的指导。
有时,您可以设置`_delete_=True `来忽略基本配置文件中的某些字段。您可以参考[MMEngine](https://github.com/open-mmlab/mmengine)中的[config tutorial](https://mmengine.readthedocs.io/en/latest/tutorials/config.html)来获得一些简单的指导。
在 MMSegmentation 里,例如为了改变 PSPNet 的主干网络的某些内容:
例如在MMSegmentation中如果您想在下面的配置文件`pspnet.py `中修改PSPNet的主干网络:
```python
norm_cfg = dict(type='SyncBN', requires_grad=True)
model = dict(
type='MaskRCNN',
type='EncoderDecoder',
pretrained='torchvision://resnet50',
backbone=dict(
type='ResNetV1c',
@ -273,14 +273,60 @@ model = dict(
norm_eval=False,
style='pytorch',
contract_dilation=True),
decode_head=dict(...),
auxiliary_head=dict(...))
decode_head=dict(
type='PSPHead',
in_channels=2048,
in_index=3,
channels=512,
pool_scales=(1, 2, 3, 6),
dropout_ratio=0.1,
num_classes=19,
norm_cfg=norm_cfg,
align_corners=False,
loss_decode=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)))
```
`ResNet``HRNet` 使用不同的关键词去构建。
用以下代码加载并解析配置文件`pspnet.py`:
```python
_base_ = '../pspnet/psp_r50_512x1024_40ki_cityscpaes.py'
from mmengine.config import Config
cfg = Config.fromfile('pspnet.py')
print(cfg.model)
```
```shell
{'type': 'EncoderDecoder',
'pretrained': 'torchvision://resnet50',
'backbone': {'type': 'ResNetV1c',
'depth': 50,
'num_stages': 4,
'out_indices': (0, 1, 2, 3),
'dilations': (1, 1, 2, 4),
'strides': (1, 2, 1, 1),
'norm_cfg': {'type': 'SyncBN', 'requires_grad': True},
'norm_eval': False,
'style': 'pytorch',
'contract_dilation': True},
'decode_head': {'type': 'PSPHead',
'in_channels': 2048,
'in_index': 3,
'channels': 512,
'pool_scales': (1, 2, 3, 6),
'dropout_ratio': 0.1,
'num_classes': 19,
'norm_cfg': {'type': 'SyncBN', 'requires_grad': True},
'align_corners': False,
'loss_decode': {'type': 'CrossEntropyLoss',
'use_sigmoid': False,
'loss_weight': 1.0}}}
```
`ResNet`和`HRNet`使用不同的关键字构建,编写一个新的配置文件`hrnet.py`,如下所示:
```python
_base_ = 'pspnet.py'
norm_cfg = dict(type='SyncBN', requires_grad=True)
model = dict(
pretrained='open-mmlab://msra/hrnetv2_w32',
@ -312,66 +358,220 @@ model = dict(
num_branches=4,
block='BASIC',
num_blocks=(4, 4, 4, 4),
num_channels=(32, 64, 128, 256)))),
decode_head=dict(...),
auxiliary_head=dict(...))
num_channels=(32, 64, 128, 256)))))
```
`_delete_=True` 将用新的键去替换 `backbone` 域内所有老的键。
用以下代码加载并解析配置文件`hrnet.py`:
```python
from mmengine.config import Config
cfg = Config.fromfile('hrnet.py')
print(cfg.model)
```
```shell
{'type': 'EncoderDecoder',
'pretrained': 'open-mmlab://msra/hrnetv2_w32',
'backbone': {'type': 'HRNet',
'norm_cfg': {'type': 'SyncBN', 'requires_grad': True},
'extra': {'stage1': {'num_modules': 1,
'num_branches': 1,
'block': 'BOTTLENECK',
'num_blocks': (4,),
'num_channels': (64,)},
'stage2': {'num_modules': 1,
'num_branches': 2,
'block': 'BASIC',
'num_blocks': (4, 4),
'num_channels': (32, 64)},
'stage3': {'num_modules': 4,
'num_branches': 3,
'block': 'BASIC',
'num_blocks': (4, 4, 4),
'num_channels': (32, 64, 128)},
'stage4': {'num_modules': 3,
'num_branches': 4,
'block': 'BASIC',
'num_blocks': (4, 4, 4, 4),
'num_channels': (32, 64, 128, 256)}}},
'decode_head': {'type': 'PSPHead',
'in_channels': 2048,
'in_index': 3,
'channels': 512,
'pool_scales': (1, 2, 3, 6),
'dropout_ratio': 0.1,
'num_classes': 19,
'norm_cfg': {'type': 'SyncBN', 'requires_grad': True},
'align_corners': False,
'loss_decode': {'type': 'CrossEntropyLoss',
'use_sigmoid': False,
'loss_weight': 1.0}}}
```
`_delete_=True` 将用新的键去替换 `backbone` 字段内所有旧的键。
### 使用配置文件里的中间变量
配置文件里会使用一些中间变量,例如数据集里的 `train_pipeline`/`test_pipeline`。
需要注意的是,在子配置文件里修改中间变量时,使用者需要再次传递这些变量给对应的域。
例如我们想改变在训练或测试时PSPNet 的多尺度策略 (multi scale strategy)`train_pipeline`/`test_pipeline` 是我们想要修改的中间变量。
配置文件中会使用一些中间变量,例如数据集(datasets)字段里的 `train_pipeline`/`test_pipeline`。 需要注意的是在子配置文件里修改中间变量时您需要再次传递这些变量给对应的字段。例如我们想改变在训练或测试PSPNet时采用的多尺度策略 (multi scale strategy)`train_pipeline`/`test_pipeline` 是我们需要修改的中间变量。
```python
_base_ = '../pspnet/psp_r50_512x1024_40ki_cityscapes.py'
_base_ = '../pspnet/pspnet_r50-d8_4xb4-40k_cityscpaes-512x1024.py'
crop_size = (512, 1024)
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='LoadAnnotations'),
dict(type='Resize', img_scale=(2048, 1024), ratio_range=(1.0, 2.0)), # 改成 [1., 2.]
dict(type='RandomResize',
img_scale=(2048, 1024),
ratio_range=(1., 2.),
keep_ration=True),
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
dict(type='RandomFlip', flip_ratio=0.5),
dict(type='PhotoMetricDistortion'),
dict(type='Normalize', **img_norm_cfg),
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
dict(type='PackSegInputs'),
]
test_pipeline = [
dict(type='LoadImageFromFile'),
dict(
type='MultiScaleFlipAug',
img_scale=(2048, 1024),
img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75], # 改成多尺度测试 (multi scale testing)。
flip=False,
transforms=[
dict(type='Resize', keep_ratio=True),
dict(type='RandomFlip'),
dict(type='Normalize', **img_norm_cfg),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img']),
])
dict(type='Resize',
scale=(2048, 1024),
keep_ratio=True),
dict(type='LoadAnnotations'),
dict(type='PackSegInputs')
]
data = dict(
train=dict(pipeline=train_pipeline),
val=dict(pipeline=test_pipeline),
test=dict(pipeline=test_pipeline))
train_dataset=dict(
type=dataset_type,
data_root=data_root,
data_prefix=dict(
img_path='leftImg8bit/train', seg_map_path='gtFine/train'),
pipeline=train_pipeline)
test_dataset=dict(
type=dataset_type,
data_root=data_root,
data_prefix=dict(
img_path='leftImg8bit/val', seg_map_path='gtFine/val'),
pipeline=test_pipeline)
train_dataloader = dict(dataset=train_dataset)
val_dataloader = dict(dataset=test_dataset)
test_dataloader = val_dataloader
```
我们首先定义新的 `train_pipeline`/`test_pipeline` 然后传递到 `data` 里。
我们首先需要定义新的 `train_pipeline`/`test_pipeline` 然后传递到 `dataset` 里。
同样的,如果我们想从 `SyncBN` 切换到 `BN` 或者 `MMSyncBN`,我们需要配置文件里的每一个 `norm_cfg`
类似的,如果我们想从 `SyncBN` 切换到 `BN` 或者 `MMSyncBN`,我们需要替换配置文件里的每一个 `norm_cfg`
```python
_base_ = '../pspnet/psp_r50_512x1024_40ki_cityscpaes.py'
_base_ = '../pspnet/pspnet_r50-d8_4xb4-40k_cityscpaes-512x1024.py'
norm_cfg = dict(type='BN', requires_grad=True)
model = dict(
backbone=dict(norm_cfg=norm_cfg),
decode_head=dict(norm_cfg=norm_cfg),
auxiliary_head=dict(norm_cfg=norm_cfg))
```
## 通过脚本参数修改配置文件
在[training script](https://github.com/open-mmlab/mmsegmentation/blob/1.x/tools/train.py)和[testing script](https://github.com/open-mmlab/mmsegmentation/blob/1.x/tools/test.py)中,我们支持脚本参数 `--cfg-options`,它可以帮助用户覆盖所使用的配置中的一些设置,`xxx=yyy` 格式的键值对将合并到配置文件中。
例如,这是一个简化的脚本 `demo_script.py `:
```python
import argparse
from mmengine.config import Config, DictAction
def parse_args():
parser = argparse.ArgumentParser(description='Script Example')
parser.add_argument('config', help='train config file path')
parser.add_argument(
'--cfg-options',
nargs='+',
action=DictAction,
help='override some settings in the used config, the key-value pair '
'in xxx=yyy format will be merged into config file. If the value to '
'be overwritten is a list, it should be like key="[a,b]" or key=a,b '
'It also allows nested list/tuple values, e.g. key="[(a,b),(c,d)]" '
'Note that the quotation marks are necessary and that no white space '
'is allowed.')
args = parser.parse_args()
return args
def main():
args = parse_args()
cfg = Config.fromfile(args.config)
if args.cfg_options is not None:
cfg.merge_from_dict(args.cfg_options)
print(cfg)
if __name__ == '__main__':
main()
```
一个配置文件示例 `demo_config.py` 如下所示:
```python
backbone = dict(
type='ResNetV1c',
depth=50,
num_stages=4,
out_indices=(0, 1, 2, 3),
dilations=(1, 1, 2, 4),
strides=(1, 2, 1, 1),
norm_eval=False,
style='pytorch',
contract_dilation=True)
```
运行 `demo_script.py`:
```shell
python demo_script.py demo_config.py
```
```shell
Config (path: demo_config.py): {'backbone': {'type': 'ResNetV1c', 'depth': 50, 'num_stages': 4, 'out_indices': (0, 1, 2, 3), 'dilations': (1, 1, 2, 4), 'strides': (1, 2, 1, 1), 'norm_eval': False, 'style': 'pytorch', 'contract_dilation': True}}
```
通过脚本参数修改配置:
```shell
python demo_script.py demo_config.py --cfg-options backbone.depth=101
```
```shell
Config (path: demo_config.py): {'backbone': {'type': 'ResNetV1c', 'depth': 101, 'num_stages': 4, 'out_indices': (0, 1, 2, 3), 'dilations': (1, 1, 2, 4), 'strides': (1, 2, 1, 1), 'norm_eval': False, 'style': 'pytorch', 'contract_dilation': True}}
```
- 更新列表/元组的值。
如果要更新的值是一个 list 或 tuple。例如需要在配置文件 `demo_config.py ``backbone ` 中设置 `stride =(1,2,1,1) `
如果您想更改这个键,你可以用两种方式进行指定:
1. `--cfg-options backbone.strides="(1, 1, 1, 1)"`. 注意引号 " 是支持 list/tuple 数据类型所必需的。
```shell
python demo_script.py demo_config.py --cfg-options backbone.strides="(1, 1, 1, 1)"
```
```shell
Config (path: demo_config.py): {'backbone': {'type': 'ResNetV1c', 'depth': 50, 'num_stages': 4, 'out_indices': (0, 1, 2, 3), 'dilations': (1, 1, 2, 4), 'strides': (1, 1, 1, 1), 'norm_eval': False, 'style': 'pytorch', 'contract_dilation': True}}
```
2. `--cfg-options backbone.strides=1,1,1,1`. 注意,在指定的值中**不允许**有空格。
另外如果原来的类型是tuple通过这种方式修改后会自动转换为list。
```shell
python demo_script.py demo_config.py --cfg-options backbone.strides=1,1,1,1
```
```shell
Config (path: demo_config.py): {'backbone': {'type': 'ResNetV1c', 'depth': 50, 'num_stages': 4, 'out_indices': (0, 1, 2, 3), 'dilations': (1, 1, 2, 4), 'strides': [1, 1, 1, 1], 'norm_eval': False, 'style': 'pytorch', 'contract_dilation': True}}
```
```{note}
这种修改方法仅支持修改string、int、float、boolean、None、list和tuple类型的配置项。
具体来说对于list和tuple类型的配置项它们内部的元素也必须是上述七种类型之一。
```