Skip to content

💯 Default Values

👉 Setting default values

Some config fields almost never change for a given project. Think of a standard epsilon, a default activation, or a fixed bias flag. Putting those defaults in the protobuf schema keeps YAML files focused on what actually varies. The generated Pydantic models pick up the same defaults so Python construction stays consistent.

The default needs to be propagated to the generated code. Hence, we'll add the default to the protobuf schema using (pgml.default).

// default.proto
syntax = "proto3";

package defaults;

import "py_gen_ml/extensions.proto";

// Optimizer configuration
message Optimizer {
    // Optimizer type
    string type = 1 [(pgml.default).string = "sgd"];
    // Learning rate
    float learning_rate = 2 [(pgml.default).float = 0.01];
}

The default value will be added to the generated code.

# Autogenerated code. DO NOT EDIT.
import py_gen_ml as pgml


class Optimizer(pgml.YamlBaseModel):
    """Optimizer configuration"""

    type: str = "sgd"
    """Optimizer type"""

    learning_rate: float = 0.01
    """Learning rate"""

In this case, all values have a default, so it is possible to instantiate the class without specifying any values.

from pgml_out.default_base import Optimizer

optimizer = Optimizer()

🔠 Enums

Enum values can be specified using the name of the enum value.

// default_enum.proto
syntax = "proto3";

package defaults_enum;

import "py_gen_ml/extensions.proto";

// Activation function
enum Activation {
    // Rectified Linear Unit
    RELU = 0;
    // Gaussian Error Linear Unit
    GELU = 1;
}

// Linear layer
message Linear {
    // Number of input features
    int32 in_features = 1;
    // Number of output features
    int32 out_features = 2;
    // Activation function
    Activation activation = 3 [(pgml.default).enum = "GELU"];
}
# Autogenerated code. DO NOT EDIT.
import enum

import py_gen_ml as pgml


class Activation(str, enum.Enum):
    """Activation function"""

    RELU = "RELU"
    """Rectified Linear Unit"""

    GELU = "GELU"
    """Gaussian Error Linear Unit"""


class Linear(pgml.YamlBaseModel):
    """Linear layer"""

    in_features: int
    """Number of input features"""

    out_features: int
    """Number of output features"""

    activation: Activation = Activation.GELU
    """Activation function"""

🔌 How defaults interact with the rest of the stack

  • YAML: omit a field and the generated model fills in the proto default when the object is constructed.
  • Patches: a patch can still override a defaulted field. Leaving it unset on the patch keeps the base (or default) value.
  • CLI: command-line overrides win over whatever was loaded from YAML / defaults, same as other fields.

Putting defaults in the schema keeps the single source of truth story intact. That beats scattering the same values across YAML and Python.

🚧 Limitations

It is currently only possible to specify defaults for built-ins such as string, float, int, etc. For message fields, you cannot specify a default value. We leave this feature for future work.