# machinable.yaml

Let's take a closer look at the features of machinable's central configuration file machinable.yaml which lives at the project directory root:

my-machinable-project
├── ...
└── machinable.yaml

# Module mapping

The component section lists the project's components and their configuration using the following convention: the name determines the python module that contains the component's code, for example:

components:
  - path.to_module:
      config_value: 1
      nested:
        value: 2
  - path.directory:
      config_value: 3
  - models.baseline:
      learning_rate: 0.1

You can group components into modules by appending the directory name to the components key:

# machinable.yaml
components:experiments:
  - optimization:
      data: sinus
components:models:
  - linear_regression:
  - gradient_descent:
      learning_rate: 0.01

The module mapping allows machinable to load and execute code automatically.

# Config inheritance

The module-to-config mapping also enables configuration sharing through inheritance. Consider the following example:

components:
  - base_component:
      config_value: 1
      nested:
        value: 2
  - extended_component^base_component:
      nested:
        value: overwritten
      new_value: 3

Here, the extended component 'inherits' the base_component's configuration using the ^ syntax. The resulting configuration that becomes available to the extended_component would have the following structure :

config_value: 1             # inherited
nested:
   value: overwritten       # inherited but overwritten
new_value: 3                # new value

Config inheritance can be useful in preventing unnecessary configuration repetitions ('I will copy my entire model to make a minor modification') and allows matching class inheritance structures in the component implementation.

# Aliases

In larger projects, module paths can become long or convoluted. To simplify the discription, you can define simpler aliases using the = syntax:

components:
  - base_component=base:
      config_value: 1
  - extended_component^base:
      new_value: 3

# Config references

It is often the case that configuration values depend on other configurations. For example, a model hyperparameter may depend on the dataset that is being used. In these cases, it may be useful to reference configuration values rather than duplicating them so they only have to be adjusted in one place. In the machinable.yaml such config references can be written using the $ symbol. In particular, $.{path} refers to values from the config root while $self.{path} refers to values of the local component. Consider the following example:

dataset: mnist
components:
  - data: $.dataset
components:models:
  - models.example:
      learning_rate: $self._parameter[$.dataset].learning_rate
      _parameter:
        mnist:
          learning_rate: 0.1
        fashion-mnist: $self._parameter[mnist]
        cifar10:
          learning_rate: 0.01

# At runtime, the above configuration will be parsed as:

dataset: mnist
components:
  - data: mnist
components:models:
  - models.example:
      learning_rate: 0.1
      _parameter:
        mnist:
          learning_rate: 0.1
        fashion-mnist:
          learning_rate: 0.1
        cifar10:
          learning_rate: 0.01

Effectively, the dataset name can be adjusted in one place but used elsewhere. Note that you can use the Python convention of a leading underscore (_example) as a weak "internal use" indicator. machinable will hide configuration values that start with an underscore (_) in execution outputs.

TIP

To implement dynamic configuration dependencies, consider using config methods.

# Versions

Components often induce a number of different versions. For example, a model might have a version with and a version without momentum. To manage the configuration of different versions it can be impractical to create multiple components that inherit the configuration from some base component. Instead, we can define configuration patterns inline. To define a version, specify the configuration difference under a key that starts with ~.

components:
  - sgd:
      learning_rate: 0.001
      data:
        name: cifar10
        augmentation: False
      ~mnist:
        data:
          name: mnist
      ~alexnet:
        learning_rate: 0.1
        data:
          name: imagenet

# At runtime, this will result in the following patterns

# ~mnist:
learning_rate: 0.001
data:
  name: mnist
  augmentation: False

# ~alexnet:
learning_rate: 0.1
data:
  name: imagenet
  augmentation: False

You can choose which version is going to be used in the execution task. Learn more about how to execute different versions in the task section.

# Using subfiles

As the machinable.yaml grows, it may be useful to split the file into smaller subfiles. It is possible to include YAML configuration files relative to the machinable.yaml using the $/ syntax:

# machinable.yaml
components:
  - test.example: $/example/included.yaml

# example/included.yaml
learning_rate: 1
alpha: 0.5

# .. will be parsed as

components:
  - test.example:
      learning_rate: 1
      alpha: 0.5