Search
Duplicate

간단한 예제로 살펴본 Hydra를 이용한 어플리케이션 구성

Created
7/4/2021, 8:58:00 AM
Tags
implementation
Hydra
Config
OmegaConf
일시
2021/05/31
글쓴이
hyungjun.kim
✅main
포스팅 종류
코드구현
Hydra: 페이스북에서 머신러닝 실험 등 복잡한 어플리케이션을 구성하기 위해 사용하는 오픈소스 프레임워크(https://github.com/facebookresearch/hydra) 입니다. 이 글에서는 Hydra의 철학과 간단한 사용법에 대해 알아봅니다.

Introduction

하나의 완성된 모형을 만들기 위해서는 데이터셋이나 학습 파라미터 값을 변경하여 적어도 수십번의 실험을 하게 됩니다. 이 때 사용된 arguments와 결과값들을 사람이 엑셀 혹은 메모장으로 관리 할 수도 있습니다. 그러나 arguments가 복잡할 수록 이를 구조화하거나 관리하기 힘들기 때문에 별도의 config 파일을 프로그램으로 작성하여 관리하는 것을 추천합니다. 신화 속의 동물 Hydra가 비슷하지만 서로 다른 머리를 가지면서도 하나의 몸에서 유기적으로 움직이듯이, configs를 통해 비슷한 여러 job을 관리 및 실행할 수 있도록 합니다. 이 글에서는 Hydra에서 어떻게 config 관리의 장점들을 강화하고 반대로 단점들을 보완하고 있는지 살펴봅니다.

Hydra의 Config 철학

01. 변경에 민감할 것

Config를 이용해 머신러닝 실험을 할 때 learning rate만 바꾸고 배치 사이즈는 동일하게 가져 갈 경우 서로 다른 2개의 파일(configs)을 별도로 미리 가지고 있어야 할까요? 만약 CLI(Command-Line Interface)에서 config에 원하는 값들만 덮어쓰기(override)가 가능하다면 모든 셋팅에 대한 config 파일이 없어도 될 것 입니다. 즉 작은 변경에 민감하게 디자인 되어야 합니다.
다음과 같은 config.yaml 이 있을 때
app: task: classfication optimizer: adamw
YAML
아래와 같이 실행하면 override할 수 있습니다.
python my_app.py app.task=regression
다음은 config 출력 결과입니다.
app: task: regression optimizer: adamw
YAML
또한, +를 이용해 새로운 arguments를 추가할 수 있습니다.
python my_app.py app.task=regression +app.epochs=10
app: task: regression optimizer: adamw epochs: 10
YAML

02. Monolithic 구조

머신러닝 실험에서 datasets, model, optimizer 등으로 구조화하여 실험을 할 수 있습니다. 또한 다양한 계층의 config를 구조화하여 통합 관리할 수 있습니다.
하위 config를 그대로 사용하기 위해 Hydra에서는 몇 가지 개념들을 도입하고 있습니다.
config group: 하위 그룹 config
package: config node path, conf/training/default.yaml -> conf.training
예시를 통해 이를 이해해보겠습니다. 아래의 예시에서 기본 config는 conf/config.yaml에서 정의하고 있습니다.
conf/config.yaml
defaults: - training: default - optimizer/adam_series: adamw
YAML
Config group으로 training과 optimizer를 사용하여 계층을 표현하고 있습니다. conf/training/default.yaml를 사용하고 싶다면 defaults를 이용해 사용할 수 있습니다. 즉 여기서는 기본값(defaults)으로 training의 기본값과 optimizer는 adamW를 사용합니다. 그런데 optimizer는 adam_series를 폴더로 한 단 계 더 계층을 이루고 있습니다. Hydra는 이러한 다층 구조를 통합적으로 관리할 수 있습니다. 즉, config.yaml과 각각의 config yaml들을 nested 형태의 구조로 구성할 수 있습니다.
다음은 optimizer에서 training에 있는 파라미터를 사용하는 예시 입니다. 즉 adamW에서는 training에 있는 lr을 package를 이용하여 변수로 값을 받을 수 있습니다.
conf/training/default.yaml
# @package training lr: 1e-5
YAML
conf/optimizer/adam_series/adamw.yaml
# @package optimizer _target_: torch.optim.AdamW lr: ${training.lr} weight_decay: 0.001
YAML
또한, adamW는 파이토치 객체인데 _target_과 hydra의 instantiate를 이용하여 어플리케이션 실행 시 이를 해석 할 수 있습니다.
src/my_hydra.py
@hydra.main(config_path='../conf/', config_name='config.yaml') def my_app(cfg: DictConfig) -> None: print(OmegaConf.to_yaml(cfg)) model = torch.nn.Sequential( torch.nn.Linear(3, 1), torch.nn.Flatten(0, 1) ) opt = instantiate(cfg.optimizer, params=model.parameters())
Python
만약 adamW를 pacakge로 optimizer 명시하지 않고 이를 표현하려면 아래처럼 _group_을 이용하여 표현 할 수 있습니다.
conf/config.yaml
defaults: - /optimizer@_group_: adam_series/adamw - training: default
YAML

03. Interpolation

일반적으로 python에서 yaml을 로드하면 파이썬 객체를 바로 평가할 수 없습니다. 물론 pickle 등으로 serialization 및 deserialization를 할 수 있지만 serialization 시의 파이썬 환경에 의존하는 등 불편한 점이 있습니다. Hydra에서는 OmegaConf를 통해 이를 해결하고 있습니다. 위의 예제 conf/optimizer/adam_series/adamw.yaml에서 ${training.lr} 변수는 OmegaConf가 lazy evaluation을 하기 떄문에 해석이 가능합니다.
앞서 소개한 기능 외에도 multirun과 loggingtab completion 등의 다양한 기능도 있으니 자세한 내용들은 튜토리얼(https://hydra.cc/docs/tutorials/intro)을 참고하시길 바랍니다.
다음 편에서는 위의 내용들을 lightning-transformers을 사례를 보면서 정리해보겠습니다.
본 블로그는 hydra-core==1.1.0rc1lightning-transformers==0.1 버전을 기준으로 작성되었습니다.

References