Hydraを使う
Hydraといってもパスワード解除の方ではなく
Metaがリリースしてるコマンドのパラメータ設定なんかを便利にしてくれる方のやつです。
argparseでコマンドラインツールに引数を処理する場合が多いと思いますが、引数が多くなると管理するのがなかなかつらいってことがよく起きるわけです。
前にこのコマンド実行したときって何を引数に渡したっけ?なんてこともありますよね
- 階層的なコンフィギュレーション
- コマンドラインからコンフィグを上書きできる
- コマンドラインで補完してくれる
- アプリケーションをローカルで実行したり、リモートで実行できる
- 1つのコマンドで複数のジョブを異なる引数で実行できる。
インストールは以下を実行
pip install hydra-core --upgrade
これで準備はOKなんで、まずはチュートリアルを見てみましょうか
シンプルコマンドラインアプリケーション
コマンドラインで引数を渡してその内容をyamlに変換してプリントするという内容のアプリケーションです。
from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None)
def my_app(cfg: DictConfig) -> None:
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
my_app()
これを実行すると、引数に渡した値がYamlに変換されてプリントされます。
$ python my_app.py +db.driver=mysql +db.user=omry +db.password=secret
db:
driver: mysql
user: omry
password: secret
Hydraは実行するとoutputsというフォルダを自動で作成してコマンドを実行したときのログを出力してくれます。
設定ファイルを指定する
今度はyamlで設定ファイルを用意しておき
デコレータで設定ファイルを指定します。
Yamlファイルを作成して以下の内容で保存します。
保存先はPythonスクリプトと同じフォルダ内に。
db:
driver: mysql
user: omry
password: secret
Pythonスクリプトを用意します。
今度はデコレータにconfig_pathとconfig_nameを指定します。
from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path=".", config_name="config")
def my_app(cfg):
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
my_app()
これでconfig.yamlが自動的に読み込まれるようになります。
$D:\hydra\config_file> python my_app.py
db:
driver: mysql
user: omry
password: secret
今度はさらにコマンドラインに引数を追加して設定値を上書きしてみましょう
$D:\hydra\config_file> python my_app.py db.driver=postsql
db:
driver: postsql
user: omry
password: secret
dirver値がmysqlからpostsqlちゃんと値が変わっていますね。
設定ファイルに無い項目を引数に渡すとどうなるかというとパースエラーになります。
D:\hydra\config_file> python my_app.py db.secret
Error parsing override 'db.secret'
missing EQUAL at '<EOF>'
See https://hydra.cc/docs/1.2/advanced/override_grammar/basic for details
Set the environment variable HYDRA_FULL_ERROR=1 for a complete stack trace.
設定ファイルに無い項目を渡したい場合は引数にプレフィクスとして+を付けます
$d:\hydra\config_file> python my_app.py +db.secret=aaa
db:
driver: mysql
user: omry
password: secret
secret: aaa
項目secretが追加されました。
今度は設定項目がある値に対して+を付けて引数を渡すとこれもエラーとなります。
$d:\hydra\config_file> python my_app.py +db.user=hoge
Could not append to config. An item is already at 'db.user'.
Either remove + prefix: 'db.user=hoge'
Or add a second + to add or override 'db.user': '++db.user=hoge'
意図しない上書きを防ぐという意味でいい設計だと思います。
設定ファイルに無ければ追加、有れば上書きしたいという場合もあると思います。
そういう時はプレフィクスとして++を付けて引数を渡します。
$d:\hydra\config_file> python my_app.py ++db.user=hoge
db:
driver: mysql
user: hoge
password: secret
configオブジェクトを扱う
Hydraは実行時に自動的にconfigオブジェクトを作成します。configオブジェクトはomegaconfのDictConfigクラスのインスタンスです。
node: # Config is hierarchical
loompa: 10 # Simple value
zippity: ${node.loompa} # Value interpolation
do: "oompa ${node.loompa}" # String interpolation
waldo: ??? # Missing value, must be populated prior to access
from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path=".", config_name="config")
def my_app(cfg: DictConfig):
assert cfg.node.loompa == 10 # attribute style access
assert cfg["node"]["loompa"] == 10 # dictionary style access
assert cfg.node.zippity == 10 # Value interpolation
assert isinstance(cfg.node.zippity, int) # Value interpolation type
assert cfg.node.do == "oompa 10" # string interpolation
cfg.node.waldo # raises an exception
if __name__ == "__main__":
my_app()
$ python my_app.py
Traceback (most recent call last):
File "my_app.py", line 32, in my_app
cfg.node.waldo
omegaconf.errors.MissingMandatoryValue: Missing mandatory value: node.waldo
full_key: node.waldo
object_type=dict