Skip to main content

W&B로 대규모 하이퍼파라미터 실험 수행하기

이 글에서는 Weights & Biases를 사용하여 수천 번의 실행으로 이루어진 대규모 실험을 추적하고 체계적으로 관리하는 방법을 살펴봅니다. 이 글은 AI 번역본입니다. 오역이 있을 경우 댓글로 알려 주세요.
Created on September 12|Last edited on September 12

최근 저는 다음과 같은 주제를 대규모로 실험하기 시작했습니다: 어떻게 사전 학습된 이미지 모델이 다른 데이터셋으로 얼마나 잘 전이되는지제가 답을 찾고자 했던 질문은 다음과 같습니다:
이미지 분류기를 미세 튜닝하기에 가장 적합한 사전 학습 모델은 무엇일까?
이 질문에 답하는 일은 복잡합니다. 명확한 정답이 있는 것도 아닙니다. 이상적으로는 정확도와 추론 시간 같은 지표들 사이의 관계를 규명하고, 각 모델 유형을 어떻게 학습할지에 대한 명확한 레시피도 정의하고 싶습니다. 하이퍼파라미터의 다양한 조합을 추적하는 일은 벅찰 수 있지만, 바로 이런 부분에서 Weights & Biases가 큰 도움을 줍니다.
이 글에서는 하이퍼파라미터 탐색을 수행할 수 있도록 스크립트를 리팩터링하는 모범 사례를 소개합니다 W&B Sweeps이 글은 추후 하이퍼파라미터 탐색을 오케스트레이션할 수 있도록 학습 코드를 체계적으로 정리하는 레시피입니다. 그럼 시작해 봅시다!
이미 하이퍼파라미터 탐색에 익숙하다고 가정하겠습니다. 아니라면 아래 접힌 섹션을 참고하세요 ⬇️
💡

목차



Sweeps 입문(클릭하여 펼치기)

학습 스크립트 만들기

저는 Jupyter 노트북에서 프로토타입을 만드는 것을 선호합니다. 그다음 코드를 리팩터링해 학습 스크립트를 수백 번도 실행할 수 있도록 준비합니다. 이상적으로는 코드를 여러 개의 매개변수화된 함수로 나누고, 이를 모두 하나로 묶어 처리할 수 있는 train 함수에 넣는 방식이 좋습니다.
보통은 데이터셋을 올바른 형식으로 가져오는 함수를 만들어 둡니다…
def get_dataloader(batch_size, img_size, seed=42, method="crop"):
"Use fastai to get the Dataloader for the Oxford Pets dataset"
dataset_path = untar_data(URLs.PETS)
files = get_image_files(dataset_path/"images")
dls = ImageDataLoaders.from_name_re(dataset_path, files,
r'(^[a-zA-Z]+_*[a-zA-Z]+)',
valid_pct=0.2,
seed=seed,
bs=batch_size,
item_tfms=Resize(img_size, method=method))
return dls
…그리고 그다음에는 학습 루프를 만듭니다. 이 예시에서는 저는 사용합니다 fastai:
def train(wandb_project: str="my_fancy_project",
batch_size: int=64,
img_size: int=224,
seed: int=42,
resize_method: str="crop",
model_name: str:"convnext_tiny",
epochs: int=5,
learning_rate: float=2e-3):
"Create a run and train the model"
with wandb.init(project=wandb_project, group="timm"):
dls = get_dataloader(batch_size, img_size, seed, resize_method)
learn = vision_learner(dls,
model_name,
metrics=[accuracy, error_rate],
cbs=WandbCallback(log_preds=False)).to_fp16()
learn.fine_tune(epochs, learning_rate)

😁 우리가 사용하는 만큼 fastai 그리고 우리는 를 사용한 긴밀한 통합을 제공합니다 WandbCallback, 수동으로 로깅을 설정할 필요가 없습니다.
💡

코드 리팩터링

가장 먼저 할 일은 매개변수를 하나씩 넘��는 대신, 함수가 인자로 config 딕셔너리를 받도록 바꾸는 것입니다. 이렇게 하면 나중에 매개변수를 쉽게 덮어쓸 수 있습니다.
프로 팁: 저는 …를 사용하는 것을 선호합니다 SimpleNamespace ~ 대신에 dict 점 표기법으로 요소에 속성처럼 접근할 수 있도록 하기 위해서입니다.
💡
import wandb
from fastai.vision.all import *
from fastai.callback.wandb import WandbCallback

config_defaults = SimpleNamespace(
batch_size=64,
epochs=5,
learning_rate=2e-3,
img_size=224,
resize_method="crop",
model_name="convnext_tiny",
seed=42,
wandb_project='my_fancy_project')

def train(config=config_defaults):
with wandb.init(project=config.wandb_project, config=config):
config = wandb.config #we have to add this line, so we can inject the parameters afterwards
dls = get_dataloader(config.batch_size, config.img_size, config.seed, config.resize_method)
learn = vision_learner(dls,
config.model_name,
metrics=[accuracy, error_rate],
cbs=WandbCallback(log_preds=False)).to_fp16()
learn.fine_tune(config.epochs, config.learning_rate)

if __name__ == "__main__":
train()
보시다시피, 이 train 함수는 입력으로 a를 받습니다 config 매개변수입니다. 이는 머신러닝 학습 스크립트에서 매우 일반적인 패턴으로, 보통 이런 매개변수들을 다음과 같이 정의합니다. YAML 파일 또는 파이썬 딕셔너리입니다. 우리는 이것을 그대로 담아 넣어 train.py 파일에 넣으면 스위프를 실행할 준비가 끝납니다!

스크립트로 변환하기(선택 사항)

거의 다 왔습니다. 저는 스크립트를 커맨드라인에서 실행할 수 있게 만드는 것을 선호해서, 보통 다음 도구를 이용해 변환합니다. argparse
Python에서 스크립트에 매개변수를 전달하는 CLI 옵션은 여러 가지가 있습니다: argparse, fastai의 call_parse, Typer, Fire, ml_collections, 몇 가지만 예로 들었습니다. 선호하는 옵션이 있다면, 그 이유와 이 설정에 어떻게 통합하면 좋을지 알려 주세요.
💡
우리는 사용할 것입니다 argparse 기본적으로 Python에서 사용하는 것처럼:
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--batch_size', type=int, default=config_defaults.batch_size)
parser.add_argument('--epochs', type=int, default=config_defaults.epochs)
parser.add_argument('--learning_rate', type=float, default=config_defaults.learning_rate)
parser.add_argument('--img_size', type=int, default=config_defaults.img_size)
parser.add_argument('--resize_method', type=str, default=config_defaults.resize_method)
parser.add_argument('--model_name', type=str, default=config_defaults.model_name)
parser.add_argument('--seed', type=int, default=config_defaults.seed)
parser.add_argument('--wandb_project', type=str, default='my_fancy_project')
return parser.parse_args()
...그리고 프로그램의 main에서 이를 사용할 수 있습니다. 이렇게 하면 무언가를 테스트하고 싶을 때 학습 스크립트의 인자를 즉석에서 오버라이드할 수 있습니다.
if __name__ == "__main__":
args = parse_args()
train(config=args)

모든 것을 하나로 정리하기

최종 스크립트 train.py 다음과 같습니다:
import wandb
import argparse
from fastai.vision.all import *
from fastai.callback.wandb import WandbCallback

config_defaults = SimpleNamespace(
batch_size=64,
epochs=5,
learning_rate=2e-3,
img_size=224,
resize_method="crop",
model_name="convnext_tiny",
seed=42,
wandb_project='my_fancy_project',
)

def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--batch_size', type=int, default=config_defaults.batch_size)
parser.add_argument('--epochs', type=int, default=config_defaults.epochs)
parser.add_argument('--learning_rate', type=float, default=config_defaults.learning_rate)
parser.add_argument('--img_size', type=int, default=config_defaults.img_size)
parser.add_argument('--resize_method', type=str, default=config_defaults.resize_method)
parser.add_argument('--model_name', type=str, default=config_defaults.model_name)
parser.add_argument('--seed', type=int, default=config_defaults.seed)
parser.add_argument('--wandb_project', type=str, default=config_defaults.wandb_project)
return parser.parse_args()

def get_dataloader(batch_size, img_size, seed=42, method="crop"):
dataset_path = untar_data(URLs.PETS)
files = get_image_files(dataset_path/"images")
dls = ImageDataLoaders.from_name_re(dataset_path, files,
r'(^[a-zA-Z]+_*[a-zA-Z]+)',
valid_pct=0.2,
seed=seed,
bs=batch_size,
item_tfms=Resize(img_size, method=method))
return dls


def train(config=config_defaults):
with wandb.init(project=config.wandb_project, config=config):
config = wandb.config. #we have to add this line, so we can inject the parameters afterwards
dls = get_dataloader(config.batch_size, config.img_size, config.seed, config.resize_method)
learn = vision_learner(dls,
config.model_name,
metrics=[accuracy, error_rate],
cbs=WandbCallback(log_preds=False)).to_fp16()
learn.fine_tune(config.epochs, config.learning_rate)

if __name__ == "__main__":
args = parse_args()
train(config=args)
이제 명령줄에서 이 스크립트를 직접 호출하고, 인자를 전달하여 파라미터를 오버라이드할 수 있습니다.
명령줄 인터페이스는 argparse 생성합니다. 서로 다른 인자들의 목록은 다음을 전달하여 얻을 수 있습니다 --help 플래그
이제 스크립트를 적절한 플래그와 함께 호출하여 어떤 파라미터든 오버라이드할 수 있습니다.
  • python train.py --img_size=128 --batch_size=32예를 들어 , 는 이미지 크기와 배치 크기를 이러한 값으로 오버라이드합니다. 또한 공백으로 구분하여 전달할 수도 있습니다. --img_size 128 ~ 대신에 =

스위프 스위프 스위프 🧹

이제 하이퍼파라미터 검색에 우리 스크립트를 사용할 준비가 되었습니다. 시작해 봅시다!

스위프 구성 준비하기

여러 가지가 있습니다 스위프를 실행하는 방법, 하지만 권장되는 방법은 …을 사용하는 것입니다 wandb sweep 명령어이를 위해서는 우리가 …을 만들어야 합니다 YAML 메서드와 우리가 탐색할 하이퍼파라미터가 담긴 파일입니다. 이 파일을 다음과 함께 저장하세요 train.py 스크립트를 준비했으면 이제 시작할 수 있습니다!
program: train.py
method: bayes
metric:
name: valid_loss
goal: minimize
parameters:
model_name:
values: ['levit_128s', 'resnet18', 'resnet34d', 'convnext_base', 'regnetx_064']
learning_rate:
min: 0.0001
max: 0.1
resize_method:
values: ['crop', 'squish']

스위프 만들기

스위프 컨트롤러는 우리 서버에서 실행됩니다(또는 당신이 직접 실행할 수도 있습니다) 로컬 스위프 컨트롤러 (자체 인프라에서 실행할 수도 있습니다). 컨트롤러가 하이퍼파라미터 분배를 관리하고 모든 스위프 결과를 수집해 반환합니다.

컨트롤러를 실행한 뒤에는 스위프 ID를 확인해야 합니다.

에이전트 실행하기

이제 준비가 끝났습니다. 실제로 작업을 수행할 에이전트(컴퓨터)를 실행할 수 있습니다. 스위프를 실행할 각 컴퓨터에서 다음 명령을 실행하세요:
$ wandb agent {SWEEP_ID}

좋습니다, 이제 스위프가 실행 중입니다!
특정 횟수의 실행 후에 중지하고 싶다면, 다음을 전달하면 됩니다 wandb agent sweep_id --count 100 최대 100개의 실험만 실행하세요.
💡

맺음말 및 추가 자료

제가 선호하는 스위프 실행 방식입니다. 저는 Jupyter를 정말 좋아해서, 실제로 모든 코딩과 코드/스크립트 준비를 Jupyter Lab 안에서 합니다. 다만, 비싼 인스턴스에서 주말 내내 돌아갈 가능성이 큰 스위프를 실행할 때는 이 방법이 훨씬 더 신뢰할 수 있습니다.


이 글은 AI로 번역된 기사입니다. 오역이 의심되는 부분이 있다면 댓글로 알려 주세요. 원문은 아래 링크에서 확인할 수 있습니다: 원문 보고서 보기
Adam Geringer
Adam Geringer •  
. typo?
1 reply
alleniver
alleniver •  
useful article! thx man
1 reply
Nick White
Nick White •  
This is such a helpful and clear intro, Thomas! Thank you!
1 reply