Hydra?
- configuration을 모듈로 세분화하여 관리 가능
- 미리 정의한 configration을 hydra 환경에서 사용
- 실행된 hydra 환경에서의 실행 기록을 outputs/날짜/시간/.hydra/config.yaml 에 저장
- 어떤 configuration을 사용했는 지 모두 기록해 실험 관리에 도움
├── outputs
│ └── 2022-06-19
│ └── 19-23-30
│ ├── .hydra
│ └── config.yaml
| └── ...
- MNIST 분류 문제를 해결하는 경우 다음과 같이 config 구성 가능
Configuration 구성
- 모듈별 폴더를 생성해 configuration 구성
- model에서 처럼 다양하게 구성해뒀다가 실험 환경에 맞게 변경 가능
├── configs
│ ├── config.yaml
│ ├── checkpoint
│ │ └── checkpoint.yaml
│ ├── data
│ │ └── mnist.yaml
│ ├── early_stopping
│ │ └── early_stopping.yaml
│ ├── logger
│ │ └── tensorboard.yaml
│ ├── lr_monitor
│ │ └── lr_monitor.yaml
│ ├── model
│ │ ├── resnet18.yaml
│ │ └── resnet34.yaml
│ ├── optimizer
│ │ └── sgd.yaml
│ ├── scheduler
│ │ └── steplr.yaml
│ └── trainer
│ └── trainer.yaml
config.yaml
- configs의 main 역할
- 각 모듈에서 선택하고 싶은 yaml을 defaults를 통해서 지정 가능
- 별도의 조작 없으면 defaults에 지정된 yaml이 사용됨
- 다른 모듈의 변수들을 참조 가능하기 때문에 공통되는 변수는 여기에 선언하는 것을 추천
- python trainer.py model=resnet34 와 같이 선언하면 model의 resnet18 대신 resnet34.yaml이 선택 됨
- 위 경우 outputs에 resnet18 대신 resnet34가 사용됐다고 log 남음
# 공통 변수
seed : 42
monitor : "val_loss"
defaults:
- model: resnet18
- data : mnist
- optimizer : sgd
- scheduler : steplr
- trainer : trainer
- logger : tensorboard
- early_stopping : early_stopping
- lr_monitor : lr_monitor
- checkpoint : checkpoint
변수 참조 방법
- ${변수} 형태
- 이때, config.yaml(main config)의 변수는 폴더 명 없이 참조 가능
- 다른 폴더의 변수를 참고하기 위해서는 ${폴더명.변수명}
# config.yaml의 monitor 참조
monitor: ${monitor}
# model 폴더의 yaml들이 data 폴더의 num_classes 변수를 참조하고 싶은 경우
num_classes : ${data.num_classes}
변수 선언 예시
- trainer.yaml
- cfg.trainer를 참조하면 다음과 같이 파라미터로 사용 가능
- Trainer(**cfg.trainer)
max_epochs: 30
deterministic: true
log_every_n_steps: 50
track_grad_norm: 2
profiler: simple
accelerator: gpu
devices : 1
object instantiate
- 문자 그대로 생성할 instance의 정보를 저장할 수 있음
- 개인적으로 코드 가독성을 해치지 않는 선에서 활용하면 좋다고 생각
- resnet18.yaml을 활용한 예시
- _target_ : object
- _target_ 밑엔 instance를 만들 때 선언 할 parameter를 정의해 줌
- 이때, parameter는 tuple 형태 대신 list 형태로 선언해줘야 인식 됨
- ${data.num_classes} : data 모듈의 num_classes 참조
model:
_target_ : torchvision.models.resnet18
pretrained : false
num_classes : ${data.num_classes}
conv:
_target_ : torch.nn.Conv2d
in_channels : ${data.in_channels}
out_channels: 64
kernel_size : [1, 1]
stride : [1, 1]
padding : [1, 1]
hydra 사용하기
- 사용하고자 하는 함수에 @hydra.main으로 표시
- 이때, hydra 환경에선 cwd()가 hydra 실행에 따라 생긴 outputs 폴더 안으로 변경되기 때문에 실제 cwd()를 알고 싶다면 get_original_cwd 활용
from hydra.utils import get_original_cwd
from omegaconf import DictConfig
@hydra.main(config_path='configs', config_name="config")
def main_hydra(cfg: DictConfig):
seed_everything(cfg.seed, workers=True)
data = MNISTDataModule(data_dir=get_original_cwd(), val_split=cfg.data.val_split, num_workers=cfg.data.num_workers, batch_size=cfg.data.batch_size,
normalize=True, shuffle=True, pin_memory=True)
lit_model = Resnet(cfg)
# logger
logger = TensorBoardLogger(**cfg.logger)
# callbacks
early_stopping = EarlyStopping(**cfg.early_stopping)
lr_monitor = LearningRateMonitor(**cfg.lr_monitor)
check_point = ModelCheckpoint(**cfg.checkpoint)
# Trainer
trainer = Trainer(**cfg.trainer, logger=logger, callbacks=[early_stopping, lr_monitor, check_point])
trainer.fit(lit_model, datamodule=data)
trainer.test(lit_model, datamodule=data)
if __name__ == "__main__":
main_hydra()
- object instantiate를 활용하고 싶다면 다음과 같이 사용
from hydra.utils import instantiate
self.model = instantiate(cfg.model.model)
self.model.conv1 = instantiate(cfg.model.conv)
- optimizer와 scheduler는 다음과 같이 사용
# sgd.yaml
_target_ : torch.optim.SGD
lr: 0.1
momentum: 0.9
weight_decay: 1e-4
# class init
self.optimizer = cfg.optimizer
self.scheduler = cfg.scheduler
self.monitor = cfg.monitor
def configure_optimizers(self):
optimizer = instantiate(self.optimizer, self.parameters())
scheduler = instantiate(self.scheduler, optimizer)
return {"optimizer": optimizer, "lr_scheduler": scheduler, "monitor": self.monitor}
코드 샘플
- Hydra와 Lightning을 활용해 간단한 MNIST 분류기를 구성했고, 이는 링크를 통해 확인할 수 있다.
참고