Hydraでハイパーパラメータを一元管理
Hydra について
Hydra は、研究や複雑なアプリケーションの開発で使用されるPythonのフレームワークです。パラメータや設定を簡単に管理できる特徴があります。
今回は機械学習で使用する際を想定し、ログの保存や複数実行する方法などについてまとめました。
インストール
> pip install hydra-core
基本的な使い方
初めに以下のようなディレクトリ構成を想定します。
conf
└ config.yaml
main.py
設定ファイル config.yaml
で管理したいパラメータを以下のように記述します。
hyper_parameter:
learning_rate : 1e-5
必要なライブラリとデコレータ、適切な引数を使用することで設定ファイルのパラメータに簡単にアクセスできます。
import hydra
from omegaconf import DictConfig
@hydra.main(version_base=None, config_path="conf", config_name="config")
def main(cfg: DictConfig) -> None:
print(cfg.hyper_parameter.learning_rate)
if __name__ == "__main__":
main()
> Python main.py
1e-05
- デコレータ
@hydra.main()
を使って関数に定義された処理を修飾します。 - 修飾された関数の引数を
cfg: DictConfig
にすることでYAML形式で定義された設定ファイルのパラメータが格納されたオブジェクトを利用できます。 - 定義したパラメータは
cfg
経由で参照できます。 - デコレータ
@hydra.main()
の引数は次を指定します。-
version_base
:Hydraのバージョン(None
の場合は最新のマイナーバージョンのデフォルトを使用します。Hydra 1.2以降は省略すると1.1が使用され、指定を求める警告が表示されます。) -
config_path
:設定ファイルの配置されているディレクトリ -
config_name
:拡張子を除いたYAML形式の設定ファイル名
-
実行プログラム内の変数から取得するパラメータを選択
次のように文字列を格納した変数から取得するパラメータを選択することもできます。
(前略)
def main(cfg: DictConfig) -> None:
strings = "learning_rate"
print(cfg.hyper_parameter[strings])
(後略)
> Python main.py
1e-05
実行時パラメータ指定
実行時にパラメータを指定できます。その場合には設定ファイルのパラメータより実行時に指定したパラメータが優先されます。
> Python main.py hyper_parameter.learning_rate=1e-6
1e-06
ハイパーパラメータの履歴・ログを管理
通常、実行後には以下のようなディレクトリとファイルが生成されます。
config.yaml
は実行時に自動的にコピーされます。
output
└ [実行日(%Y-%m-%d)]
└ [実行時刻(%H-%M-%S)]
├ .hydra
│ └ config.yaml
│ └ hydra.yaml
│ └ overrides.yaml
└ main.log
-
config.yaml
:ユーザが指定したYAML形式の設定ファイルのコピー(実行時にパラメータを指定した場合はそのパラメータが上書きされます。) -
hydra.yaml
:Hydraの構成 -
overrides.yaml
:実行時に指定されたパラメータ -
main.log
:実行ファイルのログを保存したログファイル
自動生成されるディレクトリの位置や名称を変更
自動生成ファイルの位置やディレクトリ名を変更する場合は hydra.run.dir
を変更して実行します。変更する方法は2種類あります。
-
config.yaml
で以下を追記する。
hydra:
run:
dir: outputs/${now:%Y-%m-%d_%H-%M-%S}
- 実行時引数で指定する。
> python main.py hydra.run.dir=outputs/${now:%Y-%m-%d_%H-%M-%S}
どちらの場合でも以下のようなディレクトリ構成が生成されます。
output
└ [実行時刻(%Y-%m-%d_%H-%M-%S)]
├ .hydra
│ (以下略)
ユーザが設定したパラメータをディレクトリ名に組み込む
ユーザーが設定した変数をディレクトリ名に含めることもできます。
config.yaml
で次のように設定したとします。
hydra:
run:
dir: outputs/lr_${hyper_parameter.learning_rate}
この場合、以下のようなディレクトリ構成が生成されます。
output
└ lr_1e-05
├ .hydra
│ (以下略)
ログを生成・保存
ログはPythonのロギング機能を同時に利用することで簡単に管理できます。
次のような実行ファイルを想定します。
import hydra
from omegaconf import DictConfig
import logging
log = logging.getLogger(__name__)
@hydra.main(version_base=None, config_path="conf", config_name="config")
def main(cfg: DictConfig) -> None:
log.info("---Process Start!---")
log.info("info level message")
log.debug("debug level message")
log.info(f"Learning rate: {cfg.hyper_parameter.learning_rate}")
log.info("---Process End!---")
if __name__ == "__main__":
main()
この場合の main.log
およびコンソールの出力は以下のようになります。
Hydraは通常、コンソールとログファイルに INFO
レベルでログを記録します。
[2024-09-24 14:40:12,337][__main__][INFO] - ---Process Start!---
[2024-09-24 14:40:12,337][__main__][INFO] - info level message
[2024-09-24 14:40:12,337][__main__][INFO] - Learning rate: 1e-05
[2024-09-24 14:40:12,337][__main__][INFO] - ---Process End!---
DEBUG
レベルで記録したい場合は以下の2種のどちらかで実行します。
-
config.yaml
で以下を追記する。
hydra:
verbose: __main__
-
hydra.verbose=__main__
をつけて実行する。
> python main.py hydra.verbose=__main__
この場合の main.log
およびコンソールの出力は以下のようになります。
DEBUG
レベルのログも保存されていることが確認できます。
[2024-09-24 14:30:16,970][__main__][INFO] - ---Process Start!---
[2024-09-24 14:30:16,970][__main__][INFO] - info level message
[2024-09-24 14:30:16,970][__main__][DEBUG] - debug level message
[2024-09-24 14:30:16,970][__main__][INFO] - Learning rate: 1e-05
[2024-09-24 14:30:16,970][__main__][INFO] - ---Process End!---
※注:ロギング機能によって制御されない print
文などは保存されません。
.log
ファイル / .hydra
ディレクトリを生成したくない場合
以下の2種のどちらかを実行します。
常時生成したくない場合は config.yaml
を変更、一時的に生成したくない場合には実行時引数で指定することを推奨します。
-
config.yaml
で以下を追記する。
hydra:
output_subdir: null # .hydra 以下ファイルの生成停止
defaults:
- override hydra/hydra_logging: disabled # .hydra 以下ファイルの生成停止
- override hydra/job_logging: disabled # ログ出力・記録停止
- 実行時に以下のような引数を指定する。
> python main.py hydra.output_subdir=null hydra/hydra_logging=disabled hydra/job_logging=disabled
補足
-
.hydra
ディレクトリとそれ以下ファイルの生成を停止する場合はoutput_subdir
とhydra/hydra_logging
の両方を変更してください。 - ログファイルの停止は
hydra/job_logging
を変更してください。
outputs
ディレクトリを生成したくない場合
そもそも outputs
ディレクトリを生成したくない場合は上記の設定に加えて、設定ファイル config.yaml
または 実行時引数で hydra.run.dir
を ./
に変更します。
-
config.yaml
で以下を追記する
hydra:
run:
dir: ./
(以下略)
- 実行時引数を追加する
> python main.py hydra.run.dir=./ hydra.output_subdir=null hydra/hydra_logging=disabled hydra/job_logging=disabled
複数実行
ハイパーパラメータを指定した候補(要素)からそれぞれのパターンで実行する multirun
を利用すると比較・検証に便利です。
2つ以上のパラメータで2つ以上の候補を用意してもすべての組み合わせで実行されます。
以下はコマンドラインから実行する場合の例です。
※注:要素間に空白を入れないこと
> python main.py -m hyper_parameter.learning_rate=1e-5,1e-4
上を実行した場合、通常、以下のようなディレクトリ構成になります。
multirun
└ [実行日(%Y-%m-%d)]
└ [実行時刻(%H-%M-%S)]
├ 0 (以下略)
└ 1 (以下略)
ジョブ番号の下に通常実行時と同様の .hydra
ディレクトリやログファイルが配置されます。
この場合、config.yaml
からパラメータの値を毎回確認する必要があります。
自動生成されるディレクトリの位置や名称を変更
生成されるディレクトリ名にパラメータを含める場合は config.yaml
で ${hydra.job.override_dirname}
を使用して次のように変更します。
hydra:
sweep:
dir: multirun_${now:%Y-%m-%d_%H-%M-%S}
subdir: ${hydra.job.override_dirname}
同様に実行した場合、以下のようなディレクトリ構成になります。
multirun_[実行時刻(%Y-%m-%d_%H-%M-%S)]
├ hyper_parameter.learning_rate=0.0001 (以下略)
└ hyper_parameter.learning_rate=1e-05 (以下略)
補足
-
.hydra
ディレクトリやログファイルが保存されるディレクトリはhydra.core.hydra_config.HydraConfig.get().runtime.output_dir
で取得可能です。 - hydra1.1以前ではカレントディレクトリがデフォルトで(
hydra.job.chdir=True
が指定されており)変更されるため注意が必要です。 - 今回は1ファイルで一元管理する前提で述べています。詳しくは述べませんが、2ファイル以上で管理する方法もあります。
(config.yaml
でデフォルトで使用するyamlファイルを設定し、config.yaml
と同じ階層に新たなディレクトリを作成、その配下に使用するデータベースやモデルごとにyamlファイルを作成・使用することで2ファイル以上で管理できます。) - 参考:Getting started | Hydra
終わりに
Hydraの使い方を簡単に、特に知っておくと便利だった仕様を中心にまとめてみました。何か間違っているところなどあれば、コメントなどで優しく指摘いただけると幸いです。ハイパーパラメータの管理で困っている方、Hydraを使い始める方の一助になれば幸いです。ここまでお読みいただき、ありがとうございました。
Discussion