Hydra: Manajemen Konfigurasi yang Rapi untuk Aplikasi ML dan Python
Kebanyakan proyek machine learning dimulai dari satu skrip training dan beberapa hyperparameter yang dilewatkan lewat argparse. Beberapa bulan kemudian skrip yang sama sudah memiliki tiga puluh flag, path yang di-hardcode tersebar di banyak fungsi, dan tidak ada yang bisa mereproduksi run yang menghasilkan model terbaik kuartal lalu. Hydra, framework open-source dari Meta yang dibangun di atas OmegaConf, dirancang persis untuk menyelesaikan kelas masalah ini. Tutorial ini membahas Hydra dari dasar menggunakan alur kerja training model yang realistis sebagai contoh utama.
Apa Itu Hydra dan Masalah yang Diselesaikannya
Hydra adalah framework Python untuk menyusun (compose) dan menimpa (override) konfigurasi berhierarki. Alih-alih satu file konfigurasi raksasa atau deretan flag command-line, Anda mendeskripsikan konfigurasi sebagai pohon dari file-file YAML kecil yang disusun Hydra saat runtime. Anda kemudian bisa menimpa nilai apa pun dari command line, menukar seluruh grup konfigurasi (misalnya beralih dari ResNet ke Vision Transformer), dan menjalankan sweep di berbagai kombinasi parameter.
Masalah konkret yang ditangani dalam pekerjaan ML:
- Definisi
argparseyang tersebar. Flag menumpuk di banyak file dan sulit ditemukan atau didokumentasikan. - Hyperparameter yang di-hardcode. Learning rate dan path yang terkubur dalam kode sumber membuat eksperimen rapuh.
- Run yang sulit direproduksi. Tanpa snapshot konfigurasi yang persis, mereproduksi hasil beberapa bulan kemudian hanya menjadi tebakan.
- Konfigurasi eksperimen yang kombinatorial. Mencoba tiga model terhadap empat dataset dengan dua optimizer berarti dua puluh empat run; mengelolanya secara manual rawan kesalahan.
Hydra berdiri di atas OmegaConf, yang menyediakan objek konfigurasi bertipe di baliknya (sebuah DictConfig), interpolasi variabel, dan logika penggabungan. Hydra menambahkan komposisi, override command-line, direktori output otomatis, dan multirun.
Instalasi
Hydra dipublikasikan sebagai hydra-core di PyPI. Pasang ke environment proyek Anda:
pip install hydra-core
Plugin opsional untuk sweeper dan launcher adalah paket terpisah, dipasang saat dibutuhkan:
pip install hydra-optuna-sweeper
pip install hydra-joblib-launcher
pip install hydra-submitit-launcher
Verifikasi instalasi:
python -c "import hydra; print(hydra.version)"
Tutorial ini menargetkan Hydra 1.3.x, yaitu jalur stabil saat ini.
Dasar: Entry Point, File Konfigurasi, dan cfg
Aplikasi Hydra memiliki tiga bagian: direktori konfigurasi, file YAML konfigurasi, dan fungsi entry-point yang didekorasi. Buat struktur proyek seperti ini:
project/
├── train.py
└── conf/
└── config.yaml
Mulai dengan conf/config.yaml minimal:
epochs: 10
batchsize: 32
optimizer:
name: adam
lr: 0.001
Sekarang entry point di train.py:
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(versionbase=None, configpath="conf", configname="config")
def main(cfg: DictConfig) -> None:
print(OmegaConf.toyaml(cfg))
print(f"Training selama {cfg.epochs} epoch")
print(f"Optimizer: {cfg.optimizer.name} @ lr={cfg.optimizer.lr}")
if name == "main":
main()
Beberapa catatan tentang dekorator. versionbase=None memilih perilaku modern dan menghilangkan peringatan kompatibilitas; setel secara eksplisit agar skrip Anda berperilaku konsisten di berbagai versi Hydra. configpath relatif terhadap file yang mendefinisikan main, dan configname adalah nama file YAML tanpa ekstensinya.
Jalankan:
python train.py
Objek cfg adalah DictConfig dari OmegaConf. Anda mengakses nilai dengan notasi titik (cfg.optimizer.lr) atau sintaks kamus (cfg["optimizer"]["lr"]). Objek ini berperilaku seperti namespace bersarang, dan OmegaConf mempertahankan tipe yang dideklarasikan di YAML, sehingga cfg.epochs bertipe int dan cfg.optimizer.lr bertipe float.
Override dari Command Line
Fitur Hydra yang paling berguna sehari-hari adalah menimpa konfigurasi tanpa menyentuh file. Nilai leaf mana pun dapat diatur dari command line menggunakan path bertitik:
python train.py optimizer.lr=0.01 epochs=50
Ini menghasilkan konfigurasi dengan optimizer.lr bernilai 0.01 dan epochs bernilai 50, sementara sisanya tidak tersentuh. Hydra mem-parse sisi kanan menjadi nilai bertipe, sehingga epochs=50 menjadi integer.
Secara default Hydra hanya mengizinkan Anda menimpa key yang sudah ada; ini menangkap salah ketik. Untuk menambahkan key yang belum ada di konfigurasi, beri awalan +:
python train.py +optimizer.weightdecay=0.0001
Untuk menghapus key, beri awalan ~:
python train.py ~optimizer.lr
Anda juga bisa memaksa override sekaligus menambah dengan ++ (mengatur baik key itu ada maupun tidak). Awalan-awalan ini membuat CLI menjadi alat lengkap untuk membentuk pohon konfigurasi, dan itulah yang membuat eksperimen ad-hoc menjadi cepat.
Grup Konfigurasi dan Komposisi
Proyek nyata membutuhkan lebih dari satu file datar. Model komposisi Hydra memungkinkan Anda mengorganisasi opsi terkait ke dalam grup konfigurasi — subdirektori di bawah conf/ — dan memilih satu opsi dari tiap grup. Inilah cara membuat model, dataset, dan optimizer dapat ditukar secara independen.
Restrukturisasi proyek:
conf/
├── config.yaml
├── model/
│ ├── resnet.yaml
│ └── vit.yaml
├── dataset/
│ ├── cifar.yaml
│ └── imagenet.yaml
└── optimizer/
├── adam.yaml
└── sgd.yaml
Setiap file grup mendeskripsikan satu opsi. Misalnya conf/model/resnet.yaml:
name: resnet50
numlayers: 50
pretrained: true
Dan conf/model/vit.yaml:
name: vitbase
patch
size: 16
pretrained: true
Sebuah dataset, conf/dataset/cifar.yaml:
name: cifar10
numclasses: 10
imagesize: 32
datadir: /data/cifar10
conf/config.yaml di tingkat atas mendeklarasikan opsi mana dari tiap grup yang menjadi default melalui daftar defaults:
defaults:
- model: resnet
- dataset: cifar
- optimizer: adam
- self
epochs: 10
batchsize: 32
Daftar defaults adalah instruksi komposisi Hydra. Setiap entri berarti "dari grup model, ambil resnet". Entri khusus self mengatur di mana nilai milik file ini sendiri digabungkan relatif terhadap grup; menempatkannya terakhir berarti key tingkat atas menimpa apa pun yang ditarik dari grup.
Saat runtime Hydra menyusun satu konfigurasi dari potongan-potongan ini. Model yang dipilih berada di bawah cfg.model, dataset di bawah cfg.dataset, dan seterusnya. Anda menimpa pilihan dari command line dengan menyebut nama grup:
python train.py model=vit dataset=imagenet optimizer=sgd
Inilah inti Hydra: setiap sumbu eksperimen Anda adalah file independen yang dinamai dan dikelola version control, dan Anda menyusunnya sesuka hati saat peluncuran.
Interpolasi Variabel dengan OmegaConf
OmegaConf mendukung interpolasi, memungkinkan satu nilai konfigurasi merujuk ke nilai lain sehingga Anda tidak mengulang diri. Gunakan sintaks ${path.to.value}:
paths:
root: /workspace
data: ${paths.root}/data
checkpoints: ${paths.root}/checkpoints
runname: ${model.name}${dataset.name}
Di sini paths.data ter-resolve menjadi /workspace/data, dan runname dibentuk dari nama model dan dataset yang dipilih — sehingga otomatis mencerminkan apa pun yang Anda susun.
OmegaConf juga menyediakan resolver, fungsi khusus yang dipanggil saat interpolasi. Baca variabel environment dengan oc.env:
apikey: ${oc.env:WANDBAPIKEY}
datadir: ${oc.env:DATADIR,/data}
Bentuk kedua menyediakan default (/data) ketika variabel tidak diset. Hydra mendaftarkan resolver now untuk timestamp, berguna untuk menamai run:
runid: ${now:%Y-%m-%d%H-%M-%S}
Anda bisa mendaftarkan resolver kustom dalam kode saat membutuhkan nilai terkomputasi:
from omegaconf import OmegaConf
OmegaConf.registernewresolver("mul", lambda a, b: a * b)
Lalu effectivelr: ${mul:${optimizer.lr},${numgpus}} menghitung nilai saat diakses.
Direktori Kerja Otomatis dan Reproduktibilitas
Setiap kali Anda menjalankan aplikasi Hydra, ia membuat direktori output baru dan berpindah ke dalamnya. Secara default tata letaknya adalah outputs/YYYY-MM-DD/HH-MM-SS/ untuk run tunggal. Di dalam direktori itu Hydra menulis folder .hydra/ yang berisi tiga file:
config.yaml— konfigurasi yang sudah tersusun penuh untuk run ini.overrides.yaml— override command-line persis yang Anda lewatkan.hydra.yaml— konfigurasi Hydra sendiri yang sudah ter-resolve.
Snapshot inilah yang membuat run dapat direproduksi. Beberapa bulan kemudian Anda bisa membuka .hydra/config.yaml dan melihat konfigurasi persis yang menghasilkan suatu hasil, atau menjalankan ulang dengan override yang tercatat. Log dan file apa pun yang ditulis skrip Anda ke direktori saat ini juga mendarat di sini, menjaga setiap run tetap mandiri.
Karena Hydra mengubah direktori kerja, simpan artefak relatif terhadap direktori saat ini dan semuanya akan terorganisasi per run secara otomatis. Jika Anda membutuhkan direktori peluncuran asli, gunakan API runtime:
import os
from hydra.core.hydraconfig import HydraConfig
@hydra.main(versionbase=None, configpath="conf", configname="config")
def main(cfg):
outdir = HydraConfig.get().runtime.outputdir
originalcwd = hydra.utils.getoriginalcwd()
print(f"Menulis artefak ke {os.getcwd()}")
Anda bisa menyesuaikan pola direktori lewat pengaturan hydra.run.dir, baik dalam konfigurasi maupun di CLI:
hydra:
run:
dir: ./outputs/${runname}/${now:%Y-%m-%d%H-%M-%S}
job:
name: train
chdir: true
Flag hydra.job.chdir mengontrol apakah Hydra berpindah ke direktori output; setel ke true untuk mempertahankan isolasi per run seperti dijelaskan di atas.
Multirun dan Sweep
Untuk menjelajahi ruang konfigurasi dalam satu perintah, gunakan multirun dengan flag -m (atau --multirun). Lewatkan nilai yang dipisah koma dan Hydra menjalankan hasil kali silangnya:
python train.py -m model=resnet,vit lr=0.1,0.01
Itu meluncurkan empat job — setiap kombinasi dua model dan dua learning rate — masing-masing di subdirektori sendiri di bawah multirun/. Ini langsung mengatasi masalah konfigurasi kombinatorial: Anda mendeskripsikan sumbunya sekali dan Hydra mengenumerasinya.
Sweeper default melakukan grid dasar. Untuk pencarian yang lebih pintar, ganti dengan plugin sweeper. Sweeper Optuna melakukan optimasi hyperparameter menggunakan sampler Optuna. Setelah memasang hydra-optuna-sweeper, konfigurasikan:
defaults:
- override hydra/sweeper: optuna
hydra:
sweeper:
sampler:
target: optuna.samplers.TPESampler
direction: maximize
ntrials: 30
params:
optimizer.lr: interval(1e-5, 1e-1)
model: choice(resnet, vit)
batchsize: choice(16, 32, 64)
Fungsi main Anda harus mengembalikan nilai objektif yang dioptimasi:
@hydra.main(versionbase=None, configpath="conf", configname="config")
def main(cfg: DictConfig) -> float:
valaccuracy = trainmodel(cfg)
return valaccuracy
Jalankan sweep dengan:
python train.py -m
Hydra juga menyediakan sweeper Ax (hydra-ax-sweeper) untuk optimasi Bayesian dengan gaya konfigurasi serupa. Untuk eksekusi, launcher menentukan bagaimana job dijalankan. Default menjalankannya berurutan dalam proses; launcher joblib memparalelkan di antara core lokal, dan launcher submitit mengirim tiap job ke cluster SLURM. Pilih salah satu dengan override, misalnya:
python train.py -m hydra/launcher=joblib model=resnet,vit lr=0.1,0.01,0.001
Sweeper dan launcher disusun secara independen, jadi Anda bisa menjalankan pencarian Optuna sekaligus mengirim tiap trial ke SLURM pada saat bersamaan.
Structured Config untuk Keamanan Tipe
YAML sendiri tidak bertipe — key yang salah eja atau string di tempat float baru muncul saat runtime. Structured config mendefinisikan skema Anda dengan dataclass Python dan mendaftarkannya di ConfigStore, memberi Anda validasi dan autocompletion di editor.
from dataclasses import dataclass, field
from hydra.core.configstore import ConfigStore
@dataclass
class OptimizerConfig:
name: str = "adam"
lr: float = 0.001
weightdecay: float = 0.0
@dataclass
class ModelConfig:
name: str = "resnet50"
numlayers: int = 50
pretrained: bool = True
@dataclass
class TrainConfig:
epochs: int = 10
batchsize: int = 32
optimizer: OptimizerConfig = field(defaultfactory=OptimizerConfig)
model: ModelConfig = field(defaultfactory=ModelConfig)
cs = ConfigStore.instance()
cs.store(name="config", node=TrainConfig)
Dengan skema terdaftar, Hydra memvalidasi konfigurasi tersusun terhadapnya. Melewatkan epochs=abc kini memunculkan error saat startup, bukan gagal jauh di dalam loop training, dan menetapkan nilai ke field yang tidak dideklarasikan ditolak. Anda bisa mencampur structured config dengan file YAML: daftarkan dataclass sebagai skema dan biarkan YAML menyediakan nilainya, mendapat fleksibilitas sekaligus pemeriksaan tipe.
Menginstansiasi Objek dari Konfigurasi
Ini salah satu pola Hydra yang paling kuat dan layak diadopsi sejak awal. Dengan hydra.utils.instantiate, sebuah node konfigurasi dapat mendeskripsikan objek Python yang akan dibangun — path kelas lengkapnya berada di key target, dan key sisanya menjadi argumen konstruktor. YAML Anda berhenti hanya menjadi data dan berubah menjadi resep untuk objek.
Definisikan entri grup optimizer, conf/optimizer/adam.yaml:
target: torch.optim.Adam
lr: 0.001
weight
decay: 0.0
betas: [0.9, 0.999]
Dan sebuah model, conf/model/resnet.yaml:
target: torchvision.models.resnet50
weights: IMAGENET1KV2
Dalam kode, instansiasi langsung dari konfigurasi:
import hydra
from hydra.utils import instantiate
@hydra.main(versionbase=None, configpath="conf", configname="config")
def main(cfg):
model = instantiate(cfg.model)
optimizer = instantiate(cfg.optimizer, params=model.parameters())
train(model, optimizer, cfg)
Perhatikan bahwa Anda bisa melewatkan argumen keyword tambahan ke instantiate saat dipanggil (di sini params), inilah cara objek runtime seperti parameter model disuntikkan ke konstruktor yang dideskripsikan konfigurasi. Untuk menukar optimizer, kini Anda mengubah satu file YAML atau satu override command-line — tanpa rangkaian if/else di kode training. Ini mendorong pemilihan model, dataset, dan optimizer sepenuhnya ke konfigurasi, dan itulah yang membuat seluruh eksperimen dapat di-sweep.
Secara default instantiate bersifat rekursif: node target bersarang dibangun lebih dulu. Gunakan partial: true pada node konfigurasi ketika Anda ingin instantiate mengembalikan functools.partial alih-alih langsung memanggil target — berguna untuk scheduler atau loss yang Anda bangun belakangan.
Integrasi dengan Loop Training dan Logging
Hydra mengonfigurasi modul logging standar Python untuk Anda. Cukup ambil sebuah logger dan gunakan; output mengalir ke konsol sekaligus ke file log di direktori run:
import logging
import hydra
from hydra.utils import instantiate
from omegaconf import DictConfig
log = logging.getLogger(name)
@hydra.main(versionbase=None, configpath="conf", configname="config")
def main(cfg: DictConfig) -> float:
log.info("Memulai run: %s", cfg.get("runname", "unnamed"))
model = instantiate(cfg.model)
optimizer = instantiate(cfg.optimizer, params=model.parameters())
dataset = instantiate(cfg.dataset)
bestacc = 0.0
for epoch in range(cfg.epochs):
trainloss = runepoch(model, optimizer, dataset)
valacc = evaluate(model, dataset)
log.info("epoch=%d loss=%.4f valacc=%.4f", epoch, trainloss, valacc)
bestacc = max(bestacc, valacc)
log.info("Akurasi validasi terbaik: %.4f", bestacc)
return bestacc
if name == "main":
main()
Mengembalikan bestacc membuat fungsi yang sama bisa berperan ganda sebagai objektif sweep. Anda bisa menyesuaikan level dan format log melalui grup konfigurasi hydra/joblogging jika default tidak sesuai kebutuhan. Karena direktori kerja bersifat per run, file log berada bersebelahan dengan snapshot .hydra/, sehingga setiap eksperimen membawa konfigurasi dan lognya bersama-sama.
Menggunakan Hydra di Aplikasi, Bukan Hanya Skrip
Hydra tidak terbatas pada skrip training tingkat atas. Untuk aplikasi yang dipaketkan, arahkan configpath ke sebuah package menggunakan notasi pkg:// agar konfigurasi ikut dalam wheel Anda, dan ekspos console entry point di pyproject.toml. Ketika Anda perlu menyusun konfigurasi secara programatik — misalnya di dalam web service atau notebook di mana dekorator @hydra.main merepotkan — gunakan Compose API:
from hydra import initialize, compose
with initialize(versionbase=None, configpath="conf"):
cfg = compose(configname="config", overrides=["model=vit", "epochs=5"])
runinference(cfg)
initialize menyiapkan search path dan compose mengembalikan DictConfig tersusun tanpa mengubah direktori atau mengambil alih proses. Inilah alat yang tepat untuk menanamkan konfigurasi yang dikelola Hydra ke dalam sistem yang lebih besar seperti layanan FastAPI atau job batch.
Praktik Terbaik
- Setel
versionbase=Nonesecara eksplisit agar perilaku stabil di berbagai rilis Hydra. - Jaga file grup tetap kecil dan terfokus — satu model, satu dataset, satu optimizer per file — agar komposisi tetap mudah dibaca.
- Adopsi instansiasi
targetsejak awal. Ini menghilangkan percabangan dari kode Anda dan membuat setiap komponen dapat ditukar dan di-sweep. - Gunakan structured config untuk apa pun yang berumur panjang. Validasi skema menangkap kesalahan saat startup, sebelum training mahal dimulai.
- Commit direktori
conf/Anda ke version control, dan arsipkan snapshot.hydra/per run bersama hasil eksperimen. - Utamakan interpolasi daripada duplikasi. Turunkan path dan nama run dari nilai dasar agar satu perubahan menyebar.
- Dokumentasikan grup konfigurasi Anda dalam README singkat agar kolaborator tahu opsi apa yang tersedia.
- Validasi rentang sweep pada subset kecil dulu untuk menghindari meluncurkan ratusan job dengan konfigurasi yang salah.
Kesimpulan dan Poin Penting
Hydra mengubah konfigurasi dari hal yang dipikir belakangan menjadi bagian kelas satu proyek Anda yang dapat disusun. Dengan mendeskripsikan setiap sumbu eksperimen sebagai file YAML kecil, menimpa bebas dari command line, dan men-snapshot setiap run, ia menghilangkan sebagian besar gesekan seputar reproduktibilitas dan eksperimen kombinatorial yang membebani basis kode ML.
Poin penting:
- Hydra menyusun konfigurasi berhierarki dari file-file kecil dan membiarkan Anda menimpa nilai apa pun di CLI.
- Grup konfigurasi membuat model, dataset, dan optimizer dapat ditukar secara independen;
defaultsmemilihnya. - Multirun ditambah plugin sweeper (Optuna, Ax) dan launcher (joblib, submitit) mencakup semuanya dari grid search hingga sweep cluster.
- Direktori per run otomatis dan snapshot
.hydra/memberi Anda reproduktibilitas secara gratis. instantiatedengantargetadalah pola yang membuat skrip training benar-benar digerakkan konfigurasi.- Structured config menambah keamanan tipe, dan Compose API memungkinkan Anda memakai Hydra di dalam aplikasi dan layanan.
Mulailah dengan memindahkan satu skrip argparse ke satu config.yaml, lalu secara bertahap tarik model, dataset, dan optimizer ke dalam grup konfigurasi. Hasilnya adalah pipeline training di mana setiap eksperimen adalah perintah satu baris dan setiap hasil dapat direproduksi.