목차
딥러닝 모델을 구현하여 학습시킬 때, hyperparameter에 따라 여러 번의 실험을 해주어야 한다. 이때, "config.yaml"라는 파일을 생성하여 학습 코드 실행에 필요한 argument들(hyperparameter 뿐만 아니라 dataset path 등 여러 설정 값들)을 관리해주면 편하게 실험을 진행할 수 있다.
이번 글에서는 이렇게 설정 파일을 통해 실험을 효율적으로 할 수 있게 해주는 tool인 facebook에서 공개한 hydra라는 프레임워크의 사용법을 간단히 정리해보려 한다.
Hydra
Hydra는 Configuration 파일을 다루고 관리하는 라이브러리이다. 이 라이브러리를 사용하면 Command line에서 파이썬을 실행할 때 argument parsing을 훨씬 간단하게 할 수 있다. 또한, 계층 구조로 configuration을 관리하는 것도 가능하다.
사용법도 아주 쉽다. 아래 내용을 보자.
Quick Start Guide
Example of config.yaml
딥러닝을 학습시킬 때, configuration 파일은 보통 다음과 같은 형태로 주어진다.
mode: "train" # ["train", "val", "test"]
seed: 1
data_root_dir: "[dataset_root_dir]"
dev_root_dir: "[dev_root_dir]"
device:
use_gpu: True
num_workers: 16
cudnn_benchmark: True
cudnn_deterministic: False
distributed:
use_ddp: False
num_gpus: 1 # if use_ddp=False, num_gpus should be 1
data:
dataset_dir: "${data_root_dir}/..."
model:
[method_1]:
method: [method_name]
arch:
[...]
[method_2]:
method: [method_name]
arch:
[...]
[method_3]:
method: [method_name]
arch:
[...]
optimizer:
type: [module]
method: Adam
lr: 0.0001
betas: [ 0.9, 0.999 ]
eps: 1e-08
weight_decay: 0
scheduler:
[module_1]:
milestones: [400]
gamma: 0.1
[module_2]:
milestones: [400]
gamma: 0.1
train:
batch_size: 128
epochs: 800
test:
batch_size: 1
mask_flag: 200
epochs: 500
log:
exp_name: "[exp_name]_${mode}_${now:%Y-%m-%d}/${now:%H-%M-%S}"
log_dir: "${data_root_dir}/${mode}/${now:%Y-%m-%d}/${now:%H-%M-%S}"
use_wandb: False
wandb:
use_wandb: False
entity: "ray_park" # wandb id
project: "[project_name_for_wandb]"
# hydra
hydra:
run:
dir: ${log.log_dir}
defaults:
# - hydra/launcher: submitit_slurm
- override hydra/job_logging: colorlog
- override hydra/hydra_logging: colorlog
- _self_
복잡해 보이지만, 각 모듈에 들어가서 hyperparameter를 하나하나씩 관리해주는 것보다 훨씬 간단하고 빠르다.
참고로 대괄호([])안에 있는 것은 상황마다 달라질 수 있는 내용이고, ${mode} 와 같이 달러 표시와 중괄호 안에 있는 내용은 해당 값(위 예시에서 ${mode}는 "train" 값)을 가져오게 된다.
필자는 CONFIG라는 클래스를 따로 만들어 Data Distributed Parallel 사용 여부에 따라 실험 전체에 필요한 argument들을 관리한다. 예시는 아래와 같다.
class CONFIG(object):
def __init__(self, config):
self.config = config
if config.distributed.num_gpus > 1:
self.is_master = is_master_proc(config.distributed.num_gpus)
def info(self, content):
if self.config.distributed.num_gpus > 1 and self.is_master:
logging.info(content)
elif self.config.distributed.num_gpus == 1:
logging.info(content)
else:
raise NotImplementedError
예시는 hydra로부터 config 인스턴스를 받아 DDP 사용 여부에 따라 공통적으로 configuration을 적용(특히 logging해주기 위해)해주는 부분이다.
실제 사용할 때는 main 함수에 "cfg = CONFIG(config)"와 같이 선언해주어 다양한 변수를 활용한다.
좀 더 자세한 적용 사례는 깃허브(https://github.com/JJukE/Skeleton)를 참조하자.
Python file(.py)에서 실행하는 경우 - @hydra.main() decorater
.py 파일에서 실험을 진행할 경우, main 함수 위에 decorater 및 약간의 인자만 추가해주면 된다.
import hydra
import os
@hydra.main(version_base=None, config_path="[config_dir_path]", config_name="[config.yaml]")
def main(config):
num_gpu = config.distributed.num_gpu
...
실행하면 num_gpu 변수에는 위 config.yaml 예시에서 distributed 하위의 num_gpu에 해당하는 값(1)이 들어갈 것이다.
Jupyter notebook(.ipynb)에서 실행하는 경우 - initialize() and compose()
Jupyter notebook에서는 decorator 함수를 사용할 수 없다. 따라서 별도로 제공하는 compose API를 사용해야 한다.
https://hydra.cc/docs/1.0/experimental/compose_api/
.py 파일에서 configuration 파일의 위치 등을 설정해주듯, jupyter notebook에서는 initialize()와 compose() 함수를 활용한다.
from hydra import compose, initialize
if __name__ == "__main__":
# context initialization
with initialize(version_base=None, config_path="[config_file_dir]", job_name="[job_name]"):
config = compose(config_name="config.yaml", overrides=["db=mysql", "db.user=me"])
# global initialization
hydra.core.global_hydra.GlobalHydra().instance().clear()
initialize(version_base=None, config_path="[config_file_dir]", job_name="[job_name]")
config = compose(config_name="config.yaml", overrides=["db=mysql", "db.user=me"])
context initialization은 with 구문 안에서만 hydra의 설정을 적용하여 "config"변수로 yaml파일 내용들을 불러오는 것이고, global initialization은 실행한 코드 전체(정확히 말하자면 jupyter notebook kernel이 끝나기 전까지)에 대해 hydra 설정을 계속 하는 것이다.
필자는 global initialization으로 해주고, hydra 설정을 바꾸고 싶으면 hydra.core.global_hydra.GlobalHydra().instance().clear()를 통해 설정 값을 초기화해준 후 다른 설정을 다시 해주는 것을 선호한다.
최근댓글