Hydra : 페이스북에서 복잡한 config를 관리하기 위해 사용하는 오픈소스
NLP Model의 성능을 높이기 위해서는 다양한 하이퍼 파라미터 혹은 기법의 조합을 실험하고 이를 관리해야합니다.
이를 config 없이 수행한다면 일일이 변경해야하는 수고스러움이 생기기에 율적인 config 관리는 효율적은 학습에 도움이 됩니다.
이를 위해 Hydra Docs를 살펴보고자 합니다.
발췌 : Hydra Docs
# Hydra tree
├── conf
│ ├── config.yaml # default 설정
│ ├── db # Group db
│ │ ├── mysql.yaml
│ │ └── postgresql.yaml
│ └── __init__.py
└── my_app.py # 실행의 주체
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()
해당 예시에서 Hydra는 empty cfg object를 만들고 @hydra.main에 이를 전달합니다.
커맨드 라인에서 새로운 config value를 추가할 수 있는데 이 때 +
는 해당 field가 new라는 알려줍니다.
$ python my_app.py +db.driver=mysql +db.user=omry +db.password=secret
db:
driver: mysql
user: omry
password: secret
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_path와 config_name을 데코레이터에 명시함으로써 config file을 지정할 수 있습니다.
이 때 config 파일은 yaml 파일이여야 합니다.
커맨드라인에서 지정된 config 파일을 override 할 수 있는데 이 때는 +
를 사용하지 않습니다.
# 원본
$ python my_app.py
db:
driver: mysql
user: omry
password: secret
# override
$ python my_app.py db.user=root db.password=1234
db:
driver: mysql
user: root
password: 1234
또한 ++
를 사용해 기존에 존재하는 config를 override하거나 기존에 존재하지 않는 config를 추가할 수 있습니다.
# Override an existing item
$ python my_app.py ++db.password=1234
# Add a new item
$ python my_app.py ++db.timeout=5
directory를 통해 config group을 만들 수 있습니다.
├─ conf
│ └─ db
│ ├─ mysql.yaml
│ └─ postgresql.yaml
└── my_app.py
여기서 db directory 안에 있는 mysql, postgresql은 db group으로 묶였다고 할 수 있습니다.
from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path="conf")
def my_app(cfg: DictConfig) -> None:
print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
my_app()
이와 같이 config 파일을 명시하지 않아도 +GROUP=OPTION
을 통해 config group에서 특정 config를 지정할 수 있다.
$ python my_app.py +db=postgresql
db:
driver: postgresql
pass: drowssap
timeout: 10
user: postgres_user
# override도 가능하다.
$ python my_app.py +db=postgresql db.timeout=20
db:
driver: postgresql
pass: drowssap
timeout: 20
user: postgres_user
config.yaml을 통해 default configs를 설정할 수 있습니다.
# config.yaml
defaults:
- db: mysql
이와 같이 defaults를 통해 간단히 default configs를 설정할 수 있습니다.
또한 _self_
키워드를 통해 defaults의 override를 조정할 수 있습니다.
# config.yaml
defaults:
- db: mysql
- _self_
db:
user: root
# result
db:
driver: mysql # db/mysql.yaml
pass: secret # db/mysql.yaml
user: root # config.yaml
# config.yaml
defaults:
- _self_
- db: mysql
db:
user: root
# result
db:
driver: mysql # db/mysql.yaml
pass: secret # db/mysql.yaml
user: omry # db/mysql.yaml
_self
가 아래에 있을 경우 config 파일이 override 하지만 _self
가 위에 있을 경우에는 override되지 않는다.
>>> python my_app.py --multirun db=mysql,postgresql schema=warehouse,support,school
>>> python my_app.py -m db=mysql,postgresql schema=warehouse,support,school
해당 기능을 통해 여러개 조합의 config를 한번에 실행할 수 있다.
또한 sweeping을 할 수 있다.
# config.yaml
hydra:
sweeper:
params:
db: mysql,postgresql
schema: warehouse,support,school
# 다음과 같이 사용도 가능
x=range(1,10) # 1-9
schema=glob(*) # warehouse,support,school
schema=glob(*,exclude=w*) # support,school
Hydra는 Python logging을 간편하게 설정할 수 있게한다.
import logging
from omegaconf import DictConfig
import hydra
# A logger for this file
log = logging.getLogger(__name__)
@hydra.main()
def my_app(_cfg: DictConfig) -> None:
log.info("Info level message")
log.debug("Debug level message")
if __name__ == "__main__":
my_app()
$ python my_app.py
[2019-06-27 00:52:46,653][__main__][INFO] - Info level message
# config.yaml
defaults:
- db : mysql
# db/mysql.yaml
defaults:
- base_mysql
user: omry
password: secret
port: 3307
encoding: utf8
# db/base_mysql.yaml
host: localhost
port: 3306
user: ???
password: ???
이와 같이 순서대로 확장되는 경우 다음과 같이 output이 나온다.
>>> python my_app.py
db:
host: localhost # from db/base_mysql
port: 3307 # overridden by db/mysql.yaml
user: omry # populated by db/mysql.yaml
password: secret # populated by db/mysql.yaml
encoding: utf8 # added by db/mysql.yaml
├── config.yaml
├── db
│ ├── mysql.yaml
│ └── sqlite.yaml
└── server
├── apache.yaml
└── nginx.yaml
>>> python my_app.py
db:
name: mysql
server:
name: apache
port: 80
폴더 구조와 실행 결과값이 다음과 같을 때
# experiment/nglite.yaml
# @package _global_
defaults:
- override /db: sqlite
- override /server: nginx
server:
port: 8080
>>> python my_app.py +experiment=nglite
db:
name: sqlite
server:
name: nginx
port: 8080
다음과 같이 여러개의 설정을 한번에 바꿀 수 있다.
Key concepts:
다음과 같이 간단하게 sweeping 할 수도 있다.
>>> python my_app.py --multirun +experiment=aplite,nglite
[HYDRA] Launching 2 jobs locally
[HYDRA] #0 : +experiment=aplite
db:
name: sqlite
server:
name: apache
port: 8080
[HYDRA] #1 : +experiment=nglite
db:
name: sqlite
server:
name: nginx
port: 8080