# Config System Given that the traditional yacs-based config system or python argparse command-line options suffer from providing enough flexibility for the development of new project, we adopted the alternative and non-intrusive config system called [lazy config](https://detectron2.readthedocs.io/en/latest/tutorials/lazyconfigs.html) from [detectron2](https://github.com/facebookresearch/detectron2). Please refer to [detectron2 lazy-config tutorials](https://detectron2.readthedocs.io/en/latest/tutorials/lazyconfigs.html) for more details about the syntax and basic usage of lazy config. ## Default Configs in detrex Our ``detrex`` has defined a standard set of config namespaces for later usage. Users can modify these configs according to their own needs. In summary, the pre-defined namespaces are ``model, train, dataloader, optimizer, lr_multiplier`` ### model This is configuration for model definition. We define all model configs under ``projects/``, You can refer to [projects/dab_detr/configs/models](https://github.com/rentainhe/detrex/blob/main/projects/dab_detr/configs/models/dab_detr_r50.py) for more examples. Here is the example of `dab-detr-r50` model config: ```python # dab_detr_r50.py import torch.nn as nn from detrex.modeling.matcher import HungarianMatcher from detrex.modeling.criterion import SetCriterion from detrex.layers import PositionEmbeddingSine from detrex.modeling.backbone import ResNet, BasicStem from detectron2.config import LazyCall as L from projects.dab_detr.modeling import ( DABDETR, DabDetrTransformer, DabDetrTransformerDecoder, DabDetrTransformerEncoder, ) model = L(DABDETR)( backbone=L(ResNet)( stem=L(BasicStem)(in_channels=3, out_channels=64, norm="FrozenBN"), stages=L(ResNet.make_default_stages)( depth=50, stride_in_1x1=False, norm="FrozenBN", ), out_features=["res2", "res3", "res4", "res5"], freeze_at=1, ), in_features=["res5"], # only use last level feature in DAB-DETR in_channels=2048, position_embedding=L(PositionEmbeddingSine)( num_pos_feats=128, temperature=20, normalize=True, ), transformer=L(DabDetrTransformer)( encoder=L(DabDetrTransformerEncoder)( embed_dim=256, num_heads=8, attn_dropout=0.0, feedforward_dim=2048, ffn_dropout=0.0, activation=L(nn.PReLU)(), num_layers=6, ), decoder=L(DabDetrTransformerDecoder)( embed_dim=256, num_heads=8, attn_dropout=0.0, feedforward_dim=2048, ffn_dropout=0.0, activation=L(nn.PReLU)(), num_layers=6, modulate_hw_attn=True, ), ), embed_dim=256, num_classes=80, num_queries=300, criterion=L(SetCriterion)( num_classes=80, matcher=L(HungarianMatcher)( cost_class=2.0, cost_bbox=5.0, cost_giou=2.0, cost_class_type="focal_loss_cost", alpha=0.25, gamma=2.0, ), weight_dict={ "loss_class": 1, "loss_bbox": 5.0, "loss_giou": 2.0, }, loss_class_type="focal_loss", alpha=0.25, gamma=2.0, ), aux_loss=True, pixel_mean=[123.675, 116.280, 103.530], pixel_std=[58.395, 57.120, 57.375], freeze_anchor_box_centers=True, select_box_nums_for_evaluation=300, device="cuda", ) ``` which can be loaded like: ```python # user's own config.py from dab_detr_r50 import model # check the loaded model config assert model.embed_dim == 256 # modify model config according to your own needs model.embed_dim = 512 ``` After defining model configs in python files. Please ``import`` it in the global scope of the final config file as ``model``. You can access and change all keys in the model config according to your own needs. ### train This is the configuration for training and evalution. The default training config can be found in ``configs/common/train.py``. The default training config is as follows: ```python train = dict( # Directory where output files are written to output_dir="./output", # The initialize checkpoint to be loaded init_checkpoint="", # The total training iterations max_iter=90000, # options for Automatic Mixed Precision amp=dict(enabled=False), # options for DistributedDataParallel ddp=dict( broadcast_buffers=False, find_unused_parameters=False, fp16_compression=False, ), # options for Gradient Clipping during training clip_grad=dict( enabled=False, params=dict( max_norm=0.1, norm_type=2, ), ), # # options for Fast Debugging fast_dev_run=dict(enabled=False), # options for PeriodicCheckpointer, which saves a model checkpoint # after every `checkpointer.period` iterations, # and only `checkpointer.max_to_keep` number of checkpoint will be kept. checkpointer=dict(period=5000, max_to_keep=100), # Run evaluation after every `eval_period` number of iterations eval_period=5000, # Output log to console every `log_period` number of iterations. log_period=20, device="cuda" # ... ) ``` ### dataloader This is the configuration for dataset and dataloader. We use the built-in dataset in detectron2 for simplicity. Please see ``configs/common/data`` for more examples. Here we take the `coco_detr.py` for detr-like model as an example: ```python from omegaconf import OmegaConf import detectron2.data.transforms as T from detectron2.config import LazyCall as L from detectron2.data import ( build_detection_test_loader, build_detection_train_loader, get_detection_dataset_dicts, ) from detectron2.evaluation import COCOEvaluator from detrex.data import DetrDatasetMapper dataloader = OmegaConf.create() # the defined train loader dataloader.train = L(build_detection_train_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_train"), mapper=L(DetrDatasetMapper)( # the defined two augmentations which will be random-selected during training. augmentation=[ L(T.RandomFlip)(), L(T.ResizeShortestEdge)( short_edge_length=(480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800), max_size=1333, sample_style="choice", ), ], augmentation_with_crop=[ L(T.RandomFlip)(), L(T.ResizeShortestEdge)( short_edge_length=(400, 500, 600), sample_style="choice", ), L(T.RandomCrop)( crop_type="absolute_range", crop_size=(384, 600), ), L(T.ResizeShortestEdge)( short_edge_length=(480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800), max_size=1333, sample_style="choice", ), ], is_train=True, mask_on=False, # with instance mask or not img_format="RGB", # input image format ), total_batch_size=16, # training batch size num_workers=4, ) # the defined test loader dataloader.test = L(build_detection_test_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_val", filter_empty=False), mapper=L(DetrDatasetMapper)( # use no augmentation in testing augmentation=[ L(T.ResizeShortestEdge)( short_edge_length=800, max_size=1333, ), ], augmentation_with_crop=None, is_train=False, mask_on=False, img_format="RGB", ), num_workers=4, ) # the defined evaluator for evaluation dataloader.evaluator = L(COCOEvaluator)( dataset_name="${..test.dataset.names}", ) ``` We adopted the ``built-in coco datasets`` and ``detection dataloader`` usage from detectron2, please refer to the following tutorials if you want to use custom datasets: - [Use Custom Datasets](https://detectron2.readthedocs.io/en/latest/tutorials/datasets.html) - [Dataloader](https://detectron2.readthedocs.io/en/latest/tutorials/data_loading.html) - [Data Augmentation](https://detectron2.readthedocs.io/en/latest/tutorials/augmentation.html) ### optimizer This is the configuration for optimizer. The default configuration can be found in ``configs/common/optim.py``. detrex uilizes ``detectron2.solver.build.get_default_optimizer_params`` which needs the ``nn.Module`` as argument and returns the parameter groups. ```python # configs/common/optim.py import torch from detectron2.config import LazyCall as L from detectron2.solver.build import get_default_optimizer_params AdamW = L(torch.optim.AdamW)( params=L(get_default_optimizer_params)( # params.model is meant to be set to the model object, before instantiating # the optimizer. base_lr="${..lr}", weight_decay_norm=0.0, ), lr=1e-4, betas=(0.9, 0.999), weight_decay=0.1, ) ``` if you want to use ``torch.optim.SGD`` in training, you can modify your config as follows: ```python import torch from configs.commom.optim import AdamW as optim optim._target_ = torch.optim.SGD # Remove the incompatible arguments del optim.betas # Add the needed arguments optim.momentum = 0.9 ``` ### lr_multiplier This is the configuration for ``lr_multiplier`` which is combined with ``detectron2.engine.hooks.LRScheduler`` and performs learning scheduler function during training. The default ``lr_multiplier`` config can be found in ``configs/common/coco_schedule.py``, we defined the commonly 50 epochs scheduler referred to in the papers as follows: ```python from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler def default_coco_scheduler(epochs=50, decay_epochs=40, warmup_epochs=0): """ Returns the config for a default multi-step LR scheduler such as "50epochs", commonly referred to in papers, where every 1x has the total length of 1440k training images (~12 COCO epochs). LR is decayed once at the end of training. Args: epochs (int): total training epochs. decay_epochs (int): lr decay steps. warmup_epochs (int): warmup epochs. Returns: DictConfig: configs that define the multiplier for LR during training """ # total number of iterations assuming 16 batch size, using 1440000/16=90000 total_steps_16bs = epochs * 7500 decay_steps = decay_epochs * 7500 warmup_steps = warmup_epochs * 7500 scheduler = L(MultiStepParamScheduler)( values=[1.0, 0.1], milestones=[decay_steps, total_steps_16bs], ) return L(WarmupParamScheduler)( scheduler=scheduler, warmup_length=warmup_steps / total_steps_16bs, warmup_method="linear", warmup_factor=0.001, ) ``` Please refer to [fvcore.common.param_scheduler.ParamScheduler](https://detectron2.readthedocs.io/en/latest/modules/fvcore.html#fvcore.common.param_scheduler.ParamScheduler) for more details about the ``ParamScheduler`` usage in detectron2. ## Get the Default Config Users don't have to rewrite all contents in config every time. You can use the default built-in detrex configs using ``detrex.config.get_config``. After building ``detrex`` from source, you can use ``get_config`` to get the default configs as follows: ```python from detrex.config import get_config # get the default config dataloader = get_config("common/data/coco_detr.py").dataloader optimizer = get_config("common/optim.py").AdamW lr_multiplier = get_config("common/coco_schedule.py").lr_multiplier_50ep train = get_config("common/train.py").train # modify the config train.max_iter = 375000 train.output_dir = "path/to/your/own/dir" ``` ## LazyConfig Best Practices 1. Treat the configs you write as actual "code": Avoid copying them or duplicating them. Import the common parts between configs. 2. Keep the configs you write as simple as possible: Do not include keys that do not affect the experimental setting.