# Tutorial 1: Learn about Configs¶

We incorporate modular and inheritance design into our config system, which is convenient to conduct various experiments. If you wish to inspect the config file, you may run python tools/misc/print_config.py /PATH/TO/CONFIG to see the complete config. The mmrotate is built upon the mmdet, thus it is highly recommended to learn the basics of mmdet.

## Modify a config through script arguments¶

When submitting jobs using “tools/train.py” or “tools/test.py”, you may specify --cfg-options to in-place modify the config.

• Update config keys of dict chains.

The config options can be specified following the order of the dict keys in the original config. For example, --cfg-options model.backbone.norm_eval=False changes all BN modules in model backbones to train mode.

• Update keys inside a list of configs.

Some config dicts are composed as a list in your config. For example, the training pipeline data.train.pipeline is normally a list e.g. [dict(type='LoadImageFromFile'), ...]. If you want to change 'LoadImageFromFile' to 'LoadImageFromWebcam' in the pipeline, you may specify --cfg-options data.train.pipeline.0.type=LoadImageFromWebcam.

• Update values of list/tuples.

If the value to be updated is a list or a tuple. For example, the config file normally sets workflow=[('train', 1)]. If you want to change this key, you may specify --cfg-options workflow="[(train,1),(val,1)]". Note that the quotation mark ” is necessary to support list/tuple data types, and that NO white space is allowed inside the quotation marks in the specified value.

## Config file naming convention¶

We follow the below style to name config files. Contributors are advised to follow the same style.

{model}_[model setting]_{backbone}_{neck}_[norm setting]_[misc]_[gpu x batch_per_gpu]_{dataset}_{data setting}_{angle version}


{xxx} is required field and [yyy] is optional.

• {model}: model type like rotated_faster_rcnn, rotated_retinanet, etc.

• [model setting]: specific setting for some model, like hbb for rotated_retinanet, etc.

• {backbone}: backbone type like r50 (ResNet-50), swin_tiny (SWIN-tiny).

• {neck}: neck type like fpn, refpn.

• [norm_setting]: bn (Batch Normalization) is used unless specified, other norm layer types could be gn (Group Normalization), syncbn (Synchronized Batch Normalization). gn-head/gn-neck indicates GN is applied in head/neck only, while gn-all means GN is applied in the entire model, e.g. backbone, neck, head.

• [misc]: miscellaneous setting/plugins of the model, e.g. dconv, gcb, attention, albu, mstrain.

• [gpu x batch_per_gpu]: GPUs and samples per GPU, 1xb2 is used by default.

• {dataset}: dataset like dota.

• {angle version}: like oc, le135, or le90.

## An example of RotatedRetinaNet¶

To help the users have a basic idea of a complete config and the modules in a modern detection system, we make brief comments on the config of RotatedRetinaNet using ResNet50 and FPN as the following. For more detailed usage and the corresponding alternative for each module, please refer to the API documentation.

angle_version = 'oc'  # The angle version
model = dict(
type='RotatedRetinaNet',  # The name of detector
backbone=dict(  # The config of backbone
type='ResNet',  # The type of the backbone
depth=50,  # The depth of backbone
num_stages=4,  # Number of stages of the backbone.
out_indices=(0, 1, 2, 3),  # The index of output feature maps produced in each stages
frozen_stages=1,  # The weights in the first 1 stage are fronzen
zero_init_residual=False,  # Whether to use zero init for last norm layer in resblocks to let them behave as identity.
norm_cfg=dict(  # The config of normalization layers.
type='BN',  # Type of norm layer, usually it is BN or GN
requires_grad=True),  # Whether to train the gamma and beta in BN
norm_eval=True,  # Whether to freeze the statistics in BN
style='pytorch',  # The style of backbone, 'pytorch' means that stride 2 layers are in 3x3 conv, 'caffe' means stride 2 layers are in 1x1 convs.
init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')),  # The ImageNet pretrained backbone to be loaded
neck=dict(
type='FPN',  # The neck of detector is FPN. We also support 'ReFPN'
in_channels=[256, 512, 1024, 2048],  # The input channels, this is consistent with the output channels of backbone
out_channels=256,  # The output channels of each level of the pyramid feature map
start_level=1,  # Index of the start input backbone level used to build the feature pyramid
add_extra_convs='on_input',  # It specifies the source feature map of the extra convs
num_outs=5),  # The number of output scales
num_classes=15,  # Number of classes for classification
in_channels=256,  # Input channels for bbox head
stacked_convs=4,  # Number of stacking convs of the head
feat_channels=256,  # Number of hidden channels
assign_by_circumhbbox='oc',  # The angle version of obb2hbb
anchor_generator=dict(  # The config of anchor generator
type='RotatedAnchorGenerator',  # The type of anchor generator
octave_base_scale=4,  # The base scale of octave.
scales_per_octave=3,  #  Number of scales for each octave.
ratios=[1.0, 0.5, 2.0],  # The ratio between height and width.
strides=[8, 16, 32, 64, 128]),  # The strides of the anchor generator. This is consistent with the FPN feature strides.
bbox_coder=dict(  # Config of box coder to encode and decode the boxes during training and testing
type='DeltaXYWHAOBBoxCoder',  # Type of box coder.
angle_range='oc',  # The angle version of box coder.
norm_factor=None,  # The norm factor of box coder.
edge_swap=False,  # The edge swap flag of box coder.
proj_xy=False,  # The project flag of box coder.
target_means=(0.0, 0.0, 0.0, 0.0, 0.0),  # The target means used to encode and decode boxes
target_stds=(1.0, 1.0, 1.0, 1.0, 1.0)),  # The standard variance used to encode and decode boxes
loss_cls=dict(  # Config of loss function for the classification branch
type='FocalLoss',  # Type of loss for classification branch
use_sigmoid=True,  #  Whether the prediction is used for sigmoid or softmax
gamma=2.0,  # The gamma for calculating the modulating factor
alpha=0.25,  # A balanced form for Focal Loss
loss_weight=1.0),  # Loss weight of the classification branch
loss_bbox=dict(  # Config of loss function for the regression branch
type='L1Loss',  # Type of loss
loss_weight=1.0)),  # Loss weight of the regression branch
train_cfg=dict(  # Config of training hyperparameters
assigner=dict(  # Config of assigner
type='MaxIoUAssigner',  # Type of assigner
pos_iou_thr=0.5,  # IoU >= threshold 0.5 will be taken as positive samples
neg_iou_thr=0.4,  # IoU < threshold 0.4 will be taken as negative samples
min_pos_iou=0,  # The minimal IoU threshold to take boxes as positive samples
ignore_iof_thr=-1,  # IoF threshold for ignoring bboxes
iou_calculator=dict(type='RBboxOverlaps2D')),  # Type of Calculator for IoU
allowed_border=-1,  # The border allowed after padding for valid anchors.
pos_weight=-1,  # The weight of positive samples during training.
debug=False),  # Whether to set the debug mode
test_cfg=dict(  # Config of testing hyperparameters
nms_pre=2000,  # The number of boxes before NMS
min_bbox_size=0,  # The allowed minimal box size
score_thr=0.05,  # Threshold to filter out boxes
nms=dict(iou_thr=0.1), # NMS threshold
max_per_img=2000))  # The number of boxes to be kept after NMS.
dataset_type = 'DOTADataset'  # Dataset type, this will be used to define the dataset
data_root = '../datasets/split_1024_dota1_0/'  # Root path of data
img_norm_cfg = dict(  # Image normalization config to normalize the input images
mean=[123.675, 116.28, 103.53],  # Mean values used to pre-training the pre-trained backbone models
std=[58.395, 57.12, 57.375],  # Standard variance used to pre-training the pre-trained backbone models
to_rgb=True)  # The channel orders of image used to pre-training the pre-trained backbone models
train_pipeline = [  # Training pipeline
with_bbox=True),  # Whether to use bounding box, True for detection
dict(type='RResize',  # Augmentation pipeline that resize the images and their annotations
img_scale=(1024, 1024)),  # The largest scale of image
dict(type='RRandomFlip',  # Augmentation pipeline that flip the images and their annotations
flip_ratio=0.5,  # The ratio or probability to flip
version='oc'),  # The angle version
dict(
type='Normalize',  # Augmentation pipeline that normalize the input images
mean=[123.675, 116.28, 103.53],  # These keys are the same of img_norm_cfg since the
std=[58.395, 57.12, 57.375],  # keys of img_norm_cfg are used here as arguments
to_rgb=True),
size_divisor=32),  # The number the padded images should be divisible
dict(type='DefaultFormatBundle'),  # Default format bundle to gather data in the pipeline
dict(type='Collect',  # Pipeline that decides which keys in the data should be passed to the detector
keys=['img', 'gt_bboxes', 'gt_labels'])
]
test_pipeline = [
dict(
type='MultiScaleFlipAug',  # An encapsulation that encapsulates the testing augmentations
img_scale=(1024, 1024),  # Decides the largest scale for testing, used for the Resize pipeline
flip=False,  # Whether to flip images during testing
transforms=[
dict(type='RResize'),  # Use resize augmentation
dict(
type='Normalize',  # Normalization config, the values are from img_norm_cfg
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
size_divisor=32),
dict(type='DefaultFormatBundle'),  # Default format bundle to gather data in the pipeline
dict(type='Collect',  # Collect pipeline that collect necessary keys for testing.
keys=['img'])
])
]
data = dict(
samples_per_gpu=2,  # Batch size of a single GPU
workers_per_gpu=2,  # Worker to pre-fetch data for each single GPU
train=dict(  # Train dataset config
ann_file=
'../datasets/split_1024_dota1_0/trainval/annfiles/',  # Path of annotation file
img_prefix=
'../datasets/split_1024_dota1_0/trainval/images/',  # Prefix of image path
pipeline=[  # pipeline, this is passed by the train_pipeline created before.
dict(type='RResize', img_scale=(1024, 1024)),
dict(type='RRandomFlip', flip_ratio=0.5, version='oc'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
],
version='oc'),
val=dict(  # Validation dataset config
ann_file=
'../datasets/split_1024_dota1_0/trainval/annfiles/',
img_prefix=
'../datasets/split_1024_dota1_0/trainval/images/',
pipeline=[
dict(
type='MultiScaleFlipAug',
img_scale=(1024, 1024),
flip=False,
transforms=[
dict(type='RResize'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img'])
])
],
version='oc'),
test=dict(  # Test dataset config, modify the ann_file for test-dev/test submission
ann_file=
'../datasets/split_1024_dota1_0/test/images/',
img_prefix=
'../datasets/split_1024_dota1_0/test/images/',
pipeline=[  # Pipeline is passed by test_pipeline created before
dict(
type='MultiScaleFlipAug',
img_scale=(1024, 1024),
flip=False,
transforms=[
dict(type='RResize'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img'])
])
],
version='oc'))
evaluation = dict(  # The config to build the evaluation hook
interval=12,  # Evaluation interval
metric='mAP')  # Metrics used during evaluation
optimizer = dict(  # Config used to build optimizer
type='SGD',  # Type of optimizers
lr=0.0025,  # Learning rate of optimizers
momentum=0.9,  # Momentum
weight_decay=0.0001)  # Weight decay of SGD
optimizer_config = dict(  # Config used to build the optimizer hook
max_norm=35,
norm_type=2))
lr_config = dict(  # Learning rate scheduler config used to register LrUpdater hook
policy='step',  # The policy of scheduler
warmup='linear',  # The warmup policy, also support exp and constant.
warmup_iters=500,  # The number of iterations for warmup
warmup_ratio=0.3333333333333333,  # The ratio of the starting learning rate used for warmup
step=[8, 11])  # Steps to decay the learning rate
runner = dict(
type='EpochBasedRunner',  # Type of runner to use (i.e. IterBasedRunner or EpochBasedRunner)
max_epochs=12) # Runner that runs the workflow in total max_epochs. For IterBasedRunner use max_iters
checkpoint_config = dict(  # Config to set the checkpoint hook
interval=12)  # The save interval is 12
log_config = dict(  # config to register logger hook
interval=50,  # Interval to print the log
hooks=[
# dict(type='TensorboardLoggerHook')  # The Tensorboard logger is also supported
dict(type='TextLoggerHook')
])  # The logger used to record the training process.
dist_params = dict(backend='nccl')  # Parameters to setup distributed training, the port can also be set.
log_level = 'INFO'  # The level of logging.
load_from = None  # load models as a pre-trained model from a given path. This will not resume training.
resume_from = None  # Resume checkpoints from a given path, the training will be resumed from the epoch when the checkpoint's is saved.
workflow = [('train', 1)]  # Workflow for runner. [('train', 1)] means there is only one workflow and the workflow named 'train' is executed once. The workflow trains the model by 12 epochs according to the total_epochs.
work_dir = './work_dirs/rotated_retinanet_hbb_r50_fpn_1x_dota_oc'  # Directory to save the model checkpoints and logs for the current experiments.


## FAQ¶

### Use intermediate variables in configs¶

Some intermediate variables are used in the configs files, like train_pipeline/test_pipeline in datasets. It’s worth noting that when modifying intermediate variables in the children configs, the user needs to pass the intermediate variables into corresponding fields again. For example, we would like to use an offline multi-scale strategy to train an RoI-Trans. train_pipeline are intermediate variables we would like to modify.

_base_ = ['./roi_trans_r50_fpn_1x_dota_le90.py']

data_root = '../datasets/split_ms_dota1_0/'
angle_version = 'le90'
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='RResize', img_scale=(1024, 1024)),
dict(
type='RRandomFlip',
flip_ratio=[0.25, 0.25, 0.25],
direction=['horizontal', 'vertical', 'diagonal'],
version=angle_version),
dict(
type='PolyRandomRotate',
rotate_ratio=0.5,
angles_range=180,
auto_bound=False,
version=angle_version),
dict(type='Normalize', **img_norm_cfg),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]
data = dict(
train=dict(
pipeline=train_pipeline,
ann_file=data_root + 'trainval/annfiles/',
img_prefix=data_root + 'trainval/images/'),
val=dict(
ann_file=data_root + 'trainval/annfiles/',
img_prefix=data_root + 'trainval/images/'),
test=dict(
ann_file=data_root + 'test/images/',
img_prefix=data_root + 'test/images/'))


We first define the new train_pipeline/test_pipeline and pass them into data.

Similarly, if we would like to switch from SyncBN to BN or MMSyncBN, we need to substitute every norm_cfg in the config.

_base_ = './roi_trans_r50_fpn_1x_dota_le90.py'