医療従事者出身、未経験でデータ職を目指して転職活動中のshimodasと申します。
初学者なりにMLOpsについて学び、試行錯誤の末に出来上がったものたちを発信中です。
シリーズ「ノートブックを卒業した日 ── 指南書で学んだMLOps技術をKaggleで全部試してみた」
- uv(パッケージ管理)
- Pandera(データバリデーション)
- Hydra(設定管理)← 今ここ
- MLflow(実験管理)
- Streamlit(アプリ化・デプロイ)
- Dev Container(開発環境コンテナ)
- Cookiecutter × リポジトリ設計
はじめに
機械学習に出会ったばかりの自分は、Hydraを使う前こんな状態でした。
Titanicのデータでなんとか機械学習について一通りのプロセスを経験した。learning_rate = 0.05 と書いてある行を探してコメントアウトし、learning_rate = 0.01 に書き換えてスクリプトを実行する。その繰り返し。「さっきどっちが良かったっけ」と思い返しながら、スクリプトを見ても答えは書いていない。Notionに貼り付けたスクショとメモを検索で探す....Kaggleのノートブックで実験しているうちはそれで何とかなっていました。
『先輩データサイエンティストからの指南書』(浅野純季ら著、技術評論社、2025年)に、その解決策として Hydra が紹介されていました。それまでの自分には、「実験管理」というような概念が存在していなかったような気がします。Hydraを使って「パラメータをYAMLファイルに切り出して管理する」という話でした。適当な範囲でGridsearchやRandomsearch、あるいはちょっと頑張ってOptunaを回していた程度のパラメータ管理をしていた自分にとって、目から鱗のような話でした
この記事では、前回までに引き続きまして、Playground Series S6E5(F1ピットストップ予測)のLightGBMにHydraを適用した話を書きます。シリーズ第3回は Hydra です。
対象読者: Pythonでモデルを書いていて、「実験ごとに設定を手動で変えるのが面倒」「あの実験の設定どこいったか分からなくなる」という方。
そもそもHydraとは
Hydra(ハイドラ)は、設定をYAMLで管理するためのPythonライブラリです。Facebook Research(Meta)が開発したOSSで、機械学習の実験管理によく使われているそうです。
一言でまとめると、こういうことができます。
# デフォルトで実行
python scripts/train.py
# パラメータを上書きして実行(スクリプトは変えない)
python scripts/train.py model.learning_rate=0.01
# 複数の組み合わせを一括実行(マルチラン)
python scripts/train.py -m model.learning_rate=0.01,0.05 model.num_leaves=20,40
「なぜこれが嬉しいか」は後で詳しく書きますが、要するに設定をYAMLに書いておけば、スクリプトを書き換えることなく実験条件を変えられるという話です。
インストール
pip install hydra-core
パッケージ名は hydra-core です(hydra ではないので注意)。
Hydraを使う前の世界
Hydraを使う前の自分のコードはこんな感じでした。
# train.py
params = {
"learning_rate": 0.05,
"num_leaves": 31,
"n_estimators": 300,
}
N_SPLITS = 4
RANDOM_STATE = 42
model = lgb.LGBMClassifier(**params)
何が問題なのか、という話なんですが。
まず、パラメータがスクリプトの中に散在しています。今はまとめてあるように見えても、スクリプトが複数になると train.py、predict.py、evaluate.py それぞれに定数が増えていきます。(そもそも .ipynb だけだった自分は .pyを複数使うなんてイメージしていませんでしたが)
次に、「あの実験のとき何で動かしたっけ」が追えません。learning_rate=0.01 で実験した結果と learning_rate=0.05 の結果を比べたいとき、コードのコメントアウト履歴を掘り返すことになります。(私は前述のとおりコメント・メモかスクショなどに残すなどの原始的な方法をとっていましたが...)
またgitのコミット履歴を見れば追えるかもしれませんが、実験のたびにcommitするのも現実的じゃないですよね。
つまり、「実験設定」と「コード」が分離していないことが根本の問題なんじゃないかと思います。
つまり、設定をコードに埋め込んだままだと、実験の再現性が保てないんですよね。Kaggleでも「あのパラメータどこだっけ」を何度か経験してから、この問題の深刻さに気づきました。
実装:YAMLで設定を切り出す
ディレクトリ構成
(プロジェクトルート)
├── conf/
│ └── config.yaml ← パラメータをここに集約
├── scripts/
│ └── train.py ← 学習スクリプト
└── ...
シンプルです。今回はプロジェクトルート直下の conf/ に config.yaml を置きました。
config.yaml (例)
train_ratio: 0.8
model:
learning_rate: 0.05
num_leaves: 31
n_estimators: 300
train:
random_state: 42
n_splits: 4
このように階層構造で書けます。model.learning_rate のように、ドット区切りで参照できます。
train.pyへの組み込み (例)
import hydra
from omegaconf import DictConfig, OmegaConf
@hydra.main(version_base=None, config_path="conf", config_name="config")
def main(config: DictConfig) -> None:
print(config.model.learning_rate) # 0.05
print(config.train.n_splits) # 4
model = lgb.LGBMClassifier(
**OmegaConf.to_container(config.model, resolve=True)
)
skf = StratifiedKFold(
n_splits=config.train.n_splits,
shuffle=True,
random_state=config.train.random_state,
)
# ... 以降は通常の学習コード ...
if __name__ == "__main__":
main()
@hydra.main というデコレータを1行付けるだけで、config.yaml の中身が DictConfig として main(config) に渡されます。
config_path="conf" は train.py から見た相対パスです。conf/ はプロジェクトルート直下、train.py は scripts/ の下にあるので、config_path="../conf" と指定しています。
CLIオーバーライド:スクリプトを触らずにパラメータを変える
Hydraを使うと、実行時にコマンドラインからパラメータを上書きできます。
# 通常実行(config.yamlのデフォルト値が使われる)
python scripts/train.py
# learning_rateだけ変える
python scripts/train.py model.learning_rate=0.01
# 複数まとめて変える
python scripts/train.py model.learning_rate=0.01 model.num_leaves=50
スクリプトのコードはまったく触らなくていいです。「今日は num_leaves を大きくして試してみよう」と思ったら、コマンドに足すだけです。model.learning_rate のように、YAMLの階層をドット区切りで指定するのがルールです。
マルチラン:全組み合わせを一括実行
個人的に一番「これは便利だ」と思ったのがマルチランです。
python scripts/train.py -m \
model.learning_rate=0.01,0.05 \
model.num_leaves=20,30,40
これだけで、learning_rate × num_leaves = 2 × 3 = 6 パターンの実験を一括実行してくれます。
グリッドサーチを自前で書かなくていいです。ループを書かなくていいです。「次の実験のパラメータに書き換えて実行」を繰り返す作業が不要になります。-m(--multirun)フラグを付けるだけで自動的に全組み合わせを回してくれます。
自分でグリッドサーチの実装を書いたことがある方には伝わると思いますが、あの「リスト作って、ループ回して、結果をどこかに保存して...」という実装が丸ごとなくなるイメージです。
F1ピットストップ予測で実際にマルチランを使ったとき、learning_rate=0.05 + num_leaves=31 がベースライン(AUC: 0.931)に対して、learning_rate=0.01 + num_leaves=50 で微改善するパターンが見えてきました。「どのパラメータが効いているか」が比較できる形で残るのは、一人でコンペを回していると特に助かります。
実行結果の自動保存
Hydraはデフォルトで実行結果を保存してくれます。
outputs/
└── 2026-05-10/
└── 14-30-00/
├── .hydra/
│ └── config.yaml ← そのときの設定が丸ごと保存される
└── train.log ← ログ
マルチランの場合は multirun/ 以下にパターンごとのサブディレクトリができます。
multirun/
└── 2026-05-10/
└── 14-30-00/
├── 0/ # learning_rate=0.01, num_leaves=20
├── 1/ # learning_rate=0.01, num_leaves=30
├── 2/ # learning_rate=0.01, num_leaves=40
├── 3/ # learning_rate=0.05, num_leaves=20
├── 4/ # learning_rate=0.05, num_leaves=30
└── 5/ # learning_rate=0.05, num_leaves=40
「あの実験どこ行った?」問題が解消されます。タイムスタンプが付いているので、いつ何の設定で動かしたかが後から追えます。
注意点:作業ディレクトリが変わる
Hydraを使うと、スクリプトの実行時の作業ディレクトリが outputs/ 以下に変わります。
# outputs/2026-05-10/14-30-00/ を起点にパスが解決されてしまう
df = pd.read_csv("data/raw/train.csv") # FileNotFoundError
解決策は hydra.utils.get_original_cwd() で元のディレクトリを取得することです。
import hydra
from hydra.utils import get_original_cwd
from pathlib import Path
@hydra.main(version_base=None, config_path="conf", config_name="config")
def main(config: DictConfig) -> None:
original_dir = Path(get_original_cwd())
train_path = original_dir / "data" / "raw" / "train.csv"
df = pd.read_csv(train_path) # OK
get_original_cwd() を使えば解決するのですが、ドキュメントを読まずに使い始めると確実に引っかかる罠だと思います。Hydraを入れてすぐ動かなくなったときは、まずここを疑ってみるといいかもしれません。
私は root = Path(hydra.utils.get_original_cwd()) とすることで、rootをとりconf/config.yamlにそれぞれの相対パスを記載することでパス解決を図りました。
Hydra × MLflow:実験管理の循環を作る
Hydraはパラメータ管理、MLflowは実験結果の記録と可視化。この2つを組み合わせると、実験管理の循環が完成します。(詳細は次回以降で記載しますが)
import hydra
import mlflow
from omegaconf import DictConfig, OmegaConf
@hydra.main(version_base=None, config_path="conf", config_name="config")
def main(config: DictConfig) -> None:
mlflow.set_tracking_uri("file:///app/mlruns")
mlflow.set_experiment("f1_pit_stop")
with mlflow.start_run():
# Hydraが管理するパラメータをMLflowに記録
mlflow.log_param("train_ratio", config.train_ratio)
mlflow.log_params(OmegaConf.to_container(config.model, resolve=True))
mlflow.log_params(OmegaConf.to_container(config.train, resolve=True))
mlflow.set_tag("model_type", "lightgbm")
# ... 学習 ...
mlflow.log_metrics({"oof_auc": oof_auc})
フローはこういうイメージです。
実験したい設定を config.yaml に書く
↓
python scripts/train.py model.learning_rate=0.01 で実行
↓
Hydraがパラメータを注入 → MLflowが記録 → outputs/ に自動保存
↓
MLflow UIで結果を比較
マルチランで6パターン流したとき、「どれが一番AUCが高かったか」をMLflow UIで一目で比較できます。Hydraがパラメータの整合性を担保して、MLflowが結果の可視化を担当する——役割分担がシンプルで気に入っています。
まとめ
- Hydraは
@hydra.main+config.yamlの組み合わせで、パラメータをスクリプトから切り離して管理できます - CLIオーバーライドで、スクリプトを変えずに実験条件を変えられます
-
-mフラグのマルチランで、パラメータの全組み合わせを一括実行できます -
outputs/に実行結果が自動保存されるので、「あの実験の設定」が後から追えます - Hydra使用時は作業ディレクトリが変わるので
get_original_cwd()を忘れずに - Hydra × MLflowの組み合わせで、実験管理の循環が完成します
「設定をYAMLに逃がす」というシンプルな行動が、実験の追跡可能性を大きく変えてくれると思います。
「再現できない実験は、やっていないのと大差ない」というのは、Hydraを使い始めてからより実感として持てるようになってきた気がします。
次回(第4回)は MLflow とOptunaを組み合わせた実験管理を詳しく書く予定です。
参考文献・リンク
- 浅野純季ほか『先輩データサイエンティストからの指南書 ―実務で生き抜くためのエンジニアリングスキル』技術評論社、2025年(ISBN: 978-4-297-15100-3)- https://gihyo.jp/book/2025/978-4-297-15100-3
- Kaggle Playground Series S6E5 – Predicting F1 Pit Stops
- Hydra 公式ドキュメント
- facebookresearch/hydra(GitHub)
- OmegaConf ドキュメント
- 設定管理ツール Hydra で内部構造ごと書き換える。
- 【機械学習】Hydraで「再現できない実験」とおさらば - Qiita