네이버 부스트캠프 🔗/⭐주간 학습 정리

[네이버 부스트 캠프 AI Tech] Wandb 사용설명서

Dobby98 2023. 3. 29. 01:45

이번 글은 네이버 부스트 캠프 내용 중

추가로 공부가 필요하다고 여겨서 공부를 하고 작성한 글입니다

 

✅ Week 4

목차

    1. What is wandb?
    2. How to use wandb

1. What is wandb? 

요즘 MLops라는 말이 심심치 않게 들린다

 

데이터 수집 - 전처리 - 모델 가공 - 실험 - 또는 서비스 배포 등 

위의 일련의 과정을 관리할 수 있는 하나의 시스템을 MLops라고 하는데

 

실제로 딥러닝 모델을 개발하거나 연구를 할 때도 MLops를 이용할 수 있다

 

그중에서 특히 시각화 부분이나 자신이 학습한 모델을 비교하고 하이퍼 파라미터를 쉽게 튜닝할 수 있게 해준다

 

 

그러한 MLops의 한 종류로 이번에 잘펴 볼 것은 wandb이다

풀네임 Weight & bias로 딥러닝 모델의 학습 과정을 쉽게 관리할 수 있게해준다

 

코드도 간단하게 몇줄 만 추가해주면 아래의 모델 결과를 쉽게 기록하고 관리할 수 있으며

모델의 하이퍼 파라미터도 튜닝할 수 있다

 

위의 그래프 처럼 여러 모델의 학습과정도 기록해서

쉽게 비교해볼 수 있다

 

제일큰 장점은 개인적인 실험의 경우 가격이 무료이며 무제한이다

그리고 100GB의 저장공간도 제공해준다 !! 

 

 

그럼 이제부터 wandb를 사용하는 간단한 방법에 대해서 알아보자


2. How to use wandb

1. wandb 설치 및 로그인

 

우선 로컬 환경에서 모델을 학습한다고 가정을 해보자 - 코랩도 사실 상관이 없다

그럼 우선 wandb 홈페이지에 들어가서 가입을 해주어야한다

 

가입을 하게되면 API 키를 제공해 주는데 이 key를 이용해서 wandb에 로그인 할 수 있다

 

pip install wandb

우선 로컬 환경에 wandb를 설치 해주어야한다

다른 패키지와 마찬가지고 자신의 환경에 맞춰서 설치해주면 된다

 

그리고 코드 내에서 api를 입력해주면 자신의 계정과 코드가 연결이 된다

import wandb

wandb.login( 'API Key ')

API키를 직접 login 파라미터로 제공해도 되고

위의 사진처럼 입력해주어도 된다

물론 콘솔창에 wandb login 'API key'를 입력해주어도 연동된다

 


2. wandb에 프로젝트 생성 및 기본 설정

이제 본격적으로 wandb를 이용해 보자

 

우선 wandb를 이용하려면 project를 만들어 주어야한다

물론 wandb 홈페이지에서 project를 생성하여도 되지만

 

코드 상에서도 생성이 가능하다

import wandb
wandb.init(project='project-name')

이런식으로 wandb.init 함수에 파라미터로 project 이름을 넘겨주면 생성된다

 

 

그리고 모델에 학습할 기본 정보를 dic로 config라는 변수 명에 지정을 해두고 - 다른 변수명도 상관없다

config={
    "learning_rate": 0.02,
    "architecture": "eiificeintnet-adam",
    "dataset": "CIFAR10",
    "epochs": 10,
    "batch_size":32,
}

wandb.config.update(config)

wandb.config.update ( '변수명' )을 해주면 우리의 프로젝트 config가 업데이트 된다

 

 


3. 간단한 loss, acc 기록하기

 

이번에는 간단하게 wandb를 이용해서 모델의 학습과정 중 만들어지는 loss와 acc를 기록하는 방법을 살펴보자

사실 간단하다 

 

일반적인 logging 처럼 wandb를 이용하면 된다

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)

위 코드에서 집중해서 살펴볼 부분은 

   wandb.log({"train_loss": total_loss / len(dataloader)}, step=epoch)

이 부분으로 간단하게 wandb.log를 이용해서 우리가 기록할 데이터를 입력해주면된다

 

그럼 test loss, acc는 어떻게 기록할까?

답을 보기전에 한번 직접 구성해 보기 바란다

 

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)

이런식으로 wandb.log를 활용하면된다

 

 

지금까지 기본적인 wandb 사용법을 살펴보았다 

wandb를 활용한 전체 학습 예시 코드는 아래에 기록해 두었다

import torch
from torch import nn

from torchvision.models import efficientnet_v2_l
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

import wandb
import random
from tqdm import tqdm


#로그인한 상황 가정
wandb.init(project="eiificeintnet-cifar10")

config={
    "learning_rate": 0.02,
    "architecture": "eiificeintnet-adam",
    "dataset": "CIFAR10",
    "epochs": 10,
    "batch_size":32,
}

wandb.config.update(config)

training_data = datasets.CIFAR10(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)

test_data = datasets.CIFAR10(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)


train_dataloader = DataLoader(training_data, batch_size=config['batch_size'])
test_dataloader = DataLoader(test_data, batch_size=config['batch_size'])

device = "cuda" if torch.cuda.is_available() else "cpu"

model = efficientnet_v2_l(pretrained=True)
model.fc = nn.Linear(512, 10) #output_size setting
model.to(device)


#train
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)

#val
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)


loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=config['learning_rate'])
epochs = config['epochs']

for t in tqdm(range(epochs)):
        train(train_dataloader, model, loss_fn, optimizer, t)
        test(test_dataloader, model, loss_fn, t)



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}"')

data는 cifar10 데이터를 활용해서 모델을 학습하는 과정이다

 

 

모델을 바꿔가면서 기록을 하고 싶다면 모델을 바꿔주면서 학습을 하면된다

그리고 하이퍼 파라미터도 쉽게 변경이 가능하고

조건문을 적용해서 튜닝도 쉽게 가능하다

 


4. 학습한 모델 비교 및 시각화

 

위에서 보았던 사진을 통해서 모델을 쉽게 비교할 수 있다

위의 사진은 3개의 모델을 학습한 이후 실제로 비교한 그래프로

위의 과정을 실제로 이용하였다

한눈에 모델을 비교할 수 도 있고 

어느 모델이 어느 지점에서 더 좋은지를 파악하기 쉽다

 

또한 모델 학습과정에서 사용되는 GPU의 자원도 실시간으로 추적이 되기 때문에

하드웨어를 관리하기도 편리하다

특히 OOM 사태를 관리하기에 너무 좋았다

 

 

그리고 진짜 좋아보였던 점은 협업이 가능하다는 것이다

팀 프로젝트를 구성해서 각자 학습한 모델을 기록해서

비교한다면 협업의 속도와 능률이 증가할 것이다


 

5. Loss function 관리

 

또다른 장점은 loss function을 관리할 수 있다

wandb.define_metric('train_loss', summary='min')
wandb.define_metric('val_loss', summary='min')

wandb.define_metric('train_f1', summary='max')
wandb.define_metric('val_f1', summary='max')

이런식으로 평가 metric을 지정해주면

위에서 본 그래프에 기록이 되어서 추적하기도 편리하다

 

 

+)그리고 추가적으로 좋았던 점은 실시간 학습과정에서 유저에게 오류가 발생하면

오류내용을 깔끔하게 알림으로 전달해준다는 것이었다

 

그래서 학습과정에서 오류를 처리할 때도 상당히 편리했다

 

 

진짜 강점은 아무래도 시각화에 있을 것이다

파이썬 패키지를 이용해서 직접 코드를 작성하지 않아도 wandb를 이용하면

쉽게 시각화를 할 수 있었다

강력한 시각화

 

 

Quickstart | Weights & Biases Documentation

W&B Quickstart.

docs.wandb.ai

앞으로 모델 학습과정에서 위의 docs를 참고해서 한번적용해 보고 

좋은점과 추가로 공부할 점, 그리고 단점을 정리해서 

추가글로 작성해보겠다

 

관심있는 분들이시면 한번 사용해보는것을 적극 추천하다