메인 콘텐츠로 건너뛰기
Try in Colab 이 가이드에서는 PyTorch와 MNIST 데이터를 사용하여 training 과정 동안 모델의 prediction을 추적, 시각화 및 비교하는 방법을 다룹니다. 다음 내용을 배우게 됩니다:
  1. 모델 training 또는 평가 중에 wandb.Table()에 metrics, 이미지, 텍스트 등을 log 하는 방법
  2. 이러한 테이블을 확인, 정렬, 필터링, 그룹화, 조인, 대화형 쿼리 및 탐색하는 방법
  3. 특정 이미지, 하이퍼파라미터/모델 버전 또는 타임스텝에 따라 동적으로 모델 prediction 또는 결과를 비교하는 방법

Examples

특정 이미지에 대한 예측 점수 비교

실시간 예시: training 1 에포크 vs 5 에포크 후의 예측 비교 →
Training epoch comparison
히스토그램은 두 모델 간의 클래스별 점수를 비교합니다. 각 히스토그램의 상단 녹색 바는 1 에포크만 트레이닝한 “CNN-2, 1 epoch” (id 0) 모델을 나타냅니다. 하단 보라색 바는 5 에포크 동안 트레이닝한 “CNN-2, 5 epochs” (id 1) 모델을 나타냅니다. 이미지는 두 모델의 의견이 일치하지 않는 경우로 필터링되었습니다. 예를 들어, 첫 번째 행에서 “4”는 1 에포크 후에는 모든 가능한 숫자에 대해 높은 점수를 얻었지만, 5 에포크 후에는 정답 레이블에 가장 높은 점수를 얻고 나머지는 매우 낮은 점수를 얻었습니다.

시간에 따른 주요 오류에 집중

실시간 예시 → 전체 테스트 데이터에서 잘못된 예측(“guess” != “truth”로 필터링)을 확인하세요. 1 에포크 training 후에는 229개의 잘못된 추측이 있었지만, 5 에포크 후에는 98개로 줄어든 것을 확인할 수 있습니다.
Side-by-side epoch comparison

모델 성능 비교 및 패턴 찾기

실시간 예시에서 자세한 내용 보기 → 정답을 필터링한 다음, 추측(guess)별로 그룹화하여 오분류된 이미지의 예시와 실제 레이블의 기본 분포를 두 모델을 나란히 놓고 비교해 보세요. 왼쪽은 레이어 크기와 학습률이 2배인 모델 변형이고, 오른쪽은 베이스라인입니다. 베이스라인이 각 추측 클래스에 대해 약간 더 많은 실수를 하는 것을 알 수 있습니다.
Error comparison

가입 또는 로그인

브라우저에서 진행 중인 Experiments를 확인하고 상호작용하려면 W&B에 가입하거나 로그인하세요. 이 예시에서는 편리한 호스팅 환경인 Google Colab을 사용하지만, 어디서든 트레이닝 스크립트를 실행하고 W&B의 experiment tracking 툴로 metrics를 시각화할 수 있습니다.
!pip install wandb -qqq
계정에 로그인합니다.

import wandb
wandb.login()

WANDB_PROJECT = "mnist-viz"

0. Setup

PyTorch를 사용하여 의존성을 설치하고, MNIST를 다운로드하고, training 및 테스트 데이터셋을 생성합니다.
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as T 
import torch.nn.functional as F


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

# 트레이닝 데이터로더 생성
def get_dataloader(is_train, batch_size, slice=5):
    "훈련 데이터로더를 가져옵니다"
    ds = torchvision.datasets.MNIST(root=".", train=is_train, transform=T.ToTensor(), download=True)
    loader = torch.utils.data.DataLoader(dataset=ds, 
                                         batch_size=batch_size, 
                                         shuffle=True if is_train else False, 
                                         pin_memory=True, num_workers=2)
    return loader

1. 모델 및 training 일정 정의

  • 실행할 에포크 수를 설정합니다. 각 에포크는 training 단계와 검증(테스트) 단계로 구성됩니다. 선택적으로 테스트 단계당 log 할 데이터 양을 설정합니다. 여기서는 데모를 단순화하기 위해 시각화할 배치 수와 배치당 이미지 수를 낮게 설정했습니다.
  • 간단한 컨볼루션 신경망을 정의합니다 (pytorch-tutorial 코드 참조).
  • PyTorch를 사용하여 training 및 테스트 세트를 로드합니다.
# 실행할 에포크 수
# 각 에포크에는 트레이닝 단계와 테스트 단계가 포함되므로, 
# 이 값은 로그를 남길 테스트 예측 테이블의 수를 결정합니다.
EPOCHS = 1

# 각 테스트 단계에서 테스트 데이터로부터 로그를 남길 배치 수
# (데모 단순화를 위해 기본값을 낮게 설정함)
NUM_BATCHES_TO_LOG = 10 #79

# 테스트 배치당 로그를 남길 이미지 수
# (데모 단순화를 위해 기본값을 낮게 설정함)
NUM_IMAGES_PER_BATCH = 32 #128

# 트레이닝 설정 및 하이퍼파라미터
NUM_CLASSES = 10
BATCH_SIZE = 32
LEARNING_RATE = 0.001
L1_SIZE = 32
L2_SIZE = 64
# 이 값을 변경하면 인접한 레이어의 형태를 변경해야 할 수도 있습니다
CONV_KERNEL_SIZE = 5

# 2층 컨볼루션 신경망 정의
class ConvNet(nn.Module):
    def __init__(self, num_classes=10):
        super(ConvNet, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(1, L1_SIZE, CONV_KERNEL_SIZE, stride=1, padding=2),
            nn.BatchNorm2d(L1_SIZE),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.layer2 = nn.Sequential(
            nn.Conv2d(L1_SIZE, L2_SIZE, CONV_KERNEL_SIZE, stride=1, padding=2),
            nn.BatchNorm2d(L2_SIZE),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.fc = nn.Linear(7*7*L2_SIZE, NUM_CLASSES)
        self.softmax = nn.Softmax(NUM_CLASSES)

    def forward(self, x):
        # 특정 레이어의 형태를 확인하려면 주석을 해제하세요:
        #print("x: ", x.size())
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.reshape(out.size(0), -1)
        out = self.fc(out)
        return out

train_loader = get_dataloader(is_train=True, batch_size=BATCH_SIZE)
test_loader = get_dataloader(is_train=False, batch_size=2*BATCH_SIZE)

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

2. training 실행 및 테스트 prediction 로그 기록

매 에포크마다 training 단계와 테스트 단계를 실행합니다. 각 테스트 단계에서 테스트 prediction을 저장할 wandb.Table()을 생성합니다. 이는 브라우저에서 시각화하고, 동적으로 쿼리하고, 나란히 비교할 수 있습니다.
# 테스트 이미지 배치에 대한 예측을 로그로 남기는 편의 함수
def log_test_predictions(images, labels, outputs, predicted, test_table, log_counter):
  # 모든 클래스에 대한 신뢰도 점수 획득
  scores = F.softmax(outputs.data, dim=1)
  log_scores = scores.cpu().numpy()
  log_images = images.cpu().numpy()
  log_labels = labels.cpu().numpy()
  log_preds = predicted.cpu().numpy()
  # 이미지 순서에 기반하여 ID 추가
  _id = 0
  for i, l, p, s in zip(log_images, log_labels, log_preds, log_scores):
    # 데이터 테이블에 필요한 정보 추가:
    # id, 이미지 픽셀, 모델의 추측, 실제 레이블, 모든 클래스에 대한 점수
    img_id = str(_id) + "_" + str(log_counter)
    test_table.add_data(img_id, wandb.Image(i), p, l, *s)
    _id += 1
    if _id == NUM_IMAGES_PER_BATCH:
      break

# W&B: 이 모델의 트레이닝을 추적하기 위해 새로운 run을 초기화합니다
with wandb.init(project="table-quickstart") as run:

    # W&B: config를 사용하여 하이퍼파라미터 로그 기록
    cfg = run.config
    cfg.update({"epochs" : EPOCHS, "batch_size": BATCH_SIZE, "lr" : LEARNING_RATE,
                "l1_size" : L1_SIZE, "l2_size": L2_SIZE,
                "conv_kernel" : CONV_KERNEL_SIZE,
                "img_count" : min(10000, NUM_IMAGES_PER_BATCH*NUM_BATCHES_TO_LOG)})

    # 모델, 손실 함수, 옵티마이저 정의
    model = ConvNet(NUM_CLASSES).to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)

    # 모델 트레이닝
    total_step = len(train_loader)
    for epoch in range(EPOCHS):
        # 트레이닝 단계
        for i, (images, labels) in enumerate(train_loader):
            images = images.to(device)
            labels = labels.to(device)
            # forward 패스
            outputs = model(images)
            loss = criterion(outputs, labels)
            # 역전파 및 최적화
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
    
            # W&B: 트레이닝 단계별 손실 로그 기록, UI에서 실시간으로 시각화됨
            run.log({"loss" : loss})
            if (i+1) % 100 == 0:
                print ('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'
                    .format(epoch+1, EPOCHS, i+1, total_step, loss.item()))
                

        # W&B: 각 테스트 단계의 예측을 저장할 Table 생성
        columns=["id", "image", "guess", "truth"]
        for digit in range(10):
            columns.append("score_" + str(digit))
        test_table = wandb.Table(columns=columns)

        # 모델 테스트
        model.eval()
        log_counter = 0
        with torch.no_grad():
            correct = 0
            total = 0
            for images, labels in test_loader:
                images = images.to(device)
                labels = labels.to(device)
                outputs = model(images)
                _, predicted = torch.max(outputs.data, 1)
                if log_counter < NUM_BATCHES_TO_LOG:
                    log_test_predictions(images, labels, outputs, predicted, test_table, log_counter)
                    log_counter += 1
                total += labels.size(0)
                correct += (predicted == labels).sum().item()

            acc = 100 * correct / total
            # W&B: UI에서 시각화하기 위해 트레이닝 에포크별 정확도 로그 기록
            run.log({"epoch" : epoch, "acc" : acc})
            print('Test Accuracy of the model on the 10000 test images: {} %'.format(acc))

        # W&B: wandb에 예측 테이블 로그 기록
        run.log({"test_predictions" : test_table})

다음 단계는?

다음 튜토리얼에서는 W&B Sweeps를 사용하여 하이퍼파라미터를 최적화하는 방법을 배우게 됩니다.