# 将配置文件从 MMDetection 2.x 迁移至 3.x MMDetection 3.x 的配置文件与 2.x 相比有较大变化,这篇文档将介绍如何将 2.x 的配置文件迁移到 3.x。 在前面的[配置文件教程](../user_guides/config.md)中,我们以 Mask R-CNN 为例介绍了 MMDetection 3.x 的配置文件结构,这里我们将按同样的结构介绍如何将 2.x 的配置文件迁移至 3.x。 ## 模型配置 模型的配置与 2.x 相比并没有太大变化,对于模型的 backbone,neck,head,以及 train_cfg 和 test_cfg,它们的参数与 2.x 版本的参数保持一致。 不同的是,我们在 3.x 版本的模型中新增了 `DataPreprocessor` 模块。 `DataPreprocessor` 模块的配置位于 `model.data_preprocessor` 中,它用于对输入数据进行预处理,例如对输入图像进行归一化,将不同大小的图片进行 padding 从而组成 batch,将图像从内存中读取到显存中等。这部分配置取代了原本存在于 train_pipeline 和 test_pipeline 中的 `Normalize` 和 `Pad`。
原配置 | ```python # 图像归一化参数 img_norm_cfg = dict( mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True) pipeline=[ ..., dict(type='Normalize', **img_norm_cfg), dict(type='Pad', size_divisor=32), # 图像 padding 到 32 的倍数 ... ] ``` |
新配置 | ```python model = dict( data_preprocessor=dict( type='DetDataPreprocessor', # 图像归一化参数 mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], bgr_to_rgb=True, # 图像 padding 参数 pad_mask=True, # 在实例分割中,需要将 mask 也进行 padding pad_size_divisor=32) # 图像 padding 到 32 的倍数 ) ``` |
原配置 | ```python data = dict( samples_per_gpu=2, workers_per_gpu=2, train=dict( type=dataset_type, ann_file=data_root + 'annotations/instances_train2017.json', img_prefix=data_root + 'train2017/', pipeline=train_pipeline), val=dict( type=dataset_type, ann_file=data_root + 'annotations/instances_val2017.json', img_prefix=data_root + 'val2017/', pipeline=test_pipeline), test=dict( type=dataset_type, ann_file=data_root + 'annotations/instances_val2017.json', img_prefix=data_root + 'val2017/', pipeline=test_pipeline)) ``` |
新配置 | ```python train_dataloader = dict( batch_size=2, num_workers=2, persistent_workers=True, # 避免每次迭代后 dataloader 重新创建子进程 sampler=dict(type='DefaultSampler', shuffle=True), # 默认的 sampler,同时支持分布式训练和非分布式训练 batch_sampler=dict(type='AspectRatioBatchSampler'), # 默认的 batch_sampler,用于保证 batch 中的图片具有相似的长宽比,从而可以更好地利用显存 dataset=dict( type=dataset_type, data_root=data_root, ann_file='annotations/instances_train2017.json', data_prefix=dict(img='train2017/'), filter_cfg=dict(filter_empty_gt=True, min_size=32), pipeline=train_pipeline)) # 在 3.x 版本中可以独立配置验证和测试的 dataloader val_dataloader = dict( batch_size=1, num_workers=2, persistent_workers=True, drop_last=False, sampler=dict(type='DefaultSampler', shuffle=False), dataset=dict( type=dataset_type, data_root=data_root, ann_file='annotations/instances_val2017.json', data_prefix=dict(img='val2017/'), test_mode=True, pipeline=test_pipeline)) test_dataloader = val_dataloader # 测试 dataloader 的配置与验证 dataloader 的配置相同,这里省略 ``` |
原配置 | ```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='LoadAnnotations', with_bbox=True), dict(type='Resize', img_scale=(1333, 800), keep_ratio=True), dict(type='RandomFlip', flip_ratio=0.5), dict(type='Normalize', **img_norm_cfg), dict(type='Pad', size_divisor=32), dict(type='DefaultFormatBundle'), dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']), ] ``` |
新配置 | ```python train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations', with_bbox=True), dict(type='Resize', scale=(1333, 800), keep_ratio=True), dict(type='RandomFlip', prob=0.5), dict(type='PackDetInputs') ] ``` |
原配置 | ```python test_pipeline = [ dict(type='LoadImageFromFile'), dict( type='MultiScaleFlipAug', img_scale=(1333, 800), flip=False, transforms=[ dict(type='Resize', keep_ratio=True), dict(type='RandomFlip'), dict(type='Normalize', **img_norm_cfg), dict(type='Pad', size_divisor=32), dict(type='ImageToTensor', keys=['img']), dict(type='Collect', keys=['img']), ]) ] ``` |
新配置 | ```python test_pipeline = [ dict(type='LoadImageFromFile'), dict(type='Resize', scale=(1333, 800), keep_ratio=True), dict( type='PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor')) ] ``` |
名称 | 原配置 | 新配置 |
---|---|---|
Resize | ```python dict(type='Resize', img_scale=(1333, 800), keep_ratio=True) ``` | ```python dict(type='Resize', scale=(1333, 800), keep_ratio=True) ``` |
RandomResize | ```python dict( type='Resize', img_scale=[ (1333, 640), (1333, 800)], multiscale_mode='range', keep_ratio=True) ``` | ```python dict( type='RandomResize', scale=[ (1333, 640), (1333, 800)], keep_ratio=True) ``` |
RandomChoiceResize | ```python dict( type='Resize', img_scale=[ (1333, 640), (1333, 672), (1333, 704), (1333, 736), (1333, 768), (1333, 800)], multiscale_mode='value', keep_ratio=True) ``` | ```python dict( type='RandomChoiceResize', scales=[ (1333, 640), (1333, 672), (1333, 704), (1333, 736), (1333, 768), (1333, 800)], keep_ratio=True) ``` |
RandomFlip | ```python dict(type='RandomFlip', flip_ratio=0.5) ``` | ```python dict(type='RandomFlip', prob=0.5) ``` |
评测指标名称 | 原配置 | 新配置 |
---|---|---|
COCO | ```python data = dict( val=dict( type='CocoDataset', ann_file=data_root + 'annotations/instances_val2017.json')) evaluation = dict(metric=['bbox', 'segm']) ``` | ```python val_evaluator = dict( type='CocoMetric', ann_file=data_root + 'annotations/instances_val2017.json', metric=['bbox', 'segm'], format_only=False) ``` |
Pascal VOC | ```python data = dict( val=dict( type=dataset_type, ann_file=data_root + 'VOC2007/ImageSets/Main/test.txt')) evaluation = dict(metric='mAP') ``` | ```python val_evaluator = dict( type='VOCMetric', metric='mAP', eval_mode='11points') ``` |
OpenImages | ```python data = dict( val=dict( type='OpenImagesDataset', ann_file=data_root + 'annotations/validation-annotations-bbox.csv', img_prefix=data_root + 'OpenImages/validation/', label_file=data_root + 'annotations/class-descriptions-boxable.csv', hierarchy_file=data_root + 'annotations/bbox_labels_600_hierarchy.json', meta_file=data_root + 'annotations/validation-image-metas.pkl', image_level_ann_file=data_root + 'annotations/validation-annotations-human-imagelabels-boxable.csv')) evaluation = dict(interval=1, metric='mAP') ``` | ```python val_evaluator = dict( type='OpenImagesMetric', iou_thrs=0.5, ioa_thrs=0.5, use_group_of=True, get_supercategory=True) ``` |
CityScapes | ```python data = dict( val=dict( type='CityScapesDataset', ann_file=data_root + 'annotations/instancesonly_filtered_gtFine_val.json', img_prefix=data_root + 'leftImg8bit/val/', pipeline=test_pipeline)) evaluation = dict(metric=['bbox', 'segm']) ``` | ```python val_evaluator = [ dict( type='CocoMetric', ann_file=data_root + 'annotations/instancesonly_filtered_gtFine_val.json', metric=['bbox', 'segm']), dict( type='CityScapesMetric', ann_file=data_root + 'annotations/instancesonly_filtered_gtFine_val.json', seg_prefix=data_root + '/gtFine/val', outfile_prefix='./work_dirs/cityscapes_metric/instance') ] ``` |
原配置 | ```python runner = dict( type='EpochBasedRunner', # 训练循环的类型 max_epochs=12) # 最大训练轮次 evaluation = dict(interval=2) # 验证间隔。每 2 个 epoch 验证一次 ``` |
新配置 | ```python train_cfg = dict( type='EpochBasedTrainLoop', # 训练循环的类型,请参考 https://github.com/open-mmlab/mmengine/blob/main/mmengine/runner/loops.py max_epochs=12, # 最大训练轮次 val_interval=2) # 验证间隔。每 2 个 epoch 验证一次 val_cfg = dict(type='ValLoop') # 验证循环的类型 test_cfg = dict(type='TestLoop') # 测试循环的类型 ``` |
原配置 | ```python optimizer = dict( type='SGD', # 随机梯度下降优化器 lr=0.02, # 基础学习率 momentum=0.9, # 带动量的随机梯度下降 weight_decay=0.0001) # 权重衰减 optimizer_config = dict(grad_clip=None) # 梯度裁剪的配置,设置为 None 关闭梯度裁剪 ``` |
新配置 | ```python optim_wrapper = dict( # 优化器封装的配置 type='OptimWrapper', # 优化器封装的类型。可以切换至 AmpOptimWrapper 来启用混合精度训练 optimizer=dict( # 优化器配置。支持 PyTorch 的各种优化器。请参考 https://pytorch.org/docs/stable/optim.html#algorithms type='SGD', # 随机梯度下降优化器 lr=0.02, # 基础学习率 momentum=0.9, # 带动量的随机梯度下降 weight_decay=0.0001), # 权重衰减 clip_grad=None, # 梯度裁剪的配置,设置为 None 关闭梯度裁剪。使用方法请见 https://mmengine.readthedocs.io/en/latest/tutorials/optimizer.html ) ``` |
原配置 | ```python lr_config = dict( policy='step', # 在训练过程中使用 multi step 学习率策略 warmup='linear', # 使用线性学习率预热 warmup_iters=500, # 到第 500 个 iteration 结束预热 warmup_ratio=0.001, # 学习率预热的系数 step=[8, 11], # 在哪几个 epoch 进行学习率衰减 gamma=0.1) # 学习率衰减系数 ``` |
新配置 | ```python param_scheduler = [ dict( type='LinearLR', # 使用线性学习率预热 start_factor=0.001, # 学习率预热的系数 by_epoch=False, # 按 iteration 更新预热学习率 begin=0, # 从第一个 iteration 开始 end=500), # 到第 500 个 iteration 结束 dict( type='MultiStepLR', # 在训练过程中使用 multi step 学习率策略 by_epoch=True, # 按 epoch 更新学习率 begin=0, # 从第一个 epoch 开始 end=12, # 到第 12 个 epoch 结束 milestones=[8, 11], # 在哪几个 epoch 进行学习率衰减 gamma=0.1) # 学习率衰减系数 ] ``` |
功能 | 原配置 | 新配置 |
---|---|---|
设置保存间隔 | ```python checkpoint_config = dict( interval=1) ``` | ```python default_hooks = dict( checkpoint=dict( type='CheckpointHook', interval=1)) ``` |
保存最佳模型 | ```python evaluation = dict( save_best='auto') ``` | ```python default_hooks = dict( checkpoint=dict( type='CheckpointHook', save_best='auto')) ``` |
只保留最新的几个模型 | ```python checkpoint_config = dict( max_keep_ckpts=3) ``` | ```python default_hooks = dict( checkpoint=dict( type='CheckpointHook', max_keep_ckpts=3)) ``` |
功能 | 原配置 | 新配置 |
---|---|---|
设置日志打印间隔 | ```python log_config = dict( interval=50) ``` | ```python default_hooks = dict( logger=dict( type='LoggerHook', interval=50)) # 可选: 配置日志打印数值的平滑窗口大小 log_processor = dict( type='LogProcessor', window_size=50) ``` |
使用 TensorBoard 或 WandB 可视化日志 | ```python log_config = dict( interval=50, hooks=[ dict(type='TextLoggerHook'), dict(type='TensorboardLoggerHook'), dict(type='MMDetWandbHook', init_kwargs={ 'project': 'mmdetection', 'group': 'maskrcnn-r50-fpn-1x-coco' }, interval=50, log_checkpoint=True, log_checkpoint_metadata=True, num_eval_images=100) ]) ``` | ```python vis_backends = [ dict(type='LocalVisBackend'), dict(type='TensorboardVisBackend'), dict(type='WandbVisBackend', init_kwargs={ 'project': 'mmdetection', 'group': 'maskrcnn-r50-fpn-1x-coco' }) ] visualizer = dict( type='DetLocalVisualizer', vis_backends=vis_backends, name='visualizer') ``` |
原配置 | 新配置 |
---|---|
```python cudnn_benchmark = False opencv_num_threads = 0 mp_start_method = 'fork' dist_params = dict(backend='nccl') log_level = 'INFO' load_from = None resume_from = None ``` | ```python env_cfg = dict( cudnn_benchmark=False, mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), dist_cfg=dict(backend='nccl')) log_level = 'INFO' load_from = None resume = False ``` |