Yet Another Configuration System (YACS)
YACS is a lightweight Python library for defining and managing system configurations, particularly useful in scientific experimentation for reproducibility. It provides a hierarchical CfgNode object for structured configuration and supports loading configurations from both Python files and YAML files. The current version is 0.1.8, with a relatively active release cadence.
Warnings
- breaking Version 0.1.7 introduced explicit support for non-string keys in CfgNode. Prior to this, using non-string keys might have resulted in errors or implicit string conversion, which could be a breaking change for code that relied on or inadvertently used such keys.
- gotcha YACS promotes the principle of "There is only one way to configure the same thing." It's a common footgun to use both YACS's `merge_from_list` for command-line overrides and separate `argparse` arguments for the same configuration options, leading to confusion or unexpected behavior.
- gotcha After loading and merging all configuration options, it's highly recommended to call `cfg.freeze()` to prevent accidental runtime modifications. Forgetting to freeze can lead to configurations changing unexpectedly during execution, hindering reproducibility.
- gotcha There are multiple Python projects named 'YACS' (e.g., for course scheduling or SALOME GUI). Ensure you are importing and using `yacs` from `github.com/rbgirshick/yacs` for the configuration system described here, as their functionalities are entirely distinct.
- gotcha When loading configurations from Python source files (`yacs>=0.1.4`), the module must export a global variable named `cfg` of type `dict` or `CfgNode`. Deviating from this convention will lead to import errors.
Install
-
pip install yacs
Imports
- CfgNode
from yacs.config import CfgNode as CN
Quickstart
from yacs.config import CfgNode as CN
import os
# 1. Define your default configuration in a Python file (e.g., config.py)
_C = CN()
_C.SYSTEM = CN()
_C.SYSTEM.NUM_GPUS = 8
_C.SYSTEM.NUM_WORKERS = 4
_C.TRAIN = CN()
_C.TRAIN.HYPERPARAMETER_1 = 0.1
_C.TRAIN.SCALES = (2, 4, 8, 16)
def get_cfg_defaults():
"""Get a yacs CfgNode object with default values."""
return _C.clone()
# 2. Example usage in your main application
# Simulate an experiment.yaml file
# This content would typically be in a separate file
# with only the overrides.
experiment_yaml_content = """
SYSTEM:
NUM_GPUS: 2
TRAIN:
SCALES: (1, 2, 3, 4)
"""
# Create a dummy experiment.yaml for demonstration
with open("experiment.yaml", "w") as f:
f.write(experiment_yaml_content)
if __name__ == "__main__":
cfg = get_cfg_defaults()
# Merge from an experiment-specific YAML file
cfg.merge_from_file("experiment.yaml")
# Override from a list of key-value pairs (e.g., from command line)
opts = ["SYSTEM.NUM_GPUS", 1, "TRAIN.HYPERPARAMETER_1", 0.5]
cfg.merge_from_list(opts)
# Freeze the configuration to prevent further modification
cfg.freeze()
print("Final Configuration:")
print(cfg)
print(f"Number of GPUs: {cfg.SYSTEM.NUM_GPUS}")
print(f"Training Hyperparameter 1: {cfg.TRAIN.HYPERPARAMETER_1}")
print(f"Training Scales: {cfg.TRAIN.SCALES}")
# Clean up dummy file
os.remove("experiment.yaml")