|
| 1 | +# Lazy Configs |
| 2 | + |
| 3 | +The traditional yacs-based config system provides basic, standard functionalities. |
| 4 | +However, it does not offer enough flexibility for many new projects. |
| 5 | +We develop an alternative, non-intrusive config system that can be used with |
| 6 | +detectron2 or potentially any other complex projects. |
| 7 | + |
| 8 | +## Python Syntax |
| 9 | + |
| 10 | +Our config objects are still dictionaries. Instead of using Yaml to define dictionaries, |
| 11 | +we create dictionaries in Python directly. This gives users the following power that |
| 12 | +doesn't exist in Yaml: |
| 13 | + |
| 14 | +* Easily manipulate the dictionary (addition & deletion) using Python. |
| 15 | +* Write simple arithmetics or call simple functions. |
| 16 | +* Use more data types / objects. |
| 17 | +* Import / compose other config files, using the familiar Python import syntax. |
| 18 | + |
| 19 | +A Python config file can be loaded like this: |
| 20 | +```python |
| 21 | +# config.py: |
| 22 | +a = dict(x=1, y=2, z=dict(xx=1)) |
| 23 | +b = dict(x=3, y=4) |
| 24 | + |
| 25 | +# my_code.py: |
| 26 | +from detectron2.config import LazyConfig |
| 27 | +cfg = LazyConfig.load("path/to/config.py") # an omegaconf dictionary |
| 28 | +assert cfg.a.z.xx == 1 |
| 29 | +``` |
| 30 | + |
| 31 | +After `LazyConfig.load`, `cfg` will be a dictionary that contains all dictionaries |
| 32 | +defined in the global scope of the config file. Note that: |
| 33 | +* All dictionaries are turned to an [omegaconf](https://omegaconf.readthedocs.io/) |
| 34 | + config object during loading. This enables access to omegaconf features, |
| 35 | + such as its [access syntax](https://omegaconf.readthedocs.io/en/2.1_branch/usage.html#access-and-manipulation) |
| 36 | + and [interoplation](https://omegaconf.readthedocs.io/en/2.1_branch/usage.html#variable-interpolation). |
| 37 | +* Absolute imports in `config.py` works the same as in regular Python. |
| 38 | +* Relative imports can only import dictionaries from config files. |
| 39 | + They are simply a syntax sugar for [LazyConfig.load_rel](../modules/config.html#detectron2.config.LazyConfig.load_rel). |
| 40 | + They can load Python files at relative path without requiring `__init__.py`. |
| 41 | + |
| 42 | +## Recursive Instantiation |
| 43 | + |
| 44 | +The LazyConfig system heavily uses recursive instantiation, which is a pattern that |
| 45 | +uses a dictionary to describe a |
| 46 | +call to a function/class. The dictionary consists of: |
| 47 | + |
| 48 | +1. A "\_target\_" key which contains path to the callable, such as "module.submodule.class_name". |
| 49 | +2. Other keys that represent arguments to pass to the callable. Arguments themselves can be defined |
| 50 | + using recursive instantiation. |
| 51 | + |
| 52 | +We provide a helper function [LazyCall](../modules/config.html#detectron2.config.LazyCall) that helps create such dictionaries. |
| 53 | +The following code using `LazyCall` |
| 54 | +```python |
| 55 | +from detectron2.config import LazyCall as L |
| 56 | +from my_app import Trainer, Optimizer |
| 57 | +cfg = L(Trainer)( |
| 58 | + optimizer=L(Optimizer)( |
| 59 | + lr=0.01, |
| 60 | + algo="SGD" |
| 61 | + ) |
| 62 | +) |
| 63 | +``` |
| 64 | +creates a dictionary like this: |
| 65 | +``` |
| 66 | +cfg = { |
| 67 | + "_target_": "my_app.Trainer", |
| 68 | + "optimizer": { |
| 69 | + "_target_": "my_app.Optimizer", |
| 70 | + "lr": 0.01, "algo": "SGD" |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +By representing objects using such dictionaries, a general |
| 76 | +[instantiate](../modules/config.html#detectron2.config.instantiate) |
| 77 | +function can turn them into actual objects, i.e.: |
| 78 | +```python |
| 79 | +from detectron2.config import instantiate |
| 80 | +trainer = instantiate(cfg) |
| 81 | +# equivalent to: |
| 82 | +# from my_app import Trainer, Optimizer |
| 83 | +# trainer = Trainer(optimizer=Optimizer(lr=0.01, algo="SGD")) |
| 84 | +``` |
| 85 | + |
| 86 | +This pattern is powerful enough to describe very complex objects, e.g.: |
| 87 | + |
| 88 | + <details> |
| 89 | + <summary> |
| 90 | +A Full Mask R-CNN described in recursive instantiation (click to expand) |
| 91 | + </summary> |
| 92 | + |
| 93 | +```eval_rst |
| 94 | +.. literalinclude:: ../../configs/common/models/mask_rcnn_fpn.py |
| 95 | + :language: python |
| 96 | + :linenos: |
| 97 | +``` |
| 98 | + |
| 99 | + </details> |
| 100 | + |
| 101 | +There are also objects or logic that cannot be described simply by a dictionary, |
| 102 | +such as reused objects or method calls. They may require some refactoring |
| 103 | +to work with recursive instantiation. |
| 104 | + |
| 105 | +## Using Model Zoo LazyConfigs |
| 106 | + |
| 107 | +We provide some configs in the model zoo using the LazyConfig system, for example: |
| 108 | + |
| 109 | +* [common baselines](../../configs/common/). |
| 110 | +* [new Mask R-CNN baselines](../../configs/new_baselines/) |
| 111 | + |
| 112 | +After installing detectron2, they can be loaded by the model zoo API |
| 113 | +[model_zoo.get_config](../modules/model_zoo.html#detectron2.model_zoo.get_config). |
| 114 | + |
| 115 | +Our model zoo configs follow some simple conventions, e.g. |
| 116 | +`cfg.model` defines a model object, `cfg.dataloader.{train,test}` defines dataloader objects, |
| 117 | +and `cfg.train` contains training options in key-value form. |
| 118 | +We provide a reference training script |
| 119 | +[tools/lazyconfig_train_net.py](../../tools/lazyconfig_train_net.py), |
| 120 | +that can train/eval our model zoo configs. |
| 121 | +It also shows how to support command line value overrides. |
| 122 | + |
| 123 | +Nevertheless, you are free to define any custom structure for your project and use it |
| 124 | +with your own scripts. |
| 125 | + |
| 126 | +To demonstrate the power and flexibility of the new system, we show that |
| 127 | +[a simple config file](../../configs/Misc/torchvision_imagenet_R_50.py) |
| 128 | +can let detectron2 train an ImageNet classification model from torchvision, even though |
| 129 | +detectron2 contains no features about ImageNet classification. |
| 130 | +This can serve as a reference for using detectron2 in other deep learning tasks. |
| 131 | + |
| 132 | +## Summary |
| 133 | + |
| 134 | +By using recursive instantiation to create objects, |
| 135 | +we avoid passing a giant config to many places, because `cfg` is only passed to `instantiate`. |
| 136 | +This has the following benefits: |
| 137 | + |
| 138 | +* It's __non-intrusive__: objects to be constructed are config-agnostic, regular Python |
| 139 | + functions/classes. |
| 140 | + They can even live in other libraries. For example, |
| 141 | + `{"_target_": "torch.nn.Conv2d", "in_channels": 10, "out_channels": 10, "kernel_size": 1}` |
| 142 | + defines a conv layer. |
| 143 | +* __Clarity__ of what function/classes will be called, and what arguments they use. |
| 144 | +* `cfg` doesn't need pre-defined keys and structures. It's valid as long as it translates to valid |
| 145 | + code. This gives a lot more __flexibility__. |
| 146 | +* You can still pass huge dictionaries as arguments, just like the old way. |
| 147 | + |
| 148 | +Putting recursive instantiation together with the Python config file syntax, the config file |
| 149 | +looks a lot like the code that will be executed: |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | +However, the config file just defines dictionaries, which can be easily manipulated further |
| 154 | +by composition or overrides. |
| 155 | +The corresponding code will only be executed |
| 156 | +later when `instantiate` is called. In some way, |
| 157 | +in config files we're writing "editable code" that will be "lazily executed" later when needed. |
| 158 | +That's why we call this system "LazyConfig". |
0 commit comments