hydra

Par mkurman · zorai

Framework de configuration pour les applications complexes (Hydra). Configuration hiérarchique dynamique par composition et surcharge via CLI, YAML et configs structurées. À utiliser pour la gestion d'expériences ML, le déploiement multi-environnement, les sweeps d'hyperparamètres et les workflows de recherche reproductibles. S'intègre avec PyTorch Lightning, Weights & Biases, MLflow et Optuna.

npx skills add https://github.com/mkurman/zorai --skill hydra

Hydra

Aperçu

Hydra est un framework de configuration qui crée dynamiquement des configurations hiérarchiques par composition et substitution. Il élimine les chemins en dur et les fichiers de configuration éparpillés dans les projets. Utilisez cette skill pour gérer des configurations complexes d'expériences ML, des déploiements multi-environnements, des sweeps d'hyperparamètres et des workflows de recherche reproductibles.

Quand utiliser cette skill

Cette skill doit être utilisée lorsque :

  • Vous gérez des configurations complexes d'expériences ML sur plusieurs modèles, datasets et matériels
  • Vous lancez des sweeps d'hyperparamètres avec des substitutions de config structurées
  • Vous basculez entre environnements dev/staging/prod sans modifications de code
  • Vous configurez une recherche reproductible avec des configs versionnées
  • Vous intégrez la configuration dans PyTorch Lightning, W&B et autres outils ML
  • Vous lancez des expériences multi-run avec différentes combinaisons de paramètres
  • Vous organisez de larges codebases ML avec une séparation propre entre config et code

Capacités principales

1. Installation

pip install hydra-core --upgrade

2. Pattern de configuration basique

Structure de répertoire :

conf/
  config.yaml
  db/
    mysql.yaml
    postgresql.yaml
my_app.py

conf/config.yaml :

defaults:
  - db: mysql
  - _self_

db:
  driver: mysql
  host: localhost
  port: 3306
  user: root

my_app.py :

import hydra
from omegaconf import DictConfig, OmegaConf

@hydra.main(version_base=None, config_path="conf", config_name="config")
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))
    print(f"Connecting to {cfg.db.host}:{cfg.db.port}")

if __name__ == "__main__":
    my_app()

Substitutions CLI :

python my_app.py                           # Utilise mysql
python my_app.py db=postgresql             # Bascule vers postgresql
python my_app.py db.host=prod-server       # Substitue une valeur spécifique
python my_app.py db=postgresql db.port=5432  # Substitutions multiples

3. Configs structurées (dataclasses Python)

from dataclasses import dataclass, field
from typing import List, Optional
import hydra
from hydra.core.config_store import ConfigStore

@dataclass
class ModelConfig:
    name: str = "resnet50"
    pretrained: bool = True
    num_classes: int = 1000

@dataclass
class TrainingConfig:
    learning_rate: float = 0.001
    batch_size: int = 32
    max_epochs: int = 100
    optimizer: str = "adam"

@dataclass
class DataConfig:
    dataset_path: str = "./data"
    num_workers: int = 4
    image_size: int = 224
    augmentations: List[str] = field(default_factory=lambda: ["flip", "rotate"])

@dataclass
class ExperimentConfig:
    model: ModelConfig = ModelConfig()
    training: TrainingConfig = TrainingConfig()
    data: DataConfig = DataConfig()
    seed: int = 42
    experiment_name: str = "baseline"
    tags: List[str] = field(default_factory=list)

# Enregistre la config
cs = ConfigStore.instance()
cs.store(name="base_config", node=ExperimentConfig)

@hydra.main(version_base=None, config_path=None, config_name="base_config")
def run_experiment(cfg: ExperimentConfig) -> None:
    print(f"Model: {cfg.model.name}")
    print(f"LR: {cfg.training.learning_rate}")
    print(f"Batch size: {cfg.training.batch_size}")

if __name__ == "__main__":
    run_experiment()

Substitutions CLI avec configs structurées :

python experiment.py \
    model=resnet101 \
    training.learning_rate=0.0001 \
    training.batch_size=64 \
    data.image_size=256 \
    experiment_name=experiment_1

4. Groupes de config (Configs modulaires)

Répertoire :

conf/
  config.yaml
  model/
    resnet50.yaml
    vit_base.yaml
    efficientnet.yaml
  optimizer/
    adam.yaml
    adamw.yaml
    sgd.yaml
  dataset/
    imagenet.yaml
    cifar10.yaml

conf/config.yaml :

defaults:
  - model: resnet50
  - optimizer: adamw
  - dataset: imagenet
  - _self_

training:
  epochs: 100
  mixed_precision: true

conf/model/vit_base.yaml :

name: vit_base_patch16_224
pretrained: true
num_classes: 1000
patch_size: 16
hidden_dim: 768
num_heads: 12
num_layers: 12

Utilisation :

python train.py model=vit_base                              # Bascule de modèle
python train.py model=efficientnet optimizer=sgd             # Bascule de plusieurs
python train.py model.vit_base.patch_size=32                 # Substitution imbriquée

5. Multi-run (Sweeps d'hyperparamètres)

# Sweep en grille : essayer toutes les combinaisons
python train.py --multirun \
    training.learning_rate=0.001,0.0001,0.00001 \
    training.batch_size=32,64,128

# Combinaisons spécifiques
python train.py --multirun \
    model=resnet50,vit_base \
    optimizer=adamw,sgd

# Sweep de plage
python train.py --multirun \
    seed=1,2,3,4,5

# Depuis une config de sweep
python train.py --multirun --config-name=sweep_config

6. Gestion des sorties

Hydra crée automatiquement des répertoires de sortie horodatés :

outputs/
  2024-01-15/
    10-30-45/
      .hydra/          # Métadonnées de config Hydra
      train.log        # Logs d'application
      checkpoints/     # Vos artefacts

Accéder au répertoire de sortie dans le code :

import hydra
from hydra.utils import get_original_cwd, to_absolute_path

@hydra.main(...)
def my_app(cfg):
    # Hydra change le répertoire de travail vers le répertoire de sortie
    print(os.getcwd())                    # .../outputs/2024-01-15/10-30-45/
    print(get_original_cwd())             # Répertoire de travail original

7. Intégration PyTorch Lightning

Config :

defaults:
  - model: resnet50
  - trainer: default
  - data: imagenet
  - _self_

seed: 42

Script d'entraînement :

@hydra.main(version_base=None, config_path="conf", config_name="config")
def train(cfg: DictConfig):
    pl.seed_everything(cfg.seed)
    model = MyLightningModule(cfg.model)
    datamodule = MyDataModule(cfg.data)
    trainer = pl.Trainer(**cfg.trainer)
    trainer.fit(model, datamodule)

8. Intégration logging W&B / MLflow

@hydra.main(...)
def train(cfg: DictConfig):
    # W&B
    import wandb
    wandb.init(project=cfg.wandb.project, config=OmegaConf.to_container(cfg))

    # MLflow
    import mlflow
    mlflow.log_params(OmegaConf.to_container(cfg))

9. Instantiation (hydra.utils.instantiate)

# Config
# model:
#   _target_: torch.optim.AdamW
#   lr: 0.001
#   weight_decay: 0.01

from hydra.utils import instantiate

@hydra.main(...)
def train(cfg):
    optimizer = instantiate(cfg.optimizer)  # Crée AdamW(lr=0.001, weight_decay=0.01)
    model = instantiate(cfg.model)
    scheduler = instantiate(cfg.scheduler, optimizer=optimizer)

Instantiation récursive :

model:
  _target_: mylib.models.ResNetClassifier
  backbone:
    _target_: torchvision.models.resnet50
    pretrained: true
  num_classes: 1000

10. Resolvers (Résolution dynamique de valeurs)

# Enregistre un resolver personnalisé
from omegaconf import OmegaConf

OmegaConf.register_new_resolver("sum", lambda x, y: x + y)
OmegaConf.register_new_resolver("eval", eval)

# Utilise dans YAML
# total_steps: ${sum:${train.epochs},${train.warmup_epochs}}
# batch_size_gb: ${eval:'int(${batch_size} * ${image_size}**2 * 3 * 4 / 1e9)'}

Resolvers intégrés :

output_dir: ${hydra:runtime.output_dir}
now: ${now:%Y-%m-%d_%H-%M-%S}
# Chemin relatif au fichier de config
data_path: ${oc.env:DATA_PATH,/default/path}

Patterns clés

  1. Séparez la config du code — tous les paramètres ajustables vont dans YAML/dataclasses
  2. Utilisez des groupes de config pour les familles de modèles/datasets/optimiseurs — permutation modulaire
  3. Les substitutions CLI sont la source de vérité — YAML fournit les défauts, CLI finalise
  4. Utilisez instantiate() pour la création d'objets à partir de config — réduit le boilerplate
  5. Les répertoires de sortie horodatés sont automatiques — pas besoin de les gérer manuellement
  6. Multi-run pour les sweeps--multirun plus valeurs séparées par des virgules
  7. Versionnez les fichiers de config — ils SONT votre documentation d'expérience

Références

Skills similaires