Hydra (Python)
Hydra-zen
One problem with hydra is that when using the _target_
key in some configuration file foo.yaml
for calling functions, we can only see cfg.foo
in the Python code, where cfg
refers to the OmegaConf
dictionary object loaded for hydra. This makes it hard to go to function definitions, and see documentation for the function. hydra-zen
is one solution for this.
The following is a modified example from this talk, where we use(/nest) one configured object (function_foo
) into the configuration of another (some_class_object
).
from hydra_zen import make_custom_builds_fn from some_module import some_function, some_class builds = make_custom_builds_fn(populate_full_signature=True) some_function_conf = builds(some_function) function_foo = some_function_conf(bar="baz", another_arg=True) some_class_object = builds(some_class, some_function=function_foo)
The top level configuration is them created via the make_config
function. Below, we are creating a configuration object which has one field, arg
, which is populated by the object configured above. We can then use it in some task function (e.g. a function that actually runs the experiment) as follows:
from hydra_zen import make_config, instantiate Config = make_config(some_field=some_class_object) def task_function(cfg: Config): obj = instantiate(cfg) field = obj.some_field #Instiated object from the class some_class
To run programs, we can do the following:
from hydra_zen import launch job = launch(Config, task_function, ["some_field.some_attribute_from_some_class=some_value"]
We can configure nested fields within the list easily.
To use the CLI to run the code above:
import hydra from hydra.core.config_store import ConfigStore from hydra_zen import make_config, instantiate Config = make_config(some_field=some_class_object) cs = ConfigStore.instance() cs.store(name="some_task", node=Config) @hydra.main(config_path=None, config_name="some_task") def task_function(cfg: Config): obj = instantiate(cfg) field = obj.some_field #Instiated object from the class some_class if __name__ == "__main__": task_function()
To run this from the CLI:
python some_task.py some_field.some_attribute_from_some_class=some_value
Hydra will save the config which was used in the code above, assuming the output directory is the default, running the following will give identical results to the code above:
python some_task.py -cp outputs/YYYY-MM-DD/HH-MM-SS/.hydra/ -cn config
(#ASK Is the YAML config saved via hydra-zen a monolithic file? Or is it monolithic even without hydra-zen? If it is, then it is actually helpful when debugging).
We can register configs under groups:
some_function_conf = builds(some_function) some_function_conf_dataset_1 = some_function_conf(name="MNIST") some_function_conf_dataset_2 = some_function_conf(name="CIFAR10") cs.store(group="dataset", name="MNIST_simple", node=some_function_conf_dataset_1) cs.store(group="dataset", name="CIFAR10_simple", node=some_function_conf_dataset_2)
We can then use:
python task_function.py dataset=MNIST_simple dataset.some_field=some_value
To use runtime type checking:
from beartype.vale import Is from typing_extensions import Annotated from hydra_zen import make_custom_builds_fn from hydra_zen.third_party.beartype import validates_with_beartype from hydra_zen import instantiate PositiveInt = Annotated[int, Is[lambda x: x>= 0]] def process_age(age: PositiveInt): return age builds = make_custom_builds_fn(populate_full_signature=True zen_wrappers=validates_with_beartype hydra_convert="all") ConfAge = builds(process_age) instantiate(ConfAge, age=12) # Works instantiate(ConfAge, age=-1) # Does not work