Weights & Biases란?
wandb는 MLOps 플랫폼으로 머신러닝, 딥러닝을 학습하는데 필요한 다양한 기능들을 제공한다. 대표적으로 아래의 기능등을 갖추고 있다.
- 실험관리
- 하이퍼파라미터 튜닝
- 데이터, 모델 버저닝
- 모델 관리
- 데이터 시각화
- 협업 리포트
wandb 기초 사용법
pytorch를 사용하여 학습하는 환경에서 wandb를 이용해서 필요한 metric의 log를 기록하고, 그래프 형태로 확인하는 것을 먼저 알아본다.
코랩 샘플 링크를 통해서 pytorch에서 사용하는 기본적인 wandb 사용법을 익힐 수 있다.
W&B 설치
- wandb.ai 에 접속하여 회원가입하면 API key를 발급받을 수 있다.
- pip을 통해 wandb를 설치하고 위에서 발급받은 API key로 로그인을 한다.
pip3 install wandb
wandb login # API key 입력
pytorch에서 W&B 사용하기
- 아래 코드를 통해 프로젝트를 wandb에 등록해준다.
- 기존에 같은 이름으로 등록된 project가 없으면 wandb에서 새로 생성하고, 기존에 등록된 project가 있으면 같은 project끼리 묶어 실험 결과를 비교할 수 있다.
import wandb
wandb.init(project='project-name')
- config 설정
- metric으로 log를 기록하거나, 추후에 파라미터 튜닝에 필요한 변수들을 wandb.config에 넣어준다.
- wandb.config에 담긴 파라미터 값들은 wandb 웹에서 모두 확인할 수 있다.
# init config setting
wandb.config = {
"learning_rate": 0.001,
"epochs": 5,
"batch_size": 128
}
- 학습 중 metric 기록하기
- wandb.log 함수를 통해 metric 값들을 logging할 수 있다.
- log 함수의 파라미터는 항상 dictionary 형태로 들어가야 한다.
wandb.log({"loss": loss,
"acc": acc})
- alert 알람 보내기(wandb 설정에서 슬랙이나 이메일 체크하여 사용)
wandb.alert(title='제목', text='내용')
기타 상세 사용방법
wandb.init 관련 사용법
- run name 알거나 지정하고 싶을때
- 직접 지정하면 sequence 번호가 적히지 않음(like snowy-owl-10→snowy-owl)
import wandb
wandb.init()
# get run name
run_name = wandb.run.name
# set run name
wandb.run.name = '원하는 이름'
wandb.run.save()
- 실험단위로 간단한 메모
- notes, tags를 통해서 하면 됨
- tags는 리스트 형태로 넣어 줌
wandb.init(
project="mnist",
notes="baseline",
tags=["custom loss", "custom optimizer"],
)
wandb.config 관련 사용법
- config 지정하는 다양한 방법
# init set
wandb.config = {'lr':1e-3}
# later set
wandb.config.update({"drop_rate":0.5})
# argparse를 사용할 때
args = parser.parse_args()
wandb.config.update(args)
- unique identifier를 추가해서 config로 실험을 tracking 하는 방법이 있다.
wandb.config.update({'dataset': 'ver1'})
- 이미 업로드한 config 파일을 추후에 수정하여 업데이트 할 수 있다.
import wandb
api = wandb.Api()
run = api.run("username/project/run_id")
run.config["foo"] = 32
run.update()
wandb.log 상세 사용법
- wandb.log(dict) 형태로 어떤 metric, media, custom object 등 다양한 값들을 log할 수 있다.
wandb.log({"loss": 0.314, "epoch": 5,
"inputs": wandb.Image(inputs),
"logits": wandb.Histogram(ouputs),
"captions": wandb.Html(captions)})
- 기본적으로 wandb 차트의 x축은 자동적으로 step을 지정한다. epoch, step과 같이 custom 값으로 x축을 지정할 수도 있고, step 파라미터를 넣어서 x축 값을 넣어줄 수 있다.
- (추천) step 파라미터를 넣어서 현재의 step(x축)을 알려줌
wandb.log({'train_loss':train_loss}, step=epoch)
- epoch을 따로 log함 (wandb UI 차트에서 따로 x축 지정을 해줘야함)
wandb.log({'train_loss':train_loss, 'epoch':epoch})
- best value 값을 summary에 지정하지 않으면 summary에는 마지막값이 입력된다. 따라서 실험간 metric을 비교하고 싶으면 best 값을 summary에 입력해 준다.
wandb.run.summary['best_accuracy'] = best_accuracy
- summary를 customize 하여 자동으로 원하는 operation에 따라 summary값을 지정해준다.
- 지원 summary 방법: min, max, mean, best, last, none
import wandb
wandb.init()
wandb.define_metric("loss", summary="min")
wandb.define_metric("acc", summary="max")
for i in range(10):
log_dict = {
"loss": random.uniform(0,1/(i+1)),
"acc": random.uniform(1/(i+1),1),
}
wandb.log(log_dict)
실제 적용 예시
- 아래 코드는 pytorch에서 제공하는 fashion mnist 학습하는 코드에 wandb를 추가한 코드이다.
Fishion MNIST Training Code with WandB
import torch from torch import nn from torch.utils.data import DataLoader from torchvision import datasets from torchvision.transforms import ToTensor import wandb # init wandb settings run = wandb.init(project="f-mnist") cfg = { "learning_rate": 1e-3, "epochs": 5, "batch_size": 64 } wandb.config.update(cfg) # Download training data from open datasets. training_data = datasets.FashionMNIST( root="data", train=True, download=True, transform=ToTensor(), ) # Download test data from open datasets. test_data = datasets.FashionMNIST( root="data", train=False, download=True, transform=ToTensor(), ) # Create data loaders. train_dataloader = DataLoader(training_data, batch_size=cfg['batch_size']) test_dataloader = DataLoader(test_data, batch_size=cfg['batch_size']) for X, y in test_dataloader: print(f"Shape of X [N, C, H, W]: {X.shape}") print(f"Shape of y: {y.shape} {y.dtype}") break # Get cpu or gpu device for training. device = "cuda" if torch.cuda.is_available() else "cpu" print(f"Using {device} device") # Define model class NeuralNetwork(nn.Module): def __init__(self): super(NeuralNetwork, self).__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28 * 28, 512), nn.ReLU(), nn.Linear(512, 512), nn.ReLU(), nn.Linear(512, 10) ) def forward(self, x): x = self.flatten(x) logits = self.linear_relu_stack(x) return logits model = NeuralNetwork().to(device) print(model) loss_fn = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(model.parameters(), lr=cfg['learning_rate']) def train(dataloader, model, loss_fn, optimizer, epoch): size = len(dataloader.dataset) model.train() total_loss = 0 for batch, (X, y) in enumerate(dataloader): X, y = X.to(device), y.to(device) # Compute prediction error pred = model(X) loss = loss_fn(pred, y) total_loss += loss.item() # Backpropagation optimizer.zero_grad() loss.backward() optimizer.step() if batch % 100 == 0: loss, current = loss.item(), batch * len(X) print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]") wandb.log({"train_loss": total_loss / len(dataloader)}, step=epoch) def test(dataloader, model, loss_fn, epoch): size = len(dataloader.dataset) num_batches = len(dataloader) model.eval() test_loss, correct = 0, 0 with torch.no_grad(): for X, y in dataloader: X, y = X.to(device), y.to(device) pred = model(X) test_loss += loss_fn(pred, y).item() correct += (pred.argmax(1) == y).type(torch.float).sum().item() test_loss /= num_batches correct /= size print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n") wandb.log({"test_loss": test_loss, "test_acc": correct}, step=epoch) epochs = cfg['epochs'] for t in range(epochs): print(f"Epoch {t+1}\n-------------------------------") train(train_dataloader, model, loss_fn, optimizer, t) test(test_dataloader, model, loss_fn, t) print("Done!") torch.save(model.state_dict(), "model.pth") print("Saved PyTorch Model State to model.pth") model = NeuralNetwork() model.load_state_dict(torch.load("model.pth")) classes = [ "T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt", "Sneaker", "Bag", "Ankle boot", ] model.eval() x, y = test_data[0][0], test_data[0][1] with torch.no_grad(): pred = model(x) predicted, actual = classes[pred[0].argmax(0)], classes[y] print(f'Predicted: "{predicted}", Actual: "{actual}"')
Uploaded by N2T