How to Override Hypster Parameter Values at Runtime Using the `values` Dictionary
You can override any parameter defined in a Hypster configuration function by passing a values dictionary to instantiate(), which supports flat keys, dotted-path notation, and nested structures.
The Hypster configuration framework allows you to inject runtime values without modifying your configuration source code. By leveraging the values parameter in the instantiate function, you can override defaults defined in your configuration functions across namespaces. This article explains the mechanics of runtime parameter overriding based on the gilad-rubin/hypster source code.
The Core Override Mechanism
Runtime parameter overriding centers on three components in the Hypster codebase:
instantiateinsrc/hypster/core.py(lines 17‑65): The entry point that receives thevaluesdictionary, creates anHPinstance, and executes the configuration function.HP._get_value_for_paraminsrc/hypster/hp.py(lines 68‑96): Handles lookup logic for parameter values using exact keys, dotted-path keys, or nested dictionary structures.HP.nestinsrc/hypster/hp.py(lines 35‑90): Enables sub-configurations to be overridden with separate dictionaries that merge automatically with parentvalues.
When you call instantiate(config_func, values={...}), the supplied dictionary is stored inside the newly created HP object. Every time a hyper-parameter method like hp.int, hp.select, or hp.multi_float is invoked, the HP object executes _get_value_for_param to resolve the final value.
How Parameter Lookup Works
The HP class implements a hierarchical lookup strategy in _get_value_for_param:
- Exact match: If the requested parameter name (e.g.,
"batch_size") exists as a top-level key invalues, that value is used immediately. - Qualified match: For parameters inside namespaces, the method constructs a full dotted path using the current
namespace_stack. It then checks for a key matching that path (e.g.,"model.lr"). - Nested dict fallback: The dictionary is processed through
unflatten_dict(located insrc/hypster/utils.py), allowing nested structures like{"model": {"lr": 0.1}}to satisfy lookups for"model.lr".
If none of these strategies locate a value, the parameter falls back to its declared default.
Practical Override Patterns
Flat Dictionary with Dotted Keys
You can override deeply nested parameters using dot notation without restructuring the configuration:
from hypster import hp, instantiate
def model_cfg(hp: hp.HP):
batch = hp.int(32, name="batch_size")
lr = hp.float(0.01, name="lr", min=0.0)
family = hp.select(["logistic", "rf", "lr"], name="family", default="logistic")
if family == "rf":
n_estim = hp.int(100, name="n_estimators")
max_d = hp.float(10.0, name="max_depth")
return {"family": family, "rf": {"n_estimators": n_estim, "max_depth": max_d}}
return {"family": family, "lr": {"lr": lr}}
# Override using flat dotted keys
result = instantiate(
model_cfg,
values={"batch_size": 64, "family": "rf", "rf.n_estimators": 200}
)
# Result: {'family': 'rf', 'rf': {'n_estimators': 200, 'max_depth': 10.0}}
Nested Dictionary Structures
Alternatively, supply nested dictionaries that mirror your configuration hierarchy:
result2 = instantiate(
model_cfg,
values={
"batch_size": 64,
"family": "rf",
"rf": {"n_estimators": 250, "max_depth": 12}
}
)
# Result: {'family': 'rf', 'rf': {'n_estimators': 250, 'max_depth': 12}}
Overriding Sub‑Configurations with hp.nest
When using hp.nest to compose configurations, overrides work across namespace boundaries:
def child_cfg(hp: hp.HP):
depth = hp.int(5, name="depth")
return {"depth": depth}
def parent_cfg(hp: hp.HP):
tree_cfg = hp.nest(child_cfg, name="tree")
return {"tree": tree_cfg}
# Override child's parameter through parent values
parent_res = instantiate(
parent_cfg,
values={"tree": {"depth": 9}} # Equivalent to {"tree.depth": 9}
)
# Result: {'tree': {'depth': 9}}
Handling Unknown Parameters
By default, Hypster warns about keys in values that never get accessed during configuration execution. You can control this behavior with the on_unknown parameter:
on_unknown="warn"(default): Emits a warning for unused keys.on_unknown="raise": Raises an exception if any key goes unclaimed.on_unknown="ignore": Silently ignores unused keys.
def strict_cfg(hp: hp.HP):
rate = hp.float(0.1, name="rate", min=0.0, max=1.0, strict=True)
return {"rate": rate}
# This raises because 2.0 exceeds the max bound and is unrecognized
instantiate(strict_cfg, values={"rate": 2.0}, on_unknown="raise")
Complete Working Examples
Below are three self‑contained patterns demonstrating different override strategies.
Example 1: Simple Top‑Level Overrides
from hypster import hp, instantiate
def cfg(hp: hp.HP):
lr = hp.float(0.01, name="lr", min=0.0)
bs = hp.int(32, name="batch_size", min=1)
return {"lr": lr, "batch_size": bs}
params = {"lr": 0.05, "batch_size": 128}
print(instantiate(cfg, values=params))
# → {'lr': 0.05, 'batch_size': 128}
Example 2: Dotted‑Key Overrides for Nested Configs
def child(hp: hp.HP):
depth = hp.int(3, name="depth")
return {"depth": depth}
def parent(hp: hp.HP):
tree_cfg = hp.nest(child, name="tree")
return {"tree": tree_cfg}
print(instantiate(parent, values={"tree.depth": 7}))
# → {'tree': {'depth': 7}}
Example 3: Strict Validation with Runtime Overrides
def cfg_strict(hp: hp.HP):
# strict=True forbids values outside declared bounds
rate = hp.float(0.1, name="rate", min=0.0, max=1.0, strict=True)
return {"rate": rate}
# This will raise a ValueError for out-of-bounds override
try:
instantiate(cfg_strict, values={"rate": 2.0})
except ValueError as e:
print(f"Validation failed: {e}")
Summary
- Pass a
valuesdictionary toinstantiate()to override any default parameter at runtime. - Use flat dotted keys (e.g.,
"rf.n_estimators") or nested dictionaries (e.g.,{"rf": {"n_estimators": 200}}) interchangeably. - The
HP._get_value_for_parammethod insrc/hypster/hp.pyhandles lookup priority: exact match → dotted path → unflattened nested dict. - Use
hp.nestto create composable sub‑configurations that maintain namespace isolation while remaining overridable. - Control validation strictness with
on_unknownand parameter-specificstrictflags.
Frequently Asked Questions
Can I mix dotted keys and nested dictionaries in the same values dictionary?
Yes. Hypster processes the values dictionary uniformly, so {"a.b": 1} and {"a": {"b": 1}} are resolved identically during parameter lookup. You can combine both styles within the same call to instantiate() according to readability preferences.
What happens if I override a parameter that doesn't exist in the configuration?
By default, Hypster emits a warning via on_unknown="warn". If you set on_unknown="raise", the system throws an exception listing the unrecognized keys. Setting on_unknown="ignore" suppresses all feedback for unused keys, which is useful when passing large configuration dictionaries where only a subset of parameters apply.
How do I override parameters in deeply nested sub‑configurations?
Use the hp.nest method to instantiate child configurations within parent namespaces. Then reference the child's parameters using the parent's namespace prefix—either as dotted keys like "tree.submodule.param" or as nested dictionaries under the namespace key. The namespace_stack in the HP class automatically tracks these hierarchical relationships.
Does the strict=True flag affect runtime overrides?
Yes. When a parameter is defined with strict=True, Hypster validates that the overridden value satisfies all constraints (min, max, allowed values) before accepting it. If the runtime value violates these constraints, instantiate() raises a ValueError regardless of whether on_unknown is set to warn or ignore.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →