実験管理って、やり始めるとすぐ気づくんですが、問題は1つじゃないです。
- 再現性:その実験、あとから同じ条件で動かせる?
- 比較:どの条件が良くて、どこが効いたのか一目で分かる?
この2つは似ているようで、解決策が少し違います。
- Hydra は「設定を整理して残す(再現性)」が得意
- MLflow は「結果を並べて比べる(比較・可視化)」が得意
なので、役割を分けて組み合わせると、実験管理が一段安定します。
Hydraの記事はこちら
👉 【機械学習】Hydraで「再現できない実験」とおさらば
MLflowの記事はこちら:
👉 【機械学習】MLflowで実験結果を「可視化・比較」する
この記事では、Hydra × MLflow を組み合わせて
- 実験ごとの出力フォルダ(Hydra)と
- 実験結果の比較UI(MLflow)
を 同じ「実験単位」で揃えて管理する方法を、最小構成から紹介します。
この記事でやること
最終的に、各実験で次の状態を作ります。
-
Hydraが
outputs/.../を作る
⇒.hydra/config.yamlが残る(再現性) -
同じ実行で MLflow にも Run が作られる
⇒ パラメータ・スコアが UI で比較できる(比較) -
さらに、Hydraの設定ファイル自体も MLflow 側に保存する
⇒ 「UIから見て→設定ファイルまで辿る」ができる
組み合わせの基本方針
Hydra × MLflow を綺麗に繋ぐコツは、次の3点です。
- Hydraの実行単位(run dir)を「実験の粒度」にする
- MLflowのRunに、Hydraの設定(確定後のconfig)を保存する
- MLflowに入れるparamsは「フラット化」して管理する(ネスト対策)
この方針で作ると、あとから実験を見返すときに迷いません。
ディレクトリ構成(例)
Hydra側は普通に conf/ を切っておけばOKです。
.
├── conf/
│ ├── config.yaml
│ └── model/
│ └── lightgbm.yaml
├── train.py
└── README.md
実装:Hydra × MLflow の最小構成
以下のコードは一旦これで回る最小セットです。
ポイント:
-
mlflow.set_experiment(cfg.exp_name)で実験名を揃える -
mlflow.log_metricでスコアを記録 -
cfgはネストが深いので、params用にフラット化する -
.hydra/config.yamlを artifactとして保存する(重要)
import os
from pathlib import Path
import hydra
import mlflow
from omegaconf import DictConfig, OmegaConf
def flatten_dict(d, prefix=""):
"""ネストしたdictを MLflow向けにフラット化する(a.b.c=...)"""
items = {}
for k, v in d.items():
key = f"{prefix}{k}" if prefix == "" else f"{prefix}.{k}"
if isinstance(v, dict):
items.update(flatten_dict(v, key))
else:
items[key] = v
return items
@hydra.main(config_path="conf", config_name="config", version_base=None)
def main(cfg: DictConfig):
# Hydraの実行ディレクトリ(outputs/....)を取得
run_dir = Path(os.getcwd()) # hydra実行中は cwd が run dir に切り替わる
# MLflowの実験名(Experiment)
mlflow.set_experiment(cfg.exp_name)
# 1実行 = 1Run
with mlflow.start_run(run_name=run_dir.name):
# 1) パラメータ記録(フラット化)
cfg_dict = OmegaConf.to_container(cfg, resolve=True)
flat_params = flatten_dict(cfg_dict)
# MLflowは一度に大量に入れると怒られることもあるので、必要に応じて絞る
mlflow.log_params(flat_params)
# 2) 設定ファイル自体も保存(artifact)
# Hydraが生成する確定config
hydra_config_path = run_dir / ".hydra" / "config.yaml"
if hydra_config_path.exists():
mlflow.log_artifact(str(hydra_config_path), artifact_path="hydra")
# overrides も保存しておくと差分が追いやすい
overrides_path = run_dir / ".hydra" / "overrides.yaml"
if overrides_path.exists():
mlflow.log_artifact(str(overrides_path), artifact_path="hydra")
# --- ここに学習処理を書く ---
# 例:ダミー
auc = 0.82
# 3) 指標を記録(metric)
mlflow.log_metric("auc", auc)
# 4) Hydra run dir のパスも残す(相互参照しやすい)
mlflow.set_tag("hydra.run_dir", str(run_dir))
print(f"[done] run_dir={run_dir}")
if __name__ == "__main__":
main()
使い方
python train.py
Hydra側:
-
outputs/yyy-mm-dd/hh-mm-ss/ができる -
.hydra/config.yamlが残る
MLflow側:
mlflow ui
で http://localhost:5000 を開くと
- Experiment =
cfg.exp_name - Run = 実行ごとの記録
- params/metrics が比較できる
さらに artifact に
hydra/config.yamlhydra/overrides.yaml
が付いてくるので、UI上から設定まで辿れます。
よくある詰まりどころと対処
1) mlflow.log_params(cfg_dict) がそのまま通らない
MLflowの params は基本「key: value(文字列/数値)」想定です。
Hydraのcfgはネストが深いので、フラット化が安定です(上の flatten_dict)。
- OK例:
model.params.learning_rate = 0.05 - NG例:
model.params = { ... }のような辞書丸ごと
2) mlflow.set_experiment に毎回別名を入れて散らかる
Experiment名は、最初は雑でもいいですが、散らかると見返しづらいです。
おすすめは
-
cfg.exp_nameは「大枠の目的」(例:lgbm_baseline) -
run_nameに日付時刻やHydra run dir を使う(例:22-00-00)
という分離です。
3) Hydraの実行ディレクトリが分からない
Hydra実行中はカレントディレクトリが run dir に変わるので、基本これで取れます:
run_dir = Path(os.getcwd())
もう一段便利にする小ネタ
1) 重要なメトリクスだけ「比較しやすい名前」で揃える
MLflowの比較は「列名」が命です。
aucloglosscv_mean_auc
みたいに、列名を固定すると後で比較が楽です。
2) 学習済みモデルや特徴量重要度も artifact に入れる
例えば LightGBM なら
-
model.txt/model.pkl feature_importance.csv
を保存すると、実験の価値が上がります。
mlflow.log_artifact("feature_importance.csv", artifact_path="artifacts")
おわりに
Hydra と MLflow を別々に使うだけでも便利ですが、
組み合わせると「実験の振り返り」がかなり楽になります。
- Hydra:実験条件が確実に残る(再現性)
- MLflow:結果を横並びで比べられる(比較)
そして、設定ファイルを artifact に載せておけば
「UIで見て→設定を確認して→再現する」が自然に繋がります。
まずはこの最小構成を動かして、
必要になったタイミングで artifact(モデルや図)を増やしていくのが無理がないと思います。