The monai.bundle
module supports building Python-based workflows via structured configurations.
The main benefits are threefold:
- it provides good readability and usability by separating system parameter settings from the Python code.
- it describes workflow at a relatively high level and allows for different low-level implementations.
- learning paradigms at a higher level such as federated learning and AutoML can be decoupled from the component details.
Content:
Components as part of a workflow can be specified using JSON
or YAML
syntax, for example, a network architecture
definition could be stored in a demo_config.json
file with the following content:
{
"demo_net": {
"_target_": "monai.networks.nets.BasicUNet",
"spatial_dims": 3,
"in_channels": 1,
"out_channels": 2,
"features": [16, 16, 32, 32, 64, 64]
}
}
or alternatively, in YAML
format (demo_config.yaml
):
demo_net:
_target_: monai.networks.nets.BasicUNet
spatial_dims: 3
in_channels: 1
out_channels: 2
features: [16, 16, 32, 32, 64, 64]
The configuration parser can instantiate the component as a Python object:
>>> from monai.bundle import ConfigParser
>>> config = ConfigParser()
>>> config.read_config("demo_config.json")
>>> net = config.get_parsed_content("demo_net", instantiate=True)
BasicUNet features: (16, 16, 32, 32, 64, 64).
>>> print(type(net))
<class 'monai.networks.nets.basic_unet.BasicUNet'>
or additionally, tune the input parameters then instantiate the component:
>>> config["demo_net"]["features"] = [32, 32, 32, 64, 64, 64]
>>> net = config.get_parsed_content("demo_net", instantiate=True)
BasicUNet features: (32, 32, 32, 64, 64, 64).
For more details on the ConfigParser
API, please see monai.bundle.ConfigParser
.
A few characters and keywords are interpreted beyond the plain texts, here are examples of the syntax:
"@preprocessing::transforms::keys"
Description: @
character indicates a reference to another configuration value defined at preprocessing::transforms::keys
.
where ::
indicates a sub-structure of this configuration file. (#
is a synonym for ::
, preprocessing#transforms#keys
refers to the same object.)
"@preprocessing::1"
Description: 1
is referencing as an integer, which is used to index (zero-based indexing) the preprocessing
sub-structure.
Relative reference is supported by starting the reference with #
. For example, @#A
is to use A
at the
same config structure level, and @##A
refers to A
at one level above.
"$print(42)"
Description: $
is a special character to indicate evaluating print(42)
at runtime.
"$[i for i in @datalist]"
Description: Create a list at runtime using the values in datalist
as input.
"$from torchvision.models import resnet18"
Description: $
followed by an import statement is handled slightly differently from the
Python expressions. The imported module resnet18
will be available as a global variable
to the other configuration sections. This is to simplify the use of external modules in the configuration.
The config expressions may use @
to reference other config items. For example, in $lambda x: x + @a + @b
,
@a
and @b
are references to other Python objects and are made available to the anonymous function
as 'globals'.
It's therefore possible to modify the Python objects within an expression, for example,
$lambda x: @my_list.pop() + x
will pop the last element from @my_list
and add it to x
.
"%demo_config.json::demo_net::in_channels"
Description: %
character indicates a macro to replace the current configuration element with the texts at demo_net::in_channels
in the
demo_config.json
file. The replacement is done before instantiating or evaluating the components.
{
"demo_name":{
"_target_": "my.python.module.Class",
"args1": "string",
"args2": 42}
}
Description: This dictionary defines an object with a reference name demo_name
, with an instantiable type
specified at _target_
and with input arguments args1
and args2
.
This dictionary will be instantiated as a Pytorch object at runtime.
_target_
is a required key by monai bundle syntax for the Python object name.
args1
and args2
should be compatible with the Python object to instantiate.
{
"component_name": {
"_target_": "my.module.Class",
"_desc_": "this is a customized class which also triggers 'cudnn_opt' reference",
"_requires_": "@cudnn_opt",
"_disabled_": "true",
"_mode_": "default"}
}
Description: _requires_
, _disabled_
, _desc_
, and _mode_
are optional keys.
_requires_
specifies references (string starts with@
) or Python expression that will be evaluated/instantiated before_target_
object is instantiated. It is useful when the component does not explicitly depend on the other ConfigItems via its arguments, but requires the dependencies to be instantiated/evaluated beforehand._disabled_
specifies a flag to indicate whether to skip the instantiation._desc_
can be used for providing free text descriptions._mode_
specifies the operating mode when the component is instantiated or the callable is called. it currently supports the following values:"default"
(default) -- return the return value of_target_(**kwargs)
"callable"
-- return a callable, either as_target_
itself or, ifkwargs
are provided, as a partial function offunctools.partial(_target_, **kwargs)
. Useful for defining a class or function that will be instantied or called later. User can pre-define some arguments to the_target_
and call it with additional arguments later."debug"
-- execute with debug prompt and return the return value ofpdb.runcall(_target_, **kwargs)
, see alsopdb.runcall
.
Description: Multiple config files may be specified on the command line.
The content of those config files is being merged. When same keys are specifiled in more than one config file,
the value associated with the key is being overridden, in the order config files are specified.
If the desired behaviour is to merge values from both files, the key in second config file should be prefixed with +
.
The value types for the merged contents must match and be both of dict
or both of list
type.
dict
values will be merged via update(), list
values - concatenated via extend().
Here's an example. In this case, "amp" value will be overridden by extra_config.json.
imports
and preprocessing#transforms
lists will be merged. An error would be thrown if the value type in "+imports"
is not list
:
config.json:
{
"amp": "$True"
"imports": [
"$import torch"
],
"preprocessing": {
"_target_": "Compose",
"transforms": [
"$@t1",
"$@t2"
]
},
}
extra_config.json:
{
"amp": "$False"
"+imports": [
"$from monai.networks import trt_compile"
],
"+preprocessing#transforms": [
"$@t3"
]
}
In addition to the Pythonic APIs, a few command line interfaces (CLI) are provided to interact with the bundle. The primary usage is:
python -m monai.bundle COMMANDS
where COMMANDS
is one of the following: run
, verify_metadata
, ckpt_export
, ...
(please see python -m monai.bundle --help
for a list of available options).
The CLI supports flexible use cases, such as overriding configs at runtime and predefining arguments in a file.
To display a usage page for a command, for example run
:
python -m monai.bundle run -- --help
The support is provided by Python Fire, please
make sure the optional dependency is installed, for example,
using pip install monai[fire]
or pip install fire
.
Details on the CLI argument parsing is provided in the
Python Fire Guide.
- Both
YAML
andJSON
are supported, but the advanced features of these formats are not supported. - Using meaningful names for the configuration elements can improve the readability.
- While it is possible to build complex configurations with the bundle syntax, simple structures with sparse uses of expressions or references are preferred.
- For
$import <module>
in the configuration, please make sure there are instructions for the users to install the<module>
if it is not a (optional) dependency of MONAI. - As
#
,::
, and$
might be interpreted differently by theshell
orCLI
tools, may need to add escape characters or quotes for them in the command line, like:"\$torch.device('cuda:1')"
,"'train_part#trainer'"
. - For more details and examples, please see the tutorials.