Configs#

Configs are instances of the Config class defined in config/config.py. The implementation is largely based on addict, which makes working with nested dictionaries convenient. At their core, configs are essentially nested dictionaries (similar to loaded json objects), with an easy way to access and set nested keys by using attributes. See the example below:

from robomimic.config import Config

# normal way to create nested dictionaries and read values
c = dict()
c["experiment"] = dict()
c["experiment"]["save"] = dict()
c["experiment"]["save"]["enabled"] = True
print("save enabled: {}".format(c["experiment"]["save"]["enabled"]))

# can use dot syntax instead of key lookup to create nested dictionaries
c = Config()
c.experiment.save.enabled = True

# can also use dot syntax to access attributes
print("save enabled: {}".format(c.experiment.save.enabled))

It’s easy to go back and forth between Config objects and jsons as well, which is convenient when saving config objects to disk (this happens when generating new config jsons for training, and when saving the config in a model checkpoint), and loading configs from jsons.

# dump config as a json string
json_string = c.dump()

# dump config to a json file
c.dump(filename="c.json")

# load config from json
import json
json_dic = json.load(json_string)
c = Config(json_dic)

All algorithm config classes (one class per algorithm type) are subclasses of the BaseConfig class (defined in config/base_config.py), which is a subclass of the general Config class. The algorithm config classes are kept in a global registry (more details on this later). The generate_config_templates.py script uses the functionality demonstrated above to easily generate template config jsons for each algorithm, by instantiating the default config object per class. This script should be run any time changes are made to default settings, or new config settings are added. We reproduce the code snippet below.

import os
import json
import robomimic
from robomimic.config import get_all_registered_configs

# store template config jsons in this directory
target_dir = os.path.join(robomimic.__path__[0], "exps/templates/")

# iterate through registered algorithm config classes
all_configs = get_all_registered_configs()
for algo_name in all_configs:
    # make config class for this algorithm
    c = all_configs[algo_name]()
    # dump to json
    json_path = os.path.join(target_dir, "{}.json".format(algo_name))
    c.dump(filename=json_path)

We now go over the general structure of algorithm config classes by dicusssing the BaseConfig class, which has more specific functionality than the general Config class for use with this repository.

Config Factory#

The BaseConfig class has a property and classmethod called ALGO_NAME that must be filled out by all subclasses – this should correspond to the algorithm name for each config, and should match the algorithm name that is passed to register_algo_factory_func at the top of each algorithm implementation file. For example, for BCConfig, ALGO_NAME = "bc". This property is important in order to make sure that all configs that subclass BaseConfig get registered into the REGISTERED_CONFIGS global registry (ALGO_NAME is used as a key to register the class into the registry). This also allows the config_factory function to easily create the appropriate config class for an algorithm – this is the standard entry point for creating config objects in the codebase. While config jsons can also be used to load configs during training (as discussed in the Quick Start section of the documentation), the config_factory function is still used to create an initial config object, which is then updated (see the example below).

import json
from robomimic.config import config_factory

# base config for algorithm with default values
config = config_factory("bc")

# update defaults with config json
with open("/path/to/config.json", "r") as f:
    ext_config_json = json.load(f)
config.update(ext_config_json)

At test-time, when loading a model from a checkpoint, the config is restored by reading the json string from the checkpoint, and then using it to instantiate the config. An example is below (modified slightlyt from config_from_checkpoint in utils/file_utils.py)

import robomimic.utils.file_utils as FileUtils
ckpt_dict = FileUtils.load_dict_from_checkpoint("path/to/ckpt.pth")
config_json = ckpt_dict["config"]
config = config_factory(ckpt_dict["algo_name"], dic=json.loads(config_json))

Config Structure#

The BaseConfig class (and all subclasses) have 4 important sections that need to be filled out – each is a method of the config class. See config/base_config.py (and the relevant algorithm config files like config/bc_config.py) for details on the specific settings under each section.

  • experiment_config(self)

    • This function populates the config.experiment attribute of the config, which has several experiment settings such as the name of the training run, whether to do logging, whether to save models (and how often), whether to render videos, and whether to do rollouts (and how often). The BaseConfig class has a default implementation that usually doesn’t need to be overriden.

  • train_config(self)

    • This function populates the config.train attribute of the config, which has several settings related to the training process, such as the dataset to use for training, and how the data loader should load the data. The BaseConfig class has a default implementation that usually doesn’t need to be overriden.

  • algo_config(self)

    • This function populates the config.algo attribute of the config, and is given to the Algo subclass (see algo/algo.py) for each algorithm through the algo_config argument to the constructor. Any parameter that an algorithm needs to determine its training and test-time behavior should be populated here. This function should be implemented by every subclass.

  • observation_config(self)

    • This function populates the config.observation attribute of the config, and is given to the Algo subclass (see algo/algo.py) for each algorithm through the obs_config argument to the constructor. This portion of the config is used to specify what observation modalities should be used by the networks for training, and how the observation modalities should be encoded by the networks. While the BaseConfig class has a default implementation that usually doesn’t need to be overriden, certain algorithm configs may choose to, in order to have seperate configs for different networks in the algorithm.

Config Locking#

To prevent accidental config modification, each Config object implements a two-level locking mechanism: key-locked and all-locked. A Config object can be put into the key-locked state by calling config.lock_keys(). Under the key-locked state, a Config object does not allow adding new keys but only changing the values of existing keys. A Config object can be put into the all-locked state by calling config.lock(). In this state, the object does not allow adding new keys nor changing existing values.

All Config objects that inherit the constructor of BaseConfig are key-locked by default. Upon finishing constructing the object, it is recommended to put the config object into the all-locked state by calling config.lock(). Config objects also implement context manager functions that allow convenient and controlled config modification within a scope.

  • value_unlocked()

    • This context scope allows values of existing keys to be modified if the Config object is all-locked.

  • unlocked()

    • This context scope allows new keys to be added as well as values of existing keys to be modified, regardless of the locking state.

Minimum Example#

Please see the config tutorial for more information on how to use the Config object, and examples on how the locking mechanism works.