Configuration System¤
Workshop uses a unified, type-safe configuration system built on Pydantic. This system provides validation, serialization, and a consistent interface across all components.
-
Type-Safe
Pydantic validation ensures correctness at creation time
-
Serializable
Easy save/load to YAML or JSON formats
-
Composable
Hierarchical configs with inheritance and merging
-
Self-Documenting
Field descriptions and constraints built-in
Configuration Architecture¤
The configuration system is organized hierarchically:
graph TB
subgraph "Base Configuration"
BC[BaseConfiguration]
BC -->|common fields| NAME[name]
BC -->|common fields| TYPE[type]
BC -->|common fields| DESC[description]
BC -->|common fields| META[metadata]
end
subgraph "Model Configurations"
MC[ModelConfiguration]
BC -->|inherits| MC
MC -->|defines| ARCH[Architecture]
MC -->|defines| PARAMS[Parameters]
end
subgraph "Training Configurations"
TC[TrainingConfiguration]
BC -->|inherits| TC
TC -->|contains| OC[OptimizerConfiguration]
TC -->|optional| SC[SchedulerConfiguration]
end
subgraph "Data Configurations"
DC[DataConfiguration]
BC -->|inherits| DC
DC -->|defines| DATASET[Dataset Settings]
DC -->|defines| AUG[Augmentation]
end
subgraph "Experiment Configuration"
EC[ExperimentConfiguration]
BC -->|inherits| EC
EC -->|composes| MC
EC -->|composes| TC
EC -->|composes| DC
end
BaseConfiguration¤
All configurations inherit from BaseConfiguration:
from workshop.generative_models.core.configuration import BaseConfiguration
from pydantic import Field
class BaseConfiguration:
"""Base class for all configurations."""
# Core fields
name: str = Field(..., description="Unique name")
type: ConfigurationType = Field(..., description="Configuration type")
description: str | None = Field(None, description="Human-readable description")
version: str = Field("1.0.0", description="Configuration version")
# Metadata
tags: list[str] = Field(default_factory=list, description="Tags for categorization")
metadata: dict[str, Any] = Field(
default_factory=dict,
description="Non-functional metadata for tracking"
)
Key Features:
- Required Fields:
nameandtypeare required - Optional Metadata: Non-functional information for tracking
- Version Control: Built-in versioning support
- Tags: For organizing and searching configurations
YAML Serialization¤
Save and load configurations from YAML:
from workshop.generative_models.core.configuration import ModelConfiguration
from pathlib import Path
# Create configuration
config = ModelConfiguration(
name="vae_config",
model_class="workshop.generative_models.models.vae.base.VAE",
input_dim=(28, 28, 1),
hidden_dims=[256, 128],
output_dim=64,
parameters={"beta": 1.0},
)
# Save to YAML
config.to_yaml("configs/vae_config.yaml")
# Load from YAML
loaded_config = ModelConfiguration.from_yaml("configs/vae_config.yaml")
assert config.name == loaded_config.name
Example YAML file:
name: vae_config
type: model
model_class: workshop.generative_models.models.vae.base.VAE
input_dim: [28, 28, 1]
hidden_dims: [256, 128]
output_dim: 64
activation: gelu
dropout_rate: 0.1
use_batch_norm: true
parameters:
beta: 1.0
rngs_seeds:
params: 0
dropout: 1
Configuration Merging¤
Merge configurations for easy experimentation:
# Base configuration
base_config = ModelConfiguration(
name="base_vae",
model_class="workshop.generative_models.models.vae.base.VAE",
input_dim=(28, 28, 1),
hidden_dims=[256, 128],
output_dim=64,
)
# Override specific fields
overrides = {
"hidden_dims": [512, 256, 128],
"output_dim": 128,
"parameters": {"beta": 2.0},
}
# Merge configurations
new_config = base_config.merge(overrides)
print(new_config.hidden_dims) # [512, 256, 128]
print(new_config.parameters) # {"beta": 2.0}
ModelConfiguration¤
ModelConfiguration defines model architecture and parameters:
from workshop.generative_models.core.configuration import ModelConfiguration
config = ModelConfiguration(
name="my_vae",
model_class="workshop.generative_models.models.vae.base.VAE",
# Architecture
input_dim=(28, 28, 1),
hidden_dims=[512, 256, 128],
output_dim=64,
# Common parameters
activation="gelu",
dropout_rate=0.1,
use_batch_norm=True,
# RNG seeds for reproducibility
rngs_seeds={"params": 42, "dropout": 1},
# Model-specific parameters
parameters={
"beta": 1.0, # VAE beta parameter
"kl_weight": 0.5,
},
# Optional metadata
metadata={
"experiment_id": "exp_001",
"notes": "Testing higher latent dim",
},
)
Field Reference¤
| Field | Type | Required | Description |
|---|---|---|---|
name |
str |
✅ | Unique configuration name |
model_class |
str |
✅ | Fully qualified model class |
input_dim |
int \| tuple |
✅ | Input dimensions |
hidden_dims |
list[int] |
❌ | Hidden layer dimensions |
output_dim |
int \| tuple |
❌ | Output dimensions |
activation |
str |
❌ | Activation function |
dropout_rate |
float |
❌ | Dropout rate |
use_batch_norm |
bool |
❌ | Use batch normalization |
rngs_seeds |
dict[str, int] |
❌ | RNG seeds |
parameters |
dict[str, Any] |
❌ | Model-specific params |
Parameters vs Metadata¤
Important Distinction:
# CORRECT: Use parameters for functional configuration
config = ModelConfiguration(
name="vae",
model_class="...",
input_dim=(28, 28, 1),
# Functional parameters that affect model behavior
parameters={
"beta": 1.0,
"kl_weight": 0.5,
"reconstruction_loss": "mse",
},
# Non-functional metadata for tracking
metadata={
"experiment_id": "exp_001",
"dataset_version": "v2.1",
"notes": "Testing lower KL weight",
}
)
# WRONG: Don't nest model parameters in metadata
config = ModelConfiguration(
name="vae",
model_class="...",
input_dim=(28, 28, 1),
metadata={
"vae_params": {"beta": 1.0} # DON'T DO THIS
}
)
Guidelines:
parameters: Functional configuration affecting model behaviormetadata: Non-functional information for experiment tracking
Model-Specific Parameters¤
Different models require different parameters:
# VAE configuration
vae_config = ModelConfiguration(
name="vae",
model_class="workshop.generative_models.models.vae.base.VAE",
input_dim=(28, 28, 1),
hidden_dims=[256, 128],
output_dim=64,
parameters={
"beta": 1.0, # KL weight
"kl_weight": 0.5, # Additional KL scaling
},
)
# GAN configuration
gan_config = ModelConfiguration(
name="gan",
model_class="workshop.generative_models.models.gan.base.GAN",
input_dim=(28, 28, 1),
hidden_dims=[256, 128],
output_dim=(28, 28, 1),
parameters={
"noise_dim": 100, # Latent noise dimension
"label_smoothing": 0.1, # Label smoothing for discriminator
},
)
# Diffusion configuration
diffusion_config = ModelConfiguration(
name="diffusion",
model_class="workshop.generative_models.models.diffusion.ddpm.DDPM",
input_dim=(28, 28, 1),
hidden_dims=[128, 256, 512],
output_dim=(28, 28, 1),
parameters={
"noise_steps": 1000, # Diffusion steps
"beta_start": 0.0001, # Noise schedule start
"beta_end": 0.02, # Noise schedule end
"schedule_type": "linear", # Noise schedule type
},
)
# Flow configuration
flow_config = ModelConfiguration(
name="flow",
model_class="workshop.generative_models.models.flow.realnvp.RealNVP",
input_dim=(28, 28, 1),
hidden_dims=[256, 256],
output_dim=(28, 28, 1),
parameters={
"num_coupling_layers": 8, # Number of coupling layers
"mask_type": "checkerboard", # Coupling mask pattern
},
)
OptimizerConfiguration¤
Configure optimizers with full type safety:
from workshop.generative_models.core.configuration import OptimizerConfiguration
# Adam optimizer (recommended)
adam_config = OptimizerConfiguration(
name="adam_optimizer",
optimizer_type="adam",
learning_rate=1e-3,
beta1=0.9,
beta2=0.999,
eps=1e-8,
)
# AdamW with weight decay
adamw_config = OptimizerConfiguration(
name="adamw_optimizer",
optimizer_type="adamw",
learning_rate=3e-4,
weight_decay=0.01,
beta1=0.9,
beta2=0.999,
)
# SGD with momentum
sgd_config = OptimizerConfiguration(
name="sgd_optimizer",
optimizer_type="sgd",
learning_rate=0.1,
momentum=0.9,
nesterov=True,
)
# RMSProp
rmsprop_config = OptimizerConfiguration(
name="rmsprop_optimizer",
optimizer_type="rmsprop",
learning_rate=1e-3,
initial_accumulator_value=0.1,
)
Gradient Clipping¤
Add gradient clipping to any optimizer:
# Clip by global norm (recommended)
clipped_adam = OptimizerConfiguration(
name="clipped_adam",
optimizer_type="adam",
learning_rate=1e-3,
gradient_clip_norm=1.0, # Clip to norm of 1.0
)
# Clip by value
value_clipped = OptimizerConfiguration(
name="value_clipped",
optimizer_type="adam",
learning_rate=1e-3,
gradient_clip_value=0.5, # Clip values to [-0.5, 0.5]
)
Optimizer Field Reference¤
| Field | Type | Required | Description |
|---|---|---|---|
optimizer_type |
str |
✅ | Optimizer type (adam, adamw, sgd, etc.) |
learning_rate |
float |
✅ | Learning rate (must be > 0) |
weight_decay |
float |
❌ | Weight decay (L2 penalty) |
beta1 |
float |
❌ | Beta1 for Adam (default: 0.9) |
beta2 |
float |
❌ | Beta2 for Adam (default: 0.999) |
eps |
float |
❌ | Epsilon for numerical stability |
momentum |
float |
❌ | Momentum for SGD |
nesterov |
bool |
❌ | Use Nesterov momentum |
gradient_clip_norm |
float \| None |
❌ | Gradient clipping by norm |
gradient_clip_value |
float \| None |
❌ | Gradient clipping by value |
SchedulerConfiguration¤
Configure learning rate schedules:
from workshop.generative_models.core.configuration import SchedulerConfiguration
# Cosine schedule with warmup (recommended)
cosine_schedule = SchedulerConfiguration(
name="cosine_warmup",
scheduler_type="cosine",
warmup_steps=1000,
cycle_length=50000,
min_lr_ratio=0.1,
)
# Linear decay
linear_schedule = SchedulerConfiguration(
name="linear_decay",
scheduler_type="linear",
warmup_steps=500,
total_steps=10000,
min_lr_ratio=0.0,
)
# Exponential decay
exponential_schedule = SchedulerConfiguration(
name="exponential",
scheduler_type="exponential",
decay_rate=0.95,
decay_steps=1000,
)
# Step decay
step_schedule = SchedulerConfiguration(
name="step_decay",
scheduler_type="step",
step_size=5000,
gamma=0.1,
)
# MultiStep decay
multistep_schedule = SchedulerConfiguration(
name="multistep",
scheduler_type="multistep",
milestones=[10000, 20000, 30000],
gamma=0.1,
)
Scheduler Field Reference¤
| Field | Type | Required | Description |
|---|---|---|---|
scheduler_type |
str |
✅ | Schedule type (cosine, linear, etc.) |
warmup_steps |
int |
❌ | Number of warmup steps |
min_lr_ratio |
float |
❌ | Minimum LR as ratio of initial |
cycle_length |
int \| None |
❌ | Cycle length for cosine |
total_steps |
int \| None |
❌ | Total steps for linear |
decay_rate |
float |
❌ | Decay rate for exponential |
decay_steps |
int |
❌ | Decay steps for exponential |
step_size |
int |
❌ | Step size for step schedule |
gamma |
float |
❌ | Gamma for step/multistep |
milestones |
list[int] |
❌ | Milestones for multistep |
TrainingConfiguration¤
Compose training configurations from optimizer and scheduler:
from workshop.generative_models.core.configuration import (
TrainingConfiguration,
OptimizerConfiguration,
SchedulerConfiguration,
)
# Create optimizer config
optimizer = OptimizerConfiguration(
name="adamw",
optimizer_type="adamw",
learning_rate=3e-4,
weight_decay=0.01,
gradient_clip_norm=1.0,
)
# Create scheduler config
scheduler = SchedulerConfiguration(
name="cosine_warmup",
scheduler_type="cosine",
warmup_steps=1000,
cycle_length=50000,
min_lr_ratio=0.1,
)
# Compose training configuration
training_config = TrainingConfiguration(
name="vae_training",
batch_size=128,
num_epochs=100,
optimizer=optimizer, # Required
scheduler=scheduler, # Optional
save_frequency=5000,
log_frequency=100,
checkpoint_dir="./checkpoints",
max_checkpoints=5,
use_wandb=False,
)
Training Field Reference¤
| Field | Type | Required | Description |
|---|---|---|---|
batch_size |
int |
✅ | Batch size (must be ≥ 1) |
num_epochs |
int |
✅ | Number of epochs (must be ≥ 1) |
optimizer |
OptimizerConfiguration |
✅ | Optimizer configuration |
scheduler |
SchedulerConfiguration \| None |
❌ | LR scheduler configuration |
gradient_clip_norm |
float \| None |
❌ | Global gradient clipping norm |
checkpoint_dir |
Path |
❌ | Checkpoint directory |
save_frequency |
int |
❌ | Save checkpoint every N steps |
max_checkpoints |
int |
❌ | Maximum checkpoints to keep |
log_frequency |
int |
❌ | Log metrics every N steps |
use_wandb |
bool |
❌ | Use Weights & Biases |
wandb_project |
str \| None |
❌ | W&B project name |
DataConfiguration¤
Configure data loading and preprocessing:
from workshop.generative_models.core.configuration import DataConfiguration
from pathlib import Path
data_config = DataConfiguration(
name="mnist_data",
dataset_name="mnist",
data_dir=Path("./data"),
split="train",
# Data loading
num_workers=4,
prefetch_factor=2,
pin_memory=True,
# Data splits
validation_split=0.1,
test_split=0.1,
# Augmentation
augmentation=True,
augmentation_params={
"random_flip": True,
"random_rotation": 15,
"random_crop": True,
},
)
ExperimentConfiguration¤
Compose complete experiments:
from workshop.generative_models.core.configuration import (
ExperimentConfiguration,
ModelConfiguration,
TrainingConfiguration,
DataConfiguration,
)
# Create component configurations
model_config = ModelConfiguration(...)
training_config = TrainingConfiguration(...)
data_config = DataConfiguration(...)
# Compose experiment
experiment = ExperimentConfiguration(
name="vae_experiment_001",
model_cfg=model_config,
training_cfg=training_config,
data_cfg=data_config,
# Experiment settings
seed=42,
deterministic=True,
output_dir=Path("./experiments/vae_001"),
# Tracking
track_carbon=True,
track_memory=True,
# Metadata
description="Baseline VAE experiment on MNIST",
tags=["vae", "mnist", "baseline"],
)
Configuration Registry¤
Manage configurations centrally:
from workshop.generative_models.core.configuration import (
ConfigurationRegistry,
ConfigurationType,
)
# Create registry
registry = ConfigurationRegistry()
# Register configurations
registry.register(model_config)
registry.register(training_config)
registry.register(data_config)
# Retrieve configurations
retrieved_model = registry.get("vae_config", ConfigurationType.MODEL)
retrieved_training = registry.get("vae_training", ConfigurationType.TRAINING)
# List all configurations
all_configs = registry.list_configs()
print(all_configs) # ['model/vae_config', 'training/vae_training', ...]
# List by type
model_configs = registry.list_configs(ConfigurationType.MODEL)
print(model_configs) # ['vae_config', 'gan_config', ...]
Loading from Directory¤
Load all configurations from a directory:
# Directory structure:
# configs/
# ├── model_vae.yaml
# ├── model_gan.yaml
# ├── training_default.yaml
# ├── training_fast.yaml
# └── data_mnist.yaml
registry = ConfigurationRegistry()
registry.load_from_directory("./configs")
# All configurations automatically registered
vae_config = registry.get("vae", ConfigurationType.MODEL)
default_training = registry.get("default", ConfigurationType.TRAINING)
Configuration Templates¤
Create reusable configuration templates:
# Register template
registry.register_template(
name="standard_vae_template",
template={
"model_class": "workshop.generative_models.models.vae.base.VAE",
"activation": "gelu",
"dropout_rate": 0.1,
"use_batch_norm": True,
"parameters": {
"beta": 1.0,
},
}
)
# Create configuration from template
vae_config = registry.create_from_template(
template_name="standard_vae_template",
config_class=ModelConfiguration,
# Override specific fields
name="my_vae",
input_dim=(28, 28, 1),
hidden_dims=[256, 128],
output_dim=64,
)
Custom Configurations¤
Create custom configuration classes:
from workshop.generative_models.core.configuration import (
BaseConfiguration,
ConfigurationType,
)
from pydantic import Field
class ProteinModelConfiguration(BaseConfiguration):
"""Configuration for protein modeling."""
type: ConfigurationType = Field(ConfigurationType.MODEL, frozen=True)
# Protein-specific fields
sequence_length: int = Field(..., description="Protein sequence length")
num_residues: int = Field(20, description="Number of residue types")
secondary_structure: bool = Field(True, description="Use secondary structure")
# Structure prediction
num_recycles: int = Field(3, description="Number of recycle iterations")
use_templates: bool = Field(False, description="Use template structures")
# Custom validation
@field_validator("sequence_length")
def validate_sequence_length(cls, v):
if v <= 0 or v > 10000:
raise ValueError("sequence_length must be in (0, 10000]")
return v
# Use custom configuration
protein_config = ProteinModelConfiguration(
name="alphafold_like",
sequence_length=256,
num_residues=20,
secondary_structure=True,
num_recycles=3,
)
Configuration Validation¤
Pydantic automatically validates configurations:
from pydantic import ValidationError
try:
# Invalid optimizer type
bad_config = OptimizerConfiguration(
name="bad_optimizer",
optimizer_type="invalid_type", # Not a valid optimizer
learning_rate=1e-3,
)
except ValidationError as e:
print(e)
# ValidationError: optimizer_type must be one of {...}
try:
# Invalid learning rate
bad_config = OptimizerConfiguration(
name="bad_lr",
optimizer_type="adam",
learning_rate=-1.0, # Must be > 0
)
except ValidationError as e:
print(e)
# ValidationError: learning_rate must be greater than 0
try:
# Invalid batch size
bad_config = TrainingConfiguration(
name="bad_batch",
batch_size=0, # Must be >= 1
num_epochs=10,
optimizer=optimizer_config,
)
except ValidationError as e:
print(e)
# ValidationError: batch_size must be greater than or equal to 1
Custom Validators¤
Add custom validation logic:
from pydantic import field_validator
class CustomModelConfiguration(ModelConfiguration):
"""Model configuration with custom validation."""
@field_validator("hidden_dims")
def validate_hidden_dims(cls, v):
"""Ensure hidden dims are decreasing."""
if not all(v[i] >= v[i+1] for i in range(len(v)-1)):
raise ValueError("hidden_dims must be non-increasing")
return v
@field_validator("dropout_rate")
def validate_dropout(cls, v):
"""Ensure reasonable dropout rate."""
if v < 0.0 or v > 0.5:
raise ValueError("dropout_rate should be in [0.0, 0.5]")
return v
# Valid configuration
config = CustomModelConfiguration(
name="valid",
model_class="...",
input_dim=(28, 28, 1),
hidden_dims=[512, 256, 128], # Decreasing
output_dim=64,
dropout_rate=0.2, # Reasonable
)
# Invalid configuration
try:
config = CustomModelConfiguration(
name="invalid",
model_class="...",
input_dim=(28, 28, 1),
hidden_dims=[128, 256, 512], # Increasing!
output_dim=64,
)
except ValidationError as e:
print("Validation failed:", e)
Best Practices¤
Configuration Organization¤
Organize configurations by purpose:
configs/
├── models/
│ ├── vae_small.yaml
│ ├── vae_large.yaml
│ ├── gan_dcgan.yaml
│ └── diffusion_ddpm.yaml
├── training/
│ ├── fast_training.yaml
│ ├── default_training.yaml
│ └── long_training.yaml
├── data/
│ ├── mnist.yaml
│ ├── cifar10.yaml
│ └── imagenet.yaml
└── experiments/
├── exp_001.yaml
├── exp_002.yaml
└── exp_003.yaml
Naming Conventions¤
Use clear, descriptive names:
# Good names
config = ModelConfiguration(name="vae_beta10_mnist")
config = TrainingConfiguration(name="fast_training_10epochs")
config = OptimizerConfiguration(name="adam_lr1e3_clip1")
# Bad names
config = ModelConfiguration(name="config1")
config = TrainingConfiguration(name="training")
config = OptimizerConfiguration(name="opt")
Version Control¤
Track configuration versions:
config = ModelConfiguration(
name="vae_v2",
model_class="...",
input_dim=(28, 28, 1),
version="2.0.0", # Semantic versioning
description="Second iteration with larger latent dimension",
tags=["vae", "v2", "production"],
metadata={
"previous_version": "1.0.0",
"changes": "Increased latent dimension from 32 to 64",
"author": "research_team",
},
)
Environment-Specific Configs¤
Use different configs for different environments:
# Development config (fast iteration)
dev_training = TrainingConfiguration(
name="dev_training",
batch_size=32,
num_epochs=5,
optimizer=OptimizerConfiguration(
name="dev_adam",
optimizer_type="adam",
learning_rate=1e-3,
),
save_frequency=100, # Save frequently
log_frequency=10, # Log often
)
# Production config (full training)
prod_training = TrainingConfiguration(
name="prod_training",
batch_size=128,
num_epochs=100,
optimizer=OptimizerConfiguration(
name="prod_adam",
optimizer_type="adam",
learning_rate=3e-4,
),
save_frequency=5000, # Save less often
log_frequency=100, # Log less often
)
Configuration Inheritance¤
Create base configs and override:
# Base VAE configuration
base_vae = ModelConfiguration(
name="base_vae",
model_class="workshop.generative_models.models.vae.base.VAE",
activation="gelu",
dropout_rate=0.1,
use_batch_norm=True,
)
# Small VAE (for quick experiments)
small_vae = base_vae.merge({
"name": "small_vae",
"input_dim": (28, 28, 1),
"hidden_dims": [128, 64],
"output_dim": 16,
})
# Large VAE (for final training)
large_vae = base_vae.merge({
"name": "large_vae",
"input_dim": (28, 28, 1),
"hidden_dims": [512, 256, 128],
"output_dim": 128,
})
Common Patterns¤
Hyperparameter Sweeps¤
Configure hyperparameter searches:
def create_config_sweep(base_config, param_ranges):
"""Create configurations for hyperparameter sweep."""
configs = []
import itertools
# Get all parameter combinations
keys = list(param_ranges.keys())
values = list(param_ranges.values())
for combination in itertools.product(*values):
params = dict(zip(keys, combination))
# Create config name
name_parts = [f"{k}_{v}" for k, v in params.items()]
config_name = f"{base_config.name}_{'_'.join(name_parts)}"
# Merge with base config
new_config = base_config.merge({
"name": config_name,
**params,
})
configs.append(new_config)
return configs
# Define base configuration
base_config = TrainingConfiguration(
name="base",
batch_size=128,
num_epochs=50,
optimizer=OptimizerConfiguration(
name="adam",
optimizer_type="adam",
learning_rate=1e-3,
),
)
# Define parameter ranges
param_ranges = {
"batch_size": [32, 64, 128],
"optimizer.learning_rate": [1e-4, 1e-3, 1e-2],
}
# Generate sweep configurations
sweep_configs = create_config_sweep(base_config, param_ranges)
# Train with each configuration
for config in sweep_configs:
print(f"Training with config: {config.name}")
trainer = Trainer(model=model, training_config=config)
trainer.train_epoch()
A/B Testing¤
Compare different configurations:
# Configuration A (current)
config_a = TrainingConfiguration(
name="config_a_current",
batch_size=128,
num_epochs=100,
optimizer=OptimizerConfiguration(
name="adam",
optimizer_type="adam",
learning_rate=1e-3,
),
)
# Configuration B (experimental)
config_b = TrainingConfiguration(
name="config_b_experimental",
batch_size=256,
num_epochs=100,
optimizer=OptimizerConfiguration(
name="adamw",
optimizer_type="adamw",
learning_rate=3e-4,
weight_decay=0.01,
),
scheduler=SchedulerConfiguration(
name="cosine",
scheduler_type="cosine",
warmup_steps=1000,
),
)
# Train and compare
results = {}
for config in [config_a, config_b]:
trainer = Trainer(model=model, training_config=config)
metrics = trainer.train_epoch()
results[config.name] = metrics
# Compare results
print(f"Config A Loss: {results['config_a_current']['loss']:.4f}")
print(f"Config B Loss: {results['config_b_experimental']['loss']:.4f}")
Summary¤
The Workshop configuration system provides:
- ✅ Type Safety: Pydantic validation catches errors early
- ✅ Serialization: Easy YAML/JSON save/load
- ✅ Composability: Hierarchical configs with inheritance
- ✅ Validation: Built-in and custom validators
- ✅ Self-Documentation: Field descriptions and constraints
- ✅ Flexibility: Easy to extend with custom configurations
Next Steps¤
-
Practical training examples and advanced patterns
-
Architecture and core concepts of training system
-
Complete API reference for Trainer class
Continue to the Trainer API Reference for complete API documentation.