> ## Documentation Index
> Fetch the complete documentation index at: https://wb-21fd5541-serverless-sft-revamp.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tables로 예측값 시각화하기

export const ColabLink = ({url}) => <a href={url} target="_blank" rel="noopener noreferrer" className="colab-link">
    <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
      <path d="M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01.21.03zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z" />
    </svg>
    Try in Colab
  </a>;

<ColabLink url="https://colab.research.google.com/github/wandb/examples/blob/master/colabs/datasets-predictions/W&B_Tables_Quickstart.ipynb" />

이 가이드에서는 PyTorch와 MNIST 데이터를 사용하여 training 과정 동안 모델의 prediction을 추적, 시각화 및 비교하는 방법을 다룹니다.

다음 내용을 배우게 됩니다:

1. 모델 training 또는 평가 중에 `wandb.Table()`에 metrics, 이미지, 텍스트 등을 log 하는 방법
2. 이러한 테이블을 확인, 정렬, 필터링, 그룹화, 조인, 대화형 쿼리 및 탐색하는 방법
3. 특정 이미지, 하이퍼파라미터/모델 버전 또는 타임스텝에 따라 동적으로 모델 prediction 또는 결과를 비교하는 방법

## Examples

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

[실시간 예시: training 1 에포크 vs 5 에포크 후의 예측 비교 →](https://wandb.ai/stacey/table-quickstart/reports/CNN-2-Progress-over-Training-Time--Vmlldzo3NDY5ODU#compare-predictions-after-1-vs-5-epochs)

<Frame>
  <img src="https://mintcdn.com/wb-21fd5541-serverless-sft-revamp/nnIqJ01CGwOGYvb1/images/tutorials/tables-1.png?fit=max&auto=format&n=nnIqJ01CGwOGYvb1&q=85&s=38b8a016fe3126b9b4fc519ad2cf4322" alt="Training epoch comparison" width="2204" height="1230" data-path="images/tutorials/tables-1.png" />
</Frame>

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

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

[실시간 예시 →](https://wandb.ai/stacey/table-quickstart/reports/CNN-2-Progress-over-Training-Time--Vmlldzo3NDY5ODU#top-errors-over-time)

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

<Frame>
  <img src="https://mintcdn.com/wb-21fd5541-serverless-sft-revamp/nnIqJ01CGwOGYvb1/images/tutorials/tables-2.png?fit=max&auto=format&n=nnIqJ01CGwOGYvb1&q=85&s=21cae3fdcbdfb49e830627e509f2daf8" alt="Side-by-side epoch comparison" width="2014" height="1320" data-path="images/tutorials/tables-2.png" />
</Frame>

### 모델 성능 비교 및 패턴 찾기

[실시간 예시에서 자세한 내용 보기 →](https://wandb.ai/stacey/table-quickstart/reports/CNN-2-Progress-over-Training-Time--Vmlldzo3NDY5ODU#false-positives-grouped-by-guess)

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

<Frame>
  <img src="https://mintcdn.com/wb-21fd5541-serverless-sft-revamp/nnIqJ01CGwOGYvb1/images/tutorials/tables-3.png?fit=max&auto=format&n=nnIqJ01CGwOGYvb1&q=85&s=f31f8386154d591401a38f2f3180c203" alt="Error comparison" width="2778" height="1290" data-path="images/tutorials/tables-3.png" />
</Frame>

## 가입 또는 로그인

브라우저에서 진행 중인 Experiments를 확인하고 상호작용하려면 W\&B에 [가입하거나 로그인](https://wandb.ai/login)하세요.

이 예시에서는 편리한 호스팅 환경인 Google Colab을 사용하지만, 어디서든 트레이닝 스크립트를 실행하고 W\&B의 experiment tracking 툴로 metrics를 시각화할 수 있습니다.

```python theme={null}
!pip install wandb -qqq
```

계정에 로그인합니다.

```python theme={null}

import wandb
wandb.login()

WANDB_PROJECT = "mnist-viz"
```

## 0. Setup

PyTorch를 사용하여 의존성을 설치하고, MNIST를 다운로드하고, training 및 테스트 데이터셋을 생성합니다.

```python theme={null}
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](https://github.com/yunjey/pytorch-tutorial) 코드 참조).
* PyTorch를 사용하여 training 및 테스트 세트를 로드합니다.

```python theme={null}
# 실행할 에포크 수
# 각 에포크에는 트레이닝 단계와 테스트 단계가 포함되므로, 
# 이 값은 로그를 남길 테스트 예측 테이블의 수를 결정합니다.
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()`을 생성합니다. 이는 브라우저에서 시각화하고, 동적으로 쿼리하고, 나란히 비교할 수 있습니다.

```python theme={null}
# 테스트 이미지 배치에 대한 예측을 로그로 남기는 편의 함수
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를 사용하여 하이퍼파라미터를 최적화하는 방법](/models/tutorials/sweeps/)을 배우게 됩니다.
