ML Collections

repository·master·Indexed 21 days ago

https://github.com/google/ml_collections

A library of Python collections designed for machine learning use cases. It provides specialized configuration management tools, including ConfigDict for dot-based access and type safety, FrozenConfigDict for immutable configurations, and FieldReference for shared values and lazy computation. The library also includes config_flags for defining and overriding configurations via the command line using DEFINE_config_file and DEFINE_config_dict.

Tokens
4.5K
Snippets
15
Records
20
Agent score
77%

What's inside ml_collections

  1. Use FieldReference and placeholders for shared values

    master

    A FieldReference allows multiple fields to point to the same underlying value. If the value of the reference changes, all fields pointing to it will reflect the change.

    • placeholder(type): A shortcut to create a FieldReference with a None default value and a specific type constraint.
    • Indirection Note: If you assign a FieldReference to another field via standard assignment (e.g., cfg.field2 = cfg.field1), the indirection is lost and it becomes a standard value field. To maintain the link, ensure you are working with the reference object itself.

    To change the value of a FieldReference, use .set(value). To retrieve the actual value, use .get().

    from ml_collections import config_dict
    
    placeholder = config_dict.FieldReference(0)
    cfg = config_dict.ConfigDict()
    cfg.placeholder = placeholder
    cfg.optional = config_dict.placeholder(int)
    
    cfg.optional = 1555
    cfg.placeholder = 1  # Changes the value for all fields tied to this placeholder
  2. Implement lazy computation with FieldReferences

    master

    You can perform lazy computations by using FieldReference objects within operations. This allows values to be re-calculated automatically when the underlying reference changes.

    Using FieldReferences directly

    When you perform an operation (like + or *) on a FieldReference, the result is a new FieldReference that represents the computation.

    • Use .get() to execute the operation and get the result.
    • Use .set() on the original reference to update the entire computation chain.

    Using ConfigDict.get_ref()

    Within a ConfigDict, you can trigger lazy evaluation of specific fields using .get_ref('field_name').

    • get_ref(name): Creates a bi-directional dependency. Changing the referenced field will update the computed field, but changing the computed field will also change the referenced field (one-way reference gotcha).
    • get_oneway_ref(name): Creates a one-way dependency. Changing the computed field will not affect the original referenced field.

    Warning on Cycles: You cannot create cycles. Attempting to assign a computed field back to one of its own dependencies will raise a config_dict.MutabilityError.

    from ml_collections import config_dict
    
    config = config_dict.ConfigDict()
    config.integer_field = 2
    config.float_field = 2.5
    
    # Standard assignment (not lazy)
    config.no_lazy = config.integer_field * config.float_field
    
    # Lazy evaluation using get_ref
    config.lazy_integer = config.get_ref('integer_field') * config.float_field
    
    config.integer_field = 3
    print(config.no_lazy)     # 5.0 (uses original value)
    print(config.lazy_integer) # 7.5 (re-evaluated)
  3. Override configuration values via command line

    master

    Once a configuration is defined using DEFINE_config_file or DEFINE_config_dict, you can override its values using standard command-line flag syntax.

    Non-boolean values

    Use either -(-)config.field=value or -(-)config.field value.

    Boolean values

    • Set to True: Use -(-)config.boolean_field.
    • Set to False: Use -(-)noconfig.boolean_field.
    • Explicit assignment: Use -(-)config.boolean_field=true or -(-)config.boolean_field=False.

    Note: -(-)config.boolean_field value is not supported.

    Overriding Tuples

    Tuples can be overridden by passing the tuple representation as a string. Nested tuples are also supported.

    python script.py --my_config=config.py \
                     --my_config.field1=8 \
                     --my_config.nested.field=2.1 \
                     --my_config.tuple='(1, 2, (1, 2))'
  4. Build the ML Collections documentation locally

    master

    To generate the documentation locally, ensure you have pandoc installed on your system, install the necessary Python dependencies, and use make to build the HTML files.

    # 1. Install requirements
    pip install -r ml_collections/docs/requirements.txt
    
    # 2. Ensure pandoc is installed (system level)
    # (e.g., brew install pandoc or sudo apt-get install pandoc)
    
    # 3. Generate documentation
    make html
  5. Pass parameters to `get_config()` using colon syntax

    master

    You can parameterize the get_config() function in your configuration files by appending a value after a colon in the file path. The value provided after the colon is passed as an argument to get_config().

    If no colon is provided, no value is passed (which will cause a TypeError if get_config expects an argument).

    Example Config File (config.py):

    def get_config(config_string):
      structures = {
          'linear': config_dict.ConfigDict({'type': 'linear'}),
          'lstm': config_dict.ConfigDict({'type': 'lstm'})
      }
      return structures[config_string]

    Command Line Usage:

    python script.py -- --config=config.py:linear
    python script.py -- --config=path_to_config.py:linear \
                        --config.model_config.output_size=256
  6. Use ConfigDict for experiment configurations

    master

    The ConfigDict class provides a "dict-like" data structure with dot-based access to nested elements. It is designed for expressing configurations for models and experiments.

    Key features include:

    • Dot-based access: Access nested fields using cfg.field.subfield.
    • Type safety: Assigning a value of an incompatible type raises a TypeError (e.g., assigning a string to an integer field).
    • Type conversion exception: int values can be assigned to float fields and will be automatically converted.
    • Locking: Use .lock() to prevent adding or deleting fields, which helps catch spelling mistakes.
    • Human-readable printing: Prints in a valid YAML format.
    • Keyword argument support: Fields can be passed using the ** operator.
    from ml_collections import config_dict
    
    cfg = config_dict.ConfigDict()
    cfg.float_field = 12.6
    cfg.integer_field = 123
    cfg.nested = config_dict.ConfigDict()
    cfg.nested.string_field = 'tom'
    
    print(cfg.integer_field)  # 123
    print(cfg['integer_field'])  # 123
  7. Avoid pickling errors by defining types inside `get_config()`

    master

    When using distributed programming frameworks like Launchpad or Apache Beam, avoid defining custom classes (like @dataclasses.dataclass) at the module level of your configuration file. Because config files are not imported via standard Python mechanisms, serialization libraries like cloudpickle may fail to reconstruct these types, leading to ImportError during unpickling.

    Correct Pattern: Define the class inside the get_config() function so it is serialized along with the values.

    def get_config():
      # Define the class INSIDE the function
      @dataclasses.dataclass
      class MyRecord:
        num_balloons: int
        color: str
    
      return MyRecord(num_balloons=99, color='red')
  8. Use FieldReference for referencing configuration fields

    master

    The FieldReference class allows you to create references to specific fields within a configuration. This enables complex configuration patterns where one field's value depends on another.

    Common operations include:

    • Accessing values: Use .get() to retrieve the current value of the referenced field.
    • Type conversion: Use .to_int(), .to_float(), or .to_str() to cast the referenced value to a specific type.
    • Cycle detection: Use .has_cycle() to ensure that references do not create infinite loops.
  9. Use FrozenConfigDict for immutable configurations

    master

    A FrozenConfigDict is an immutable and hashable version of ConfigDict. It is useful when you want to ensure a configuration cannot be modified after creation.

    When initializing a FrozenConfigDict with a dictionary containing lists or sets, it automatically converts them to immutable types (e.g., lists become tuples, sets become frozensets).

    Converting between types:

    • To turn a FrozenConfigDict back into a mutable ConfigDict, use .as_configdict() or pass the frozen object to the ConfigDict() constructor.
    • To create a FrozenConfigDict from an existing ConfigDict, pass the ConfigDict instance to the FrozenConfigDict() constructor.
    from ml_collections import config_dict
    
    initial_dictionary = {
        'int': 1,
        'list': [1, 2],
    }
    
    cfg = config_dict.ConfigDict(initial_dictionary)
    frozen_dict = config_dict.FrozenConfigDict(initial_dictionary)
    
    # FrozenConfigDict is immutable
    try:
      frozen_dict.int = 2
    except AttributeError as e:
      print(e)
    
    # Converting back to mutable
    thawed_cfg = frozen_dict.as_configdict()
  10. Compare ConfigDict objects for equality

    master

    You can check if two ConfigDict or FrozenConfigDict objects are equal using the == operator.

    • == operator: Compares the computed values. Equality is satisfied if the resulting values are the same, even if the underlying computation paths differ.
    • .eq_as_configdict(): Use this method to treat both objects as ConfigDict for comparison. This is useful when comparing a FrozenConfigDict with a ConfigDict, as == might distinguish between the types even if their contents are logically equivalent.
    from ml_collections import config_dict
    
    cfg_1 = config_dict.ConfigDict()
    cfg_1.a = 1
    cfg_1.b = cfg_1.get_ref('a') + 2
    
    cfg_2 = config_dict.ConfigDict()
    cfg_2.a = 1
    cfg_2.b = cfg_2.get_ref('a') * 3
    
    # True because both result in b=3
    print(cfg_1 == cfg_2)
  11. Lock and unlock ConfigDict fields

    master

    The .lock() method prevents the addition or deletion of new fields in a ConfigDict. However, existing fields can still have their values modified.

    To temporarily add or delete fields while the config is locked, use the .unlocked() context manager.

    import copy
    from ml_collections import config_dict
    
    cfg = config_dict.ConfigDict()
    cfg.integer_field = 123
    
    cfg.lock()
    try:
      cfg.new_field = 124  # Raises AttributeError
    except AttributeError as e:
      print(e)
    
    with cfg.unlocked():
      cfg.new_field = 1555  # Works fine
  12. Define configuration dictionaries with DEFINE_config_dict

    master
    Use ml_collections.config_flags.DEFINE_config_dict to define a configuration dictionary as a command-line flag. This allows you to pass a ConfigDict object directly via the command line, typically using a serialized format or specific flag syntax supported by the underlying flag parser.