Skip to content

Latest commit

 

History

History
162 lines (129 loc) · 9.75 KB

config.md

File metadata and controls

162 lines (129 loc) · 9.75 KB

Configuration & Registry System

Features

We inherited and modified the configuration system from mmcv, along with a registry-based module-building system. The original mmcv config support parent (base) config, and an easy-to-use file / command-line interface.

Here's an example config chain:

# Content of renbody.yaml (parent config)
dataloader_cfg: # we see the term "dataloader" as one word?
    dataset_cfg: &dataset_cfg
        masks_dir: masks # good naming ^_^
        ratio: 0.5
        bounds: [[-5.0, -5.0, -5.0], [5.0, 5.0, 5.0]] # thinner?

        force_sparse_view: True
        view_sample: [0, 60, 1]
        frame_sample: [0, 150, 1] # only train for a thousand frames

model_cfg:
    sampler_cfg:
        bg_brightness: 0.0
    renderer_cfg:
        bg_brightness: 0.0

val_dataloader_cfg:
    dataset_cfg:
        <<: *dataset_cfg
        mask_bkgd: 0.0
        frame_sample: [0, 150, 30]
    sampler_cfg:
        view_sample: [0, 60, 20]


# Content of 0013_01.yaml (inner config, child of renbody.yaml, parent of 0013_01_obj.yaml)
configs: configs/datasets/renbody/renbody.yaml

dataloader_cfg: # we see the term "dataloader" as one word?
    dataset_cfg: &dataset_cfg
        data_root: data/renbody/0013_01
        images_dir: images_calib

val_dataloader_cfg:
    dataset_cfg:
        <<: *dataset_cfg

# Content of 0013_01_obj.yaml (child of 0013_01.yaml)
configs: configs/datasets/renbody/0013_01.yaml

dataloader_cfg: &dataloader_cfg
    dataset_cfg: &dataset_cfg # ratio: 0.5
        bounds: [[-0.5352, -0.7697, -0.9967], [0.4148, 0.7203, 0.9533]] # !: BATCH

val_dataloader_cfg:
    dataset_cfg:
        <<: *dataset_cfg

We build upon mmcv's system to support some of the missing features:

  • Full inheritance support is added (personally find this very useful):
    • If a parent config has the same key with the parent, the parent's value will be recursively overwritten. For example, the bounds key in 0013_01_obj.yaml will overwrite the bounds in renbody.yaml.
    • Special keywords can be used for acessing the parent's config. Check out configs/specs/solid.yaml for some examples. Note that to use these special keys will lose their effects if formatted, thus we added a magic line # prettier-ignore to prevent this.
    • Adding a _delete_: True in the children will make sure the config system ignore the parent configs of this key.
    • Multiple parent can be used. This is most useful when building experiments configs like configs/exps/4k4d/4k4d_0013_01.yaml.
    • Special keys are supported for extracting information from the config file itself, like {{fileBasenameNoExtension}}. I always put such a config at the end of an experiment config to match the config to the experiment records.
  • Empty yaml files are allowed and ignored (will raise error in mmcv).
  • Extra arguments are automatically ignored (possibly with warning messages).
  • More robust path support:
    • Project root-relative paths. (configs/models/enerf.yaml)
    • Config file relative paths. (enerf.yaml)
    • Absolute config file paths. (/home/<user>/easyvolcap/configs/models/enerf.yaml)
  • An _append_ operator for appending to existing arrays.
  • _ configs are ignored.

Exists in mmcv and useful features:

  • Dict-based command-line interface. (model_cfg.supervisor_cfg.msk_loss_weight=0.01)
  • A type-based building system for registered modules (build_from_cfg will look up the registry for the type string key).
    • This allows for easy switching between modules. (val_dataloader_cfg.dataset_cfg.type=InferenceDataset)
  • A configs key for specifying a file to inherit from (also available from the command line). (configs=configs/specs/orbit.yaml)
  • A _delete_ operator for preventing inheritance.
  • Digital keys for replacing array elements. (model_cfg.network_cfg.network_cfgs.0.type=...)
  • Arrays can be explicitly defined in the command line. (val_dataloader_cfg.dataset_cfg.view_sample=[0, -1, 1])
  • .yaml, .py, .json flavors config files.
    • I personally prefer .yamls due to their simplicity, however, most modern projects use .pys.

Caveats:

  • None in the command line will be parsed to actual None. null will also be parsed to None.
  • None in python config files are straight forward.
  • None in yaml config files are expected to be null.
  • For array indexing [0, -1, 1] will exclude the last element, use [0, null, 1] instead.

Using the Configuration System

Take away:

  • Configurations are all python dict under the hood.
  • Layer by layer we replace values from the inner most (functions default arguments) to the outer most (comman-line args).
  • You can simply think of the config system as a fancier way of defining function arguments in Python. By fancier I mean adding some warning and command line interfaces.

Here's an example of using the config system programmatically and simultaneously marking the function itself as callable from configs.

@catch_throw
@callable_from_cfg
def gui(
    viewer_cfg: dotdict = dotdict(type="VolumetricVideoViewer"),  # use different naming for config here, is this good?
    invokation_type: str = 'test',  # TODO: implement camera and other dataset types

    # Reproducibility configuration
    base_device: str = 'cuda',
    dry_run: bool = False,  # return without hassle
    **kwargs,
):
    runner: "VolumetricVideoRunner" = globals()[invokation_type](kwargs,
                                                                 base_device=base_device,
                                                                 dry_run=True,
                                                                 )  # return the runner (trainer) immediately
    viewer: "VolumetricVideoViewer" = RUNNERS.build(viewer_cfg, runner=runner)  # will start the window
    if dry_run: return runner  # just construct everything, then return

    launcher(**kwargs, runner_function=viewer.run, runner_object=runner)

We provide interfaces for using the config-based building system (with or without registering) directly:

  • Mark a function with the @callable_from_cfg decorator to enjoy the default argument substitution functionality.
  • For a function (whose argument you want to make configurable), use @callable_from_cfg.
  • For a module (or a function returning a module), which will be called later in runtime (i.e. a PyTorch nn.Module), use the registry system.
    • Define a corresponding registry for this type of module. (EMBEDDERS = Registry('embedders'))
    • Register the module with the registry (@EMBEDDERS.register_module()) to make it accessible from a string.
    • We defined a default __init__.py for folders to support fully automatic recursive imports to actually call register_module.
      • You can find such __init__.py lying around throughout the entire project. (easyvolcap/dataloaders/__init__.py)
      • When a new directory contains modules for registration, copy this file to the directory.
      • We register all modules in main.py before doing any compiling & building.
    • The imports for the registry and config system can be found in the official easyvolcap/engine/__init__.py. You can copy the imports of this file to utilized the registry and configuration system.

The configuration system is built in layers:

  • The most default arguments are always written as function defaults in the python code, this is the recommended way for default args.
    • With a complete module, a complete list (physically meaningful) of default args should be provided in this fashion.
    • This allows for easy switching between different modules. (model_cfg.network_cfg.sampler_cfg.type=CostVolumeDepthSampler)
  • The next level is defined in configs/base.yaml, which defines the default structure for a simple nert+latent code representation.
    • To make command-line configuration (and replacement-based configuration) possible, the full structure of the dataloader, model, and runner needs to be specified.
    • Otherwise, something like val_dataloader_cfg.dataset_cfg.type=InferenceDataset will not complain about no val_dataloader key found.
  • Then, we settled on defining model-related and data-related configs separately in configs/models and configs/datasets respectively.
  • The command line arguments are the outermost layer of configuration.
    • Note that special entries are separately defined as --config and --type for the entry point (shorthand: -c and -t).
  • It's also possible to use multiple parent configs (defined by the configs key or separated by , in the entrypoint arguments for -c)
    • Those parent configs will be parsed layer by layer, the latter overwritting the previous if duplicated keys are found.