Shortcuts

从 MMSegmentation 0.x 迁移

引言

本指南介绍了 MMSegmentation 0.x 和 MMSegmentation1.x 在表现和 API 方面的基本区别,以及这些与迁移过程的关系。

新的依赖

MMSegmentation 1.x 依赖于一些新的软件包,您可以准备一个新的干净环境,然后根据安装教程重新安装。

或手动安装以下软件包。

  1. MMEngine:MMEngine 是 OpenMMLab 2.0 架构的核心,我们将许多与计算机视觉无关的内容从 MMCV 拆分到 MMEngine 中。

  2. MMCV:OpenMMLab 的计算机视觉包。这不是一个新的依赖,但您需要将其升级到 2.0.0 或以上的版本。

  3. MMClassification(可选):OpenMMLab 的图像分类工具箱和基准。这不是一个新的依赖,但您需要将其升级到 1.0.0rc6 版本。

  4. MMDetection(可选): OpenMMLab 的目标检测工具箱和基准。这不是一个新的依赖,但您需要将其升级到 3.0.0 或以上的版本。

启动训练

OpenMMLab 2.0 的主要改进是发布了 MMEngine,它为启动训练任务的统一接口提供了通用且强大的执行器。

与 MMSeg 0.x 相比,MMSeg 1.x 在 tools/train.py 中提供的命令行参数更少

功能 原版 新版
加载预训练模型 --load_from=$CHECKPOINT --cfg-options load_from=$CHECKPOINT
从特定检查点恢复训练 --resume-from=$CHECKPOINT --resume=$CHECKPOINT
从最新的检查点恢复训练 --auto-resume --resume='auto'
训练期间是否不评估检查点 --no-validate --cfg-options val_cfg=None val_dataloader=None val_evaluator=None
指定训练设备 --gpu-id=$DEVICE_ID -
是否为不同进程设置不同的种子 --diff-seed --cfg-options randomness.diff_rank_seed=True
是否为 CUDNN 后端设置确定性选项 --deterministic --cfg-options randomness.deterministic=True

测试启动

与训练启动类似,MMSegmentation 1.x 的测试启动脚本在 tools/test.py 中仅提供关键命令行参数,以下是测试启动脚本的区别,更多关于测试启动的细节请参考这里

功能 0.x 1.x
指定评测指标 --eval mIoU --cfg-options test_evaluator.type=IoUMetric
测试时数据增强 --aug-test --tta
测试时是否只保存预测结果不计算评测指标 --format-only --cfg-options test_evaluator.format_only=True

配置文件

模型设置

model.backendmodel.neckmodel.decode_headmodel.loss 字段没有更改。

添加 model.data_preprocessor 字段以配置 DataPreProcessor,包括:

  • mean(Sequence,可选):R、G、B 通道的像素平均值。默认为 None。

  • std(Sequence,可选):R、G、B 通道的像素标准差。默认为 None。

  • size(Sequence,可选):固定的填充大小。

  • size_divisor(int,可选):填充图像可以被当前值整除。

  • seg_pad_val(float,可选):分割图的填充值。默认值:255。

  • padding_mode(str):填充类型。默认值:’constant’。

    • constant:常量值填充,值由 pad_val 指定。

  • bgr_to_rgb(bool):是否将图像从 BGR 转换为 RGB。默认为 False。

  • rgb_to_bgr(bool):是否将图像从 RGB 转换为 BGR。默认为 False。

注: 有关详细信息,请参阅模型文档

数据集设置

data 的更改:

原版 data 字段被拆分为 train_dataloaderval_dataloadertest_dataloader,允许我们以细粒度配置它们。例如,您可以在训练和测试期间指定不同的采样器和批次大小。 samples_per_gpu 重命名为 batch_sizeworkers_per_gpu 重命名为 num_workers

原版
data = dict(
    samples_per_gpu=4,
    workers_per_gpu=4,
    train=dict(...),
    val=dict(...),
    test=dict(...),
)
新版
train_dataloader = dict(
    batch_size=4,
    num_workers=4,
    dataset=dict(...),
    sampler=dict(type='DefaultSampler', shuffle=True)  # 必须
)

val_dataloader = dict(
    batch_size=4,
    num_workers=4,
    dataset=dict(...),
    sampler=dict(type='DefaultSampler', shuffle=False)  # 必须
)

test_dataloader = val_dataloader

数据增强变换流程变更

  • 原始格式转换 ToTensorImageToTensorCollect 组合为 PackSegInputs

  • 我们不建议在数据集流程中执行 NormalizePad。请将其从流程中删除,并将其设置在 data_preprocessor 字段中。

  • MMSeg 1.x 中原始的 Resize 已更改为 RandomResize ,输入参数 img_scale 重命名为 scalekeep_ratio 的默认值修改为 False。

  • 原始的 test_pipeline 将单尺度和多尺度测试结合在一起,在 MMSeg 1.x 中,我们将其分为 test_pipelinetta_pipeline

注: 我们将一些数据转换工作转移到数据预处理器中,如归一化,请参阅文档了解更多详细信息。

训练流程

原版
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', reduce_zero_label=True),
    dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)),
    dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
    dict(type='RandomFlip', prob=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']),
]
新版
train_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='LoadAnnotations', reduce_zero_label=True),
    dict(
        type='RandomResize',
        scale=(2560, 640),
        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'),
    dict(
        type='MultiScaleFlipAug',
        img_scale=(2560, 640),
        # img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
        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']),
        ])
]
新版
test_pipeline = [
    dict(type='LoadImageFromFile'),
    dict(type='Resize', scale=(2560, 640), keep_ratio=True),
    dict(type='LoadAnnotations', reduce_zero_label=True),
    dict(type='PackSegInputs')
]
img_ratios = [0.5, 0.75, 1.0, 1.25, 1.5, 1.75]
tta_pipeline = [
    dict(type='LoadImageFromFile', backend_args=None),
    dict(
        type='TestTimeAug',
        transforms=[
            [
                dict(type='Resize', scale_factor=r, keep_ratio=True)
                for r in img_ratios
            ],
            [
                dict(type='RandomFlip', prob=0., direction='horizontal'),
                dict(type='RandomFlip', prob=1., direction='horizontal')
            ], [dict(type='LoadAnnotations')], [dict(type='PackSegInputs')]
        ])
]

evaluation 中的更改:

  • evaluation 字段被拆分为 val_evaluatortest_evaluator 。而且不再支持 intervalsave_best 参数。 interval 已移动到 train_cfg.val_intervalsave_best 已移动到 default_hooks.checkpoint.save_bestpre_eval 已删除。

  • IoU 已更改为 IoUMetric

原版
evaluation = dict(interval=2000, metric='mIoU', pre_eval=True)
新版
val_evaluator = dict(type='IoUMetric', iou_metrics=['mIoU'])
test_evaluator = val_evaluator

Optimizer 和 Schedule 设置

optimizeroptimizer_config 中的更改:

  • 现在我们使用 optim_wrapper 字段来指定优化过程的所有配置。以及 optimizeroptim_wrapper 的一个子字段。

  • paramwise_cfg 也是 optim_wrapper 的一个子字段,以替代 optimizer

  • optimizer_config 现在被删除,它的所有配置都被移动到 optim_wrapper 中。

  • grad_clip 重命名为 clip_grad

原版
optimizer = dict(type='AdamW', lr=0.0001, weight_decay=0.0005)
optimizer_config = dict(grad_clip=dict(max_norm=1, norm_type=2))
新版
optim_wrapper = dict(
    type='OptimWrapper',
    optimizer=dict(type='AdamW', lr=0.0001, weight_decay=0.0005),
    clip_grad=dict(max_norm=1, norm_type=2))

lr_config 中的更改:

  • 我们将 lr_config 字段删除,并使用新的 param_scheduler 替代。

  • 我们删除了与 warmup 相关的参数,因为我们使用 scheduler 组合来实现该功能。

新的 scheduler 组合机制非常灵活,您可以使用它来设计多种学习率/动量曲线。有关详细信息,请参见教程

原版
lr_config = dict(
    policy='poly',
    warmup='linear',
    warmup_iters=1500,
    warmup_ratio=1e-6,
    power=1.0,
    min_lr=0.0,
    by_epoch=False)
新版
param_scheduler = [
    dict(
        type='LinearLR', start_factor=1e-6, by_epoch=False, begin=0, end=1500),
    dict(
        type='PolyLR',
        power=1.0,
        begin=1500,
        end=160000,
        eta_min=0.0,
        by_epoch=False,
    )
]

runner 中的更改:

原版 runner 字段中的大多数配置被移动到 train_cfgval_cfgtest_cfg 中,以在训练、验证和测试中配置 loop。

原版
runner = dict(type='IterBasedRunner', max_iters=20000)
新版
# `val_interval` 是旧版本的 `evaluation.interval`。
train_cfg = dict(type='IterBasedTrainLoop', max_iters=20000, val_interval=2000)
val_cfg = dict(type='ValLoop') # 使用默认的验证循环。
test_cfg = dict(type='TestLoop') # 使用默认的测试循环。

事实上,在 OpenMMLab 2.0 中,我们引入了 Loop 来控制训练、验证和测试中的行为。Runner 的功能也发生了变化。您可以在 MMMEngine执行器教程 中找到更多的详细信息。

运行时设置

checkpoint_configlog_config 中的更改:

checkpoint_config 被移动到 default_hooks.checkpoint 中,log_config 被移动到 default_hooks.logger 中。 并且我们将许多钩子设置从脚本代码移动到运行时配置的 default_hooks 字段中。

default_hooks = dict(
    # 记录每次迭代的时间。
    timer=dict(type='IterTimerHook'),

    # 每50次迭代打印一次日志。
    logger=dict(type='LoggerHook', interval=50, log_metric_by_epoch=False),

    # 启用参数调度程序。
    param_scheduler=dict(type='ParamSchedulerHook'),

    # 每2000次迭代保存一次检查点。
    checkpoint=dict(type='CheckpointHook', by_epoch=False, interval=2000),

    # 在分布式环境中设置采样器种子。
    sampler_seed=dict(type='DistSamplerSeedHook'),

    # 验证结果可视化。
    visualization=dict(type='SegVisualizationHook'))

此外,我们将原版 logger 拆分为 logger 和 visualizer。logger 用于记录信息,visualizer 用于在不同的后端显示 logger,如 terminal 和 TensorBoard。

原版
log_config = dict(
    interval=100,
    hooks=[
        dict(type='TextLoggerHook'),
        dict(type='TensorboardLoggerHook'),
    ])
新版
default_hooks = dict(
    ...
    logger=dict(type='LoggerHook', interval=100),
)
vis_backends = [dict(type='LocalVisBackend'),
                dict(type='TensorboardVisBackend')]
visualizer = dict(
    type='SegLocalVisualizer', vis_backends=vis_backends, name='visualizer')

load_fromresume_from 中的更改:

  • 删除 resume_from。我们使用 resumeload_from 来替换它。

    • 如果 resume=Trueload_fromnot None,则从 load_from 中的检查点恢复训练。

    • 如果 resume=Trueload_fromNone,则尝试从工作目录中的最新检查点恢复。

    • 如果 resume=Falseload_fromnot None,则只加载检查点,而不继续训练。

    • 如果 resume=Falseload_fromNone,则不加载或恢复。

dist_params 中的更改:dist_params 字段现在是 env_cfg 的子字段。并且 env_cfg 中还有一些新的配置。

env_cfg = dict(
    # 是否启用 cudnn_benchmark
    cudnn_benchmark=False,

    # 设置多进程参数
    mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),

    # 设置分布式参数
    dist_cfg=dict(backend='nccl'),
)

workflow 的改动:workflow 相关功能被删除。

新字段 visualizer:visualizer 是 OpenMMLab 2.0 体系结构中的新设计。我们在 runner 中使用 visualizer 实例来处理结果和日志可视化,并保存到不同的后端。更多详细信息,请参阅可视化教程

新字段 default_scope:搜索所有注册模块的起点。MMSegmentation 中的 default_scopemmseg。请参见注册器教程了解更多详情。