Source code for sequifier.config.optimizer_config

"""Configuration for explicit parameter routing and optimizer groups."""

from __future__ import annotations

from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, model_validator

from sequifier.config.components import ComponentSpec


[docs]class OptimizerSelector(BaseModel): """Match parameter catalog descriptors; supplied conditions are conjunctive.""" model_config = ConfigDict(extra="forbid", frozen=True) semantic_groups: list[str] | None = None component: Literal["ingestion", "backbone", "decoder"] | None = None parameter_kind: Literal["weight", "bias", "other"] | None = None ndim: int | None = Field(default=None, ge=0) @model_validator(mode="after") def validate_selector(self) -> "OptimizerSelector": if not any( value is not None for value in ( self.semantic_groups, self.component, self.parameter_kind, self.ndim, ) ): raise ValueError( "An optimizer selector must specify at least one condition." ) if self.semantic_groups is not None and ( not self.semantic_groups or any(not pattern for pattern in self.semantic_groups) ): raise ValueError("semantic_groups must contain nonempty patterns.") return self
[docs]class OptimizerGroupSpec(BaseModel): """One named route and the optimizer applied to its parameters.""" model_config = ConfigDict(extra="forbid", frozen=True) id: str = Field(min_length=1) select: OptimizerSelector | Literal["otherwise"] optimizer: ComponentSpec learning_rate: float = Field(gt=0) base_learning_rate: float | None = Field(default=None, ge=0)
[docs]class OptimizerPlan(BaseModel): """Ordered, exhaustive optimizer routes with a final fallback.""" model_config = ConfigDict(extra="forbid", frozen=True) groups: list[OptimizerGroupSpec] = Field(min_length=1) @model_validator(mode="after") def validate_groups(self) -> "OptimizerPlan": identifiers = [group.id for group in self.groups] if len(set(identifiers)) != len(identifiers): raise ValueError("Optimizer group IDs must be unique.") if self.groups[-1].select != "otherwise" or any( group.select == "otherwise" for group in self.groups[:-1] ): raise ValueError("The final optimizer group must select 'otherwise'.") return self
[docs]def resolve_plan_scheduler_arguments( plan: OptimizerPlan, scheduler: ComponentSpec ) -> dict[str, Any]: """Resolve scheduler rate boundaries from the ordered optimizer plan.""" arguments = dict(scheduler.arguments) if scheduler.name in {"OneCycleLR", "CyclicLR"}: if arguments.get("cycle_momentum", False) is not False: raise ValueError( "cycle_momentum must be false with an optimizer plan because " "the composite optimizer has no shared momentum setting." ) arguments["cycle_momentum"] = False if scheduler.name == "CyclicLR": if "base_lr" in arguments or "max_lr" in arguments: raise ValueError( "global_training.scheduler.base_lr and max_lr must be omitted " "with an optimizer plan; set rates in each optimizer group." ) missing = [ group.id for group in plan.groups if group.base_learning_rate is None ] if missing: raise ValueError( "CyclicLR requires base_learning_rate in every optimizer group; " f"missing: {missing!r}." ) for group in plan.groups: assert group.base_learning_rate is not None if group.base_learning_rate >= group.learning_rate: raise ValueError( f"Optimizer group {group.id!r} must have base_learning_rate " "less than learning_rate for CyclicLR." ) arguments["base_lr"] = [group.base_learning_rate for group in plan.groups] arguments["max_lr"] = [group.learning_rate for group in plan.groups] else: if any(group.base_learning_rate is not None for group in plan.groups): raise ValueError( "Optimizer group base_learning_rate is only used with CyclicLR." ) if scheduler.name == "OneCycleLR": if "max_lr" in arguments: raise ValueError( "global_training.scheduler.max_lr must be omitted with an " "optimizer plan; each group's learning_rate is its peak rate." ) arguments["max_lr"] = [group.learning_rate for group in plan.groups] return arguments