이 가이드는 하이퍼파라미터 탐색을 최적화하기 위해 Python 트레이닝 스크립트 또는 노트북에 W&B를 통합하는 방법을 안내합니다.
아래와 같이 모델을 트레이닝하는 Python 스크립트가 있다고 가정해 보자. 목표는 검증 정확도(val_acc)를 최대화하는 하이퍼파라미터를 찾는 것이다.
Python 스크립트에서는 train_one_epoch와 evaluate_one_epoch 두 개의 함수를 정의한다. train_one_epoch 함수는 한 에포크 동안의 트레이닝을 시뮬레이션하고 트레이닝 정확도와 손실 값을 반환한다. evaluate_one_epoch 함수는 검증 데이터셋에 대해 모델을 평가하는 과정을 시뮬레이션하고 검증 정확도와 손실 값을 반환한다.
학습률(lr), 배치 크기(batch_size), 에포크 수(epochs)와 같은 하이퍼파라미터 값을 포함하는 설정 사전(config)을 정의한다. 설정 사전에 있는 값들은 트레이닝 과정을 제어한다.
그 다음 일반적인 트레이닝 루프를 흉내 내는 main 함수를 정의한다. 각 에포크마다 트레이닝 및 검증 데이터셋에 대해 정확도와 손실이 계산된다.
이 코드는 모의 트레이닝 스크립트다. 실제로 모델을 트레이닝하지 않고, 무작위 정확도와 손실 값을 생성해 트레이닝 과정을 시뮬레이션한다. 이 코드의 목적은 트레이닝 스크립트에 W&B를 어떻게 통합하는지 보여 주는 것이다.
import random
import numpy as np
def train_one_epoch(epoch, lr, batch_size):
acc = 0.25 + ((epoch / 30) + (random.random() / 10))
loss = 0.2 + (1 - ((epoch - 1) / 10 + random.random() / 5))
return acc, loss
def evaluate_one_epoch(epoch):
acc = 0.1 + ((epoch / 20) + (random.random() / 10))
loss = 0.25 + (1 - ((epoch - 1) / 10 + random.random() / 6))
return acc, loss
# 하이퍼파라미터 값을 담은 config 변수
config = {"lr": 0.0001, "batch_size": 16, "epochs": 5}
def main():
lr = config["lr"]
batch_size = config["batch_size"]
epochs = config["epochs"]
for epoch in np.arange(1, epochs):
train_acc, train_loss = train_one_epoch(epoch, lr, batch_size)
val_acc, val_loss = evaluate_one_epoch(epoch)
print("epoch: ", epoch)
print("training accuracy:", train_acc, "training loss:", train_loss)
print("validation accuracy:", val_acc, "validation loss:", val_loss)
if __name__ == "__main__":
main()
다음 섹션에서는 트레이닝 중 하이퍼파라미터와 메트릭을 추적하도록 Python 스크립트에 W&B를 추가합니다. 검증 정확도(val_acc)를 최대화하는 최적의 하이퍼파라미터를 찾기 위해 W&B를 사용할 것입니다.
트레이닝 스크립트에 W&B를 사용하도록 수정하세요. Python 스크립트나 노트북에 W&B를 통합하는 방법은 스윕을 어떻게 관리하는지에 따라 달라집니다.
W&B Python SDK를 사용하여 스윕을 시작, 중지 및 관리하려면 Python script or notebook 탭의 안내를 따르세요. 대신 W&B CLI를 사용하려면 CLI 탭의 안내를 따르세요.
스윕 설정이 포함된 YAML 설정 파일을 생성합니다. 이 설정 파일에는 스윕이 탐색할 하이퍼파라미터가 들어 있습니다. 다음 예시에서는 배치 크기(batch_size), 에포크(epochs), 학습률(lr) 하이퍼파라미터를 스윕마다 다르게 설정합니다.# config.yaml
program: train.py
method: random
name: sweep
metric:
goal: maximize
name: val_acc
parameters:
batch_size:
values: [16, 32, 64]
lr:
min: 0.0001
max: 0.1
epochs:
values: [5, 10, 15]
W&B Sweep 설정을 생성하는 방법에 대한 자세한 내용은 Define sweep configuration을 참고하세요.YAML 파일에서 program 키에 Python 스크립트 이름을 지정해야 합니다.다음으로, 예제 코드에 아래 내용을 추가하세요:
- W&B Python SDK(
wandb)와 PyYAML(yaml)을 임포트합니다. PyYAML은 YAML 설정 파일을 읽는 데 사용됩니다.
- 설정 파일을 읽습니다.
wandb.init()을 사용해 백그라운드 프로세스를 시작하여 데이터를 W&B Run으로 동기화하고 로깅합니다. config 객체를 config 파라미터로 전달합니다.
- 하드코딩된 값을 사용하는 대신
wandb.Run.config에서 하이퍼파라미터 값을 정의합니다.
wandb.Run.log()를 사용해 최적화할 메트릭을 로그합니다. 설정에서 정의한 메트릭을 반드시 로그해야 합니다. 설정 사전(이 예제에서는 sweep_configuration) 내에서 스윕이 최대화할 값으로 val_acc를 정의합니다.
import wandb
import yaml
import random
import numpy as np
def train_one_epoch(epoch, lr, batch_size):
acc = 0.25 + ((epoch / 30) + (random.random() / 10))
loss = 0.2 + (1 - ((epoch - 1) / 10 + random.random() / 5))
return acc, loss
def evaluate_one_epoch(epoch):
acc = 0.1 + ((epoch / 20) + (random.random() / 10))
loss = 0.25 + (1 - ((epoch - 1) / 10 + random.random() / 6))
return acc, loss
def main():
# 기본 하이퍼파라미터를 설정합니다
with open("./config.yaml") as file:
config = yaml.load(file, Loader=yaml.FullLoader)
with wandb.init(config=config) as run:
for epoch in np.arange(1, run.config['epochs']):
train_acc, train_loss = train_one_epoch(epoch, run.config['lr'], run.config['batch_size'])
val_acc, val_loss = evaluate_one_epoch(epoch)
run.log(
{
"epoch": epoch,
"train_acc": train_acc,
"train_loss": train_loss,
"val_acc": val_acc,
"val_loss": val_loss,
}
)
# main 함수를 호출합니다.
main()
CLI에서 스윕 에이전트가 시도할 최대 run 개수를 설정합니다. 이는 선택 사항입니다. 이 예제에서는 최대 개수를 5로 설정합니다.다음으로 wandb sweep 명령어로 스윕을 초기화합니다. YAML 파일 이름을 지정하세요. 필요에 따라 --project 플래그에 사용할 프로젝트 이름을 추가로 지정할 수 있습니다:wandb sweep --project sweep-demo-cli config.yaml
이 명령은 스윕 ID를 반환합니다. 스윕을 초기화하는 방법에 대한 자세한 내용은
Initialize sweeps를 참조하세요.스윕 ID를 복사한 다음, 아래 코드 스니펫에서 sweepID를 해당 값으로 바꿔
wandb agent 명령으로 스윕 작업을 시작합니다:wandb agent --count $NUM your-entity/sweep-demo-cli/sweepID
자세한 내용은 스윕 작업 시작 문서를 참조하세요. 다음 단계에 따라 Python 스크립트에 W&B를 추가하세요:
- 키-값 쌍이 sweep configuration을 정의하도록 사전 객체를 만드세요. sweep 설정은 W&B가 대신 탐색할 하이퍼파라미터와 최적화할 메트릭을 정의합니다. 이전 예제를 이어서, 배치 크기(
batch_size), 에포크(epochs), 학습률(lr)이 각 스윕에서 변경할 하이퍼파라미터입니다. 검증 점수의 정확도를 최대화하려고 하므로 "goal": "maximize"로 설정하고, 최적화하려는 변수의 이름을 지정합니다. 이 예제에서는 val_acc이므로 "name": "val_acc"로 설정합니다.
- 스윕 설정 사전을
wandb.sweep()에 전달합니다. 이를 통해 스윕이 초기화되고 스윕 ID(sweep_id)가 반환됩니다. 자세한 내용은 스윕 초기화를 참조하세요.
- 스크립트 상단에서 W&B Python SDK(
wandb)를 임포트합니다.
main 함수 내부에서 wandb.init()를 사용해 백그라운드 프로세스를 시작하고 데이터를 W&B Run으로 동기화하고 기록합니다. wandb.init() 메서드의 매개변수로 프로젝트 이름을 전달하세요. 프로젝트 이름을 전달하지 않으면 W&B는 기본 프로젝트 이름을 사용합니다.
wandb.Run.config 객체에서 하이퍼파라미터 값을 가져옵니다. 이렇게 하면 하드코딩된 값 대신 스윕 설정 사전에서 정의한 하이퍼파라미터 값을 사용할 수 있습니다.
wandb.Run.log()을 사용하여 최적화에 사용할 메트릭을 W&B에 로깅하세요. 설정에서 정의한 메트릭은 반드시 로깅해야 합니다. 예를 들어, 최적화할 메트릭을 val_acc로 정의했다면 val_acc를 로깅해야 합니다. 메트릭을 로깅하지 않으면 W&B는 무엇을 최적화해야 하는지 알 수 없습니다. 설정 사전(이 예시에서는 sweep_configuration)에서 스윕이 val_acc 값을 최대화하도록 정의합니다.
wandb.agent()를 사용하여 스윕을 시작합니다. 스윕 ID와 스윕이 실행할 함수 이름(function=main)을 지정하고, 시도할 run의 최대 개수를 4개로 설정합니다(count=4).
이 모든 내용을 종합하면, 스크립트는 다음과 같은 형태가 됩니다:import wandb # W&B Python SDK 가져오기
import numpy as np
import random
import argparse
def train_one_epoch(epoch, lr, batch_size):
acc = 0.25 + ((epoch / 30) + (random.random() / 10))
loss = 0.2 + (1 - ((epoch - 1) / 10 + random.random() / 5))
return acc, loss
def evaluate_one_epoch(epoch):
acc = 0.1 + ((epoch / 20) + (random.random() / 10))
loss = 0.25 + (1 - ((epoch - 1) / 10 + random.random() / 6))
return acc, loss
def main(args=None):
# 스윕 에이전트에 의해 호출될 때 args는 None이므로,
# 스윕 설정의 프로젝트를 사용합니다
project = args.project if args else None
with wandb.init(project=project) as run:
# `wandb.Run.config` 객체에서 하이퍼파라미터 값을 가져옵니다
lr = run.config["lr"]
batch_size = run.config["batch_size"]
epochs = run.config["epochs"]
# 트레이닝 루프를 실행하고 성능 값을 W&B에 기록합니다
for epoch in np.arange(1, epochs):
train_acc, train_loss = train_one_epoch(epoch, lr, batch_size)
val_acc, val_loss = evaluate_one_epoch(epoch)
run.log(
{
"epoch": epoch,
"train_acc": train_acc,
"train_loss": train_loss,
"val_acc": val_acc, # 최적화할 메트릭
"val_loss": val_loss,
}
)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--project", type=str, default="sweep-example", help="W&B 프로젝트 이름")
args = parser.parse_args()
# 스윕 설정 사전 정의
sweep_configuration = {
"method": "random",
"name": "sweep",
# 최적화할 메트릭
# 예를 들어, 검증 정확도를 최대화하려면
# "goal": "maximize"로 설정하고 최적화할 변수의
# 이름을 지정합니다. 이 경우 "val_acc"
"metric": {
"goal": "maximize",
"name": "val_acc"
},
"parameters": {
"batch_size": {"values": [16, 32, 64]},
"epochs": {"values": [5, 10, 15]},
"lr": {"max": 0.1, "min": 0.0001},
},
}
# 설정 사전을 전달하여 스윕을 초기화합니다
sweep_id = wandb.sweep(sweep=sweep_configuration, project=args.project)
# 스윕 작업을 시작합니다
wandb.agent(sweep_id, function=main, count=4)
스윕에서 W&B로 메트릭 로깅하기스윕 설정과 wandb.Run.log() 모두에 최적화 대상으로 정의한 메트릭을 로깅해야 합니다. 예를 들어 스윕 설정에서 최적화할 메트릭을 val_acc로 정의했다면, val_acc를 W&B에도 로깅해야 합니다. 메트릭을 로깅하지 않으면 W&B는 무엇을 최적화해야 하는지 알 수 없습니다.with wandb.init() as run:
val_loss, val_acc = train()
run.log(
{
"val_loss": val_loss,
"val_acc": val_acc
}
)
아래는 W&B에 메트릭을 잘못 로깅한 예시입니다. 스윕 설정에서 최적화하는 메트릭은 val_acc이지만, 코드에서는 val_acc를 키 validation 아래에 있는 중첩된 사전 안에 로깅하고 있습니다. 메트릭은 중첩된 사전 안이 아니라, 반드시 최상위 수준에서 직접 로깅해야 합니다.with wandb.init() as run:
val_loss, val_acc = train()
run.log(
{
"validation": {
"val_loss": val_loss,
"val_acc": val_acc
}
}
)