이미지 분류(Image Classification)

분류기(classifier)는 픽셀(pixel)에서 클래스 확률 분포(class probability distribution)로 가는 함수입니다. 나머지는 모두 배관(plumbing) 작업입니다.

유형: Build 언어: Python 선수 학습: Phase 2 Lesson 09 Model Evaluation, Phase 3 Lesson 10 Mini Framework, Phase 4 Lesson 03 CNNs 소요 시간: 약 75분

학습 목표

  • CIFAR-10 스타일 이미지 분류 파이프라인(pipeline)을 처음부터 끝까지(end-to-end) 구축합니다. 데이터셋(dataset), 데이터 증강(augmentation), 모델(model), 학습 루프(training loop), 평가(evaluation)를 모두 포함합니다.
  • 데이터 로더(dataloader), 손실(loss), 옵티마이저(optimizer), 스케줄러(scheduler), 데이터 증강 각각의 역할을 설명하고, 어느 한 요소가 깨졌을 때 손실 곡선(loss curve)에 어떻게 나타나는지 예측합니다.
  • 믹스업(mixup), 컷아웃(cutout), 라벨 스무딩(label smoothing)을 직접 구현하고 각 기법을 언제 추가할 가치가 있는지 설명합니다.
  • 혼동 행렬(confusion matrix)과 클래스별 정밀도/재현율(per-class precision/recall) 표를 읽고, 전체 정확도(aggregate accuracy) 너머에 있는 데이터셋과 모델의 실패 원인을 진단합니다.

문제

배포되는 거의 모든 비전 작업(vision task)은 어느 수준에서는 이미지 분류로 환원됩니다. 객체 검출(detection)은 영역(region)을 분류하고, 세그멘테이션(segmentation)은 픽셀을 분류하며, 검색(retrieval)은 클래스 중심(class centroid)과의 유사도(similarity)로 순위를 매깁니다. 분류의 데이터셋 루프, 증강 정책(augmentation policy), 손실, 평가를 제대로 만드는 능력은 Phase 4의 모든 과제로 이어집니다.

대부분의 분류 버그(bug)는 모델 내부가 아니라 파이프라인에 있습니다. 잘못된 정규화(normalization), 셔플(shuffle)되지 않은 학습 세트(training set), 라벨을 왜곡하는 증강, 학습 데이터가 섞인 검증 분할(validation split), 에포크 30 이후 조용히 발산(diverge)하는 학습률(learning rate) 같은 것들입니다. 올바른 설정이라면 CIFAR-10에서 93%를 찍을 수 있는 CNN이 깨진 설정에서는 70~75%에 머물 수 있고, 그동안 손실 곡선은 그럴듯해 보일 수 있습니다.

이 강의는 파이프라인 전체를 직접 손으로 연결합니다. torchvision.datasets가 버그를 숨기지 않도록, 모든 부분을 들여다볼 수 있는(inspectable) 구성으로 시작합니다.

사전 테스트

2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요

1.모델이 (N, C) 형태(shape)의 원시 로짓(raw logits)을 출력합니다. `loss = cross_entropy(softmax(logits), y)`라고 작성하면 무엇이 잘못됩니까?

2.10개 클래스 분류기(classifier)의 정확도가 92%입니다. 클래스 0은 9,000개 예시를 갖고, 클래스 1~9는 총 1,000개를 나눠 가집니다. 이 대표 수치(headline number)가 무엇을 숨길 수 있습니까?

0/2 답변 완료

개념

분류 파이프라인(Classification pipeline)

flowchart LR
    A["Dataset<br/>(images + labels)"] --> B["Augment<br/>(random transforms)"]
    B --> C["Normalise<br/>(mean/std)"]
    C --> D["DataLoader<br/>(batch + shuffle)"]
    D --> E["Model<br/>(CNN)"]
    E --> F["Logits<br/>(N, C)"]
    F --> G["Cross-entropy loss"]
    F --> H["Argmax<br/>at eval"]
    G --> I["Backward"]
    I --> J["Optimizer step"]
    J --> K["Scheduler step"]
    K --> E

    style A fill:#dbeafe,stroke:#2563eb
    style E fill:#fef3c7,stroke:#d97706
    style G fill:#fecaca,stroke:#dc2626
    style H fill:#dcfce7,stroke:#16a34a

이 루프의 모든 줄이 버그가 들어갈 수 있는 자리입니다. 교차 엔트로피(cross-entropy)는 소프트맥스(softmax) 출력이 아니라 원시 로짓(raw logits)을 받습니다. 믹스업을 제외하면 데이터 증강은 입력(input)에만 적용되고 라벨(label)에는 적용되지 않습니다. optimizer.zero_grad()는 스텝(step)마다 한 번, .backward() 전에 호출해야 합니다.

교차 엔트로피(Cross-entropy), 로짓(logits), 소프트맥스(softmax)

분류기는 이미지마다 C개의 숫자, 즉 로짓을 출력합니다. 소프트맥스는 이를 확률 분포로 바꿉니다.

softmax(z)_i = exp(z_i) / sum_j exp(z_j)
CE(z, y) = -log(softmax(z)_y)
         = -z_y + log(sum_j exp(z_j))

오른쪽 형태가 로그-합-지수(log-sum-exp)를 사용하는 수치적으로 안정적인(numerically stable) 형태입니다. PyTorch의 nn.CrossEntropyLoss는 소프트맥스와 음의 로그 우도(NLL; Negative Log Likelihood)를 하나의 안정적인 연산(op)으로 융합(fuse)하므로 원시 로짓을 직접 받아야 합니다. 소프트맥스를 먼저 적용하면 사실상 log(softmax(softmax(z)))를 계산하게 되어 거의 항상 버그입니다.

데이터 증강이 동작하는 이유

CNN은 가중치 공유(weight sharing) 덕분에 평행 이동(translation)에 대한 귀납 편향(inductive bias)을 갖지만, 잘라내기(crop), 뒤집기(flip), 색상 변형(color jitter), 가림(occlusion)에 대한 불변성(invariance)이 자동으로 생기지는 않습니다. 학습 중에 적용되는 무작위 변환(random transform)은 "이 두 이미지는 같은 라벨이다. 차이를 무시하는 특징(feature)을 학습하라"는 데이터 수준의 지시입니다.

데이터 증강은 라벨을 보존해야 합니다. 숫자 데이터셋(digit dataset)에서 회전(rotation)이 6을 9처럼 만들 수 있다면 작은 회전 범위를 쓰거나 다른 증강 기법을 선택해야 합니다.

Original crop:  "dog facing left"
Flip:           "dog facing right"       <- same label, different pixels
Rotate(+15):    "dog, slight tilt"
Colour jitter:  "dog in warmer light"
RandomErasing:  "dog with patch missing"

믹스업(Mixup)과 컷믹스(Cutmix)

일반 데이터 증강은 픽셀을 바꾸지만 원-핫(one-hot) 라벨은 그대로 둡니다. 믹스업(Mixup)컷믹스(Cutmix)는 입력과 라벨을 함께 섞습니다.

Mixup:
  lambda ~ Beta(a, a)
  x = lambda * x_i + (1 - lambda) * x_j
  y = lambda * y_i + (1 - lambda) * y_j

Cutmix:
  x_i 안에 x_j의 random rectangle을 붙임
  y = area-weighted mix of y_i and y_j

모델은 날카로운 원-핫 타깃(target)을 외우는 대신 클래스 사이를 부드럽게 보간(interpolate)하는 법을 배웁니다. 학습 손실(train loss)은 올라가지만 테스트 정확도(test accuracy)와 보정(calibration)이 좋아질 수 있습니다.

라벨 스무딩(Label smoothing)

라벨 스무딩은 [0, 0, 1, 0] 같은 타깃 대신 [eps/C, eps/C, 1-eps, eps/C]처럼 부드러운 타깃을 사용합니다. 작은 eps=0.1 정도로 모델이 지나치게 날카로운(sharp) 로짓을 만들지 않게 하며 보정 성능을 개선합니다. PyTorch에서는 nn.CrossEntropyLoss(label_smoothing=0.1)로 사용할 수 있습니다.

정확도 너머의 평가

전체 정확도는 데이터 불균형(imbalance)을 숨깁니다. 9:1 비율의 이진 분류기(binary classifier)가 항상 다수 클래스(majority class)만 예측해도 90% 정확도를 냅니다. 실제 진단에는 다음이 필요합니다.

  • 클래스별 정확도(Per-class accuracy): 클래스별 성능을 바로 드러냅니다.
  • 혼동 행렬(Confusion matrix): i행 j열은 실제 클래스(true class) i를 예측 클래스(predicted class) j로 예측한 횟수입니다.
  • Top-1 / Top-5: 정답 클래스가 상위 k개 예측 안에 들어가는지 봅니다.
  • 보정(Calibration; ECE): 0.8 신뢰도(confidence)의 예측이 실제로 80% 맞는지 확인합니다.

만들어 보기

Step 1: 결정론적 합성 데이터셋(deterministic synthetic dataset)

실제 CIFAR-10 대신 빠르고 재현 가능한(reproducible) 합성(synthetic) CIFAR 스타일 데이터셋을 만듭니다. 32x32 RGB 이미지에 클래스별 고유 색상 팔레트(color palette)와 주파수 패턴(frequency pattern)을 넣고 잡음(noise)을 추가합니다. 같은 파이프라인은 실제 CIFAR-10에도 그대로 적용됩니다.

import numpy as np
import torch
from torch.utils.data import Dataset


def synthetic_cifar(num_per_class=1000, num_classes=10, seed=0):
    rng = np.random.default_rng(seed)
    X = []
    Y = []
    for c in range(num_classes):
        centre = rng.uniform(0, 1, (3,))
        freq = 2 + c
        for _ in range(num_per_class):
            yy, xx = np.meshgrid(np.linspace(0, 1, 32), np.linspace(0, 1, 32), indexing="ij")
            r = np.sin(xx * freq) * 0.5 + centre[0]
            g = np.cos(yy * freq) * 0.5 + centre[1]
            b = (xx + yy) * 0.5 * centre[2]
            img = np.stack([r, g, b], axis=-1)
            img += rng.normal(0, 0.08, img.shape)
            img = np.clip(img, 0, 1)
            X.append(img.astype(np.float32))
            Y.append(c)
    X = np.stack(X)
    Y = np.array(Y)
    idx = rng.permutation(len(X))
    return X[idx], Y[idx]


class ArrayDataset(Dataset):
    def __init__(self, X, Y, transform=None):
        self.X = X
        self.Y = Y
        self.transform = transform

    def __len__(self):
        return len(self.X)

    def __getitem__(self, i):
        img = self.X[i]
        if self.transform is not None:
            img = self.transform(img)
        img = torch.from_numpy(img).permute(2, 0, 1)
        return img, int(self.Y[i])

ArrayDataset은 NumPy HWC 이미지를 PyTorch CHW 텐서(tensor)로 바꿔 반환합니다.

Step 2: 정규화(Normalization)와 데이터 증강(augmentation)

def standardize(mean, std):
    mean = np.array(mean, dtype=np.float32)
    std = np.array(std, dtype=np.float32)
    def _fn(img):
        return (img - mean) / std
    return _fn

def random_hflip(p=0.5):
    def _fn(img):
        if np.random.random() < p:
            return img[:, ::-1, :].copy()
        return img
    return _fn

def random_crop(pad=4):
    def _fn(img):
        h, w = img.shape[:2]
        padded = np.pad(img, ((pad, pad), (pad, pad), (0, 0)), mode="reflect")
        y = np.random.randint(0, 2 * pad)
        x = np.random.randint(0, 2 * pad)
        return padded[y:y + h, x:x + w, :]
    return _fn


def compose(*fns):
    def _fn(img):
        for fn in fns:
            img = fn(img)
        return img
    return _fn

잘라내기 전에는 0으로 채우는 패딩(zero-pad)보다 거울 반사 패딩(reflect-pad)이 더 자연스럽습니다. 검은 테두리(black border)를 모델이 지름길(shortcut)로 학습하는 일을 줄여 줍니다.

Step 3: 믹스업(Mixup)

믹스업은 데이터셋 내부가 아니라 학습 스텝 근처의 배치 변환(batch transform)으로 구현합니다.

def mixup_batch(x, y, num_classes, alpha=0.2):
    if alpha <= 0:
        return x, torch.nn.functional.one_hot(y, num_classes).float()
    lam = float(np.random.beta(alpha, alpha))
    idx = torch.randperm(x.size(0), device=x.device)
    x_mixed = lam * x + (1 - lam) * x[idx]
    y_onehot = torch.nn.functional.one_hot(y, num_classes).float()
    y_mixed = lam * y_onehot + (1 - lam) * y_onehot[idx]
    return x_mixed, y_mixed


def soft_cross_entropy(logits, soft_targets):
    log_probs = torch.log_softmax(logits, dim=-1)
    return -(soft_targets * log_probs).sum(dim=-1).mean()

소프트 타깃(soft target)에 대한 교차 엔트로피는 log_softmax와 타깃 분포(target distribution)의 원소별 곱(element-wise product)으로 계산합니다.

Step 4: 학습 루프(Training loop)

한 에포크(epoch) 동안 데이터를 한 번 돌고, 배치(batch)마다 기울기(gradient)를 계산하고, 스케줄러는 에포크마다 한 번씩 스텝합니다.

import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torch.optim import SGD
from torch.optim.lr_scheduler import CosineAnnealingLR


def train_one_epoch(model, loader, optimizer, device, num_classes, use_mixup=True):
    model.train()
    total, correct, loss_sum = 0, 0, 0.0
    for x, y in loader:
        x, y = x.to(device), y.to(device)
        if use_mixup:
            x_m, y_soft = mixup_batch(x, y, num_classes)
            logits = model(x_m)
            loss = soft_cross_entropy(logits, y_soft)
        else:
            logits = model(x)
            loss = F.cross_entropy(logits, y, label_smoothing=0.1)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        loss_sum += loss.item() * x.size(0)
        total += x.size(0)
        # 믹스업이 켜진 상태에서의 학습 정확도는 섞이지 않은 라벨 `y` 기준의 근사값입니다.
        # 모델은 소프트 타깃을 봤으므로 실제 성능은 검증 정확도(validation accuracy)로 판단합니다.
        with torch.no_grad():
            pred = logits.argmax(dim=-1)
            correct += (pred == y).sum().item()
    return loss_sum / total, correct / total


@torch.no_grad()
def evaluate(model, loader, device, num_classes):
    model.eval()
    total, correct = 0, 0
    loss_sum = 0.0
    cm = torch.zeros(num_classes, num_classes, dtype=torch.long)
    for x, y in loader:
        x, y = x.to(device), y.to(device)
        logits = model(x)
        loss = nn.functional.cross_entropy(logits, y)
        pred = logits.argmax(dim=-1)
        for t, p in zip(y.cpu(), pred.cpu()):
            cm[t, p] += 1
        loss_sum += loss.item() * x.size(0)
        total += x.size(0)
        correct += (pred == y).sum().item()
    return loss_sum / total, correct / total, cm

학습 루프를 쓸 때마다 확인할 불변 조건(invariant)은 다음과 같습니다.

  1. 학습 전 model.train(), 평가 전 model.eval()을 호출합니다.
  2. .zero_grad()는 배치마다 .backward() 전에 호출합니다.
  3. 지표(metric) 누적에는 .item()을 사용해 계산 그래프(graph)를 잡아두지 않습니다.
  4. 평가에는 @torch.no_grad() 또는 with torch.no_grad()를 사용합니다.
  5. 손실에는 소프트맥스가 아니라 원시 로짓을 전달합니다.

Step 5: 전체 연결

합성 데이터셋을 학습/검증(train/val)으로 나누고, TinyResNet 또는 MiniClassifier를 학습합니다. 파이프라인이 올바르다면 합성 데이터셋에서는 몇 에포크 안에 거의 완벽한 검증 정확도에 도달합니다. 데이터셋을 실제 CIFAR-10으로 바꿔도 루프는 그대로 유지됩니다.

from main import synthetic_cifar, ArrayDataset
from main import standardize, random_hflip, random_crop, compose
from main import mixup_batch, soft_cross_entropy
from main import train_one_epoch, evaluate
# TinyResNet comes from the previous lesson (03-cnns-lenet-to-resnet).
# Adjust the import path to wherever you stored the previous lesson's code.
from cnns_lenet_to_resnet import TinyResNet  # example placeholder

X, Y = synthetic_cifar(num_per_class=500)
split = int(0.9 * len(X))
X_train, Y_train = X[:split], Y[:split]
X_val, Y_val = X[split:], Y[split:]

mean = [0.5, 0.5, 0.5]
std = [0.25, 0.25, 0.25]
train_tf = compose(random_hflip(), random_crop(pad=4), standardize(mean, std))
eval_tf = standardize(mean, std)

train_ds = ArrayDataset(X_train, Y_train, transform=train_tf)
val_ds = ArrayDataset(X_val, Y_val, transform=eval_tf)

train_loader = DataLoader(train_ds, batch_size=128, shuffle=True, num_workers=0)
val_loader = DataLoader(val_ds, batch_size=256, shuffle=False, num_workers=0)

device = "cuda" if torch.cuda.is_available() else "cpu"
model = TinyResNet(num_classes=10).to(device)
optimizer = SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4, nesterov=True)
scheduler = CosineAnnealingLR(optimizer, T_max=10)

for epoch in range(10):
    tr_loss, tr_acc = train_one_epoch(model, train_loader, optimizer, device, 10, use_mixup=True)
    va_loss, va_acc, _ = evaluate(model, val_loader, device, 10)
    scheduler.step()
    print(f"epoch {epoch:2d}  lr {scheduler.get_last_lr()[0]:.4f}  "
          f"train {tr_loss:.3f}/{tr_acc:.3f}  val {va_loss:.3f}/{va_acc:.3f}")

합성 데이터셋에서는 이 파이프라인이 5 에포크 안에 거의 완벽한 검증 정확도에 도달합니다. 핵심은 파이프라인이 올바르고, 모델이 학습 가능한 신호(learnable signal)를 학습할 수 있다는 점입니다. 데이터셋을 실제 CIFAR-10으로 바꿔도 같은 루프로 약 90%까지 학습할 수 있습니다.

Step 6: 혼동 행렬 읽기

def print_confusion(cm, labels=None):
    c = cm.shape[0]
    labels = labels or [str(i) for i in range(c)]
    print(f"{'':>6}" + "".join(f"{l:>5}" for l in labels))
    for i in range(c):
        row = cm[i].tolist()
        print(f"{labels[i]:>6}" + "".join(f"{v:>5}" for v in row))
    print()
    tp = cm.diag().float()
    fp = cm.sum(dim=0).float() - tp
    fn = cm.sum(dim=1).float() - tp
    prec = tp / (tp + fp).clamp_min(1)
    rec = tp / (tp + fn).clamp_min(1)
    f1 = 2 * prec * rec / (prec + rec).clamp_min(1e-9)
    for i in range(c):
        print(f"{labels[i]:>6}  prec {prec[i]:.3f}  rec {rec[i]:.3f}  f1 {f1[i]:.3f}")

_, _, cm = evaluate(model, val_loader, device, 10)
print_confusion(cm)

핵심 계산만 따로 보면 다음과 같습니다.

tp = cm.diag().float()
fp = cm.sum(dim=0).float() - tp
fn = cm.sum(dim=1).float() - tp
prec = tp / (tp + fp).clamp_min(1)
rec = tp / (tp + fn).clamp_min(1)
f1 = 2 * prec * rec / (prec + rec).clamp_min(1e-9)

행(row)은 실제 클래스, 열(column)은 예측 결과입니다. 클래스 3과 5 사이의 비대각(off-diagonal) 값이 크다면 두 클래스의 혼동된 예시(confused example)를 직접 보고, 목표 지향적 데이터 수집(targeted data collection)이나 클래스별 데이터 증강(class-specific augmentation)을 설계해야 합니다.

사용하기

실제 CIFAR-10에서는 torchvision 구성 요소로 같은 파이프라인을 구성할 수 있습니다.

from torchvision.datasets import CIFAR10
from torchvision.transforms import Compose, RandomCrop, RandomHorizontalFlip, ToTensor, Normalize

mean = (0.4914, 0.4822, 0.4465)
std = (0.2470, 0.2435, 0.2616)
train_tf = Compose([
    RandomCrop(32, padding=4, padding_mode="reflect"),
    RandomHorizontalFlip(),
    ToTensor(),
    Normalize(mean, std),
])
eval_tf = Compose([ToTensor(), Normalize(mean, std)])

train_ds = CIFAR10(root="./data", train=True,  download=True, transform=train_tf)
val_ds   = CIFAR10(root="./data", train=False, download=True, transform=eval_tf)

평균(mean)과 표준편차(std)는 데이터셋마다 다릅니다. CIFAR-10에는 CIFAR-10 학습 세트에서 계산한 CIFAR-10 통계량을 사용하고, ImageNet 통계량을 그대로 복사해 붙여 넣지 않습니다. 여기서 거울 반사 패딩(reflect padding)은 커뮤니티 기본 잘라내기 정책(community-default crop policy)입니다. ImageNet 통계량을 그대로 붙여 넣으면 약 1%의 정확도 누수(accuracy leak)가 생길 수 있는데, 누군가 성능을 분석(profile)하기 전까지 잘 드러나지 않습니다.

산출물 만들기

이 강의의 최종 산출물은 다음 두 가지입니다.

  • outputs/prompt-classifier-pipeline-auditor.md: 학습 스크립트의 다섯 가지 불변 조건을 감사(audit)하고 첫 번째 위반 사항을 찾는 프롬프트(prompt)
  • outputs/skill-classification-diagnostics.md: 혼동 행렬과 클래스 이름으로 클래스별 실패를 요약하고 가장 영향이 큰 수정안(fix)을 제안하는 스킬(skill)

연습문제

  1. 쉬움: 같은 모델을 합성 데이터셋에서 믹스업 사용 여부에 따라 각각 5 에포크씩 학습합니다. 학습/검증 손실을 그래프로 그리고, 믹스업을 적용했을 때 학습 손실이 더 높음에도 검증 정확도가 비슷하거나 더 나은 이유를 설명합니다.
  2. 중간: 컷아웃(Cutout)을 구현합니다. 학습 이미지마다 무작위 8x8 정사각형 영역을 0으로 만듭니다(zero out). 데이터 증강 없음, 수평 뒤집기(hflip)+잘라내기(crop), hflip+crop+cutout, hflip+crop+mixup을 비교하고 각 경우의 검증 정확도를 보고합니다.
  3. 어려움: CIFAR-100 파이프라인을 만들고 ResNet-34 학습 실행을 발표된(published) 정확도의 1% 이내로 재현합니다. 학습률 3개와 가중치 감쇠(weight decay) 2개를 스윕(sweep)하고 로컬 CSV에 기록한 뒤 최상위 혼동(top confusions) 표를 만듭니다.

핵심 용어

용어흔한 설명실제 의미
로짓(Logits)원시 출력이미지마다 클래스별로 나오는, 소프트맥스 적용 전(pre-softmax) 벡터
교차 엔트로피(Cross-entropy)손실정답 클래스의 음의 로그 확률. log-softmax와 NLL을 수치적으로 안정적으로 결합
데이터 로더(DataLoader)배처(batcher)데이터셋에 셔플링, 배치 구성, 멀티 워커 로딩(multi-worker loading)을 더하는 래퍼(wrapper)
데이터 증강(Augmentation)무작위 변환학습 시에 라벨을 보존하면서 픽셀을 바꿔 불변성을 가르치는 방법
믹스업 / 컷믹스(Mixup / Cutmix)두 이미지 섞기입력과 라벨을 함께 섞어 부드러운 결정 경계(decision boundary)를 학습
라벨 스무딩(Label smoothing)더 부드러운 타깃원-핫을 덜 날카로운 타깃으로 바꿔 보정 성능을 개선
Top-k 정확도(Top-k accuracy)top-5정답 클래스가 상위 k개 예측 안에 있는지를 보는 지표
혼동 행렬(Confusion matrix)오류 위치실제 클래스와 예측 클래스의 C x C 횟수 표

더 읽을거리

실습 코드

이 강의의 실습 코드 1개

main
Code

산출물

이 강의에서 생성된 프롬프트, 스킬, 코드 산출물 2개

skill-classification-diagnostics

Given a confusion matrix and class names, surface per-class failures and propose the single most impactful fix

Skill
prompt-classifier-pipeline-auditor

Audit a PyTorch image classification training script for the five invariants that cover most silent bugs

Prompt

확인 문제

3문제 · 모두 맞추면 완료 표시가 가능합니다

1.믹스업(Mixup)은 원-핫(one-hot) 라벨을 `lambda * y_i + (1-lambda) * y_j` 같은 보간된 소프트 타깃(interpolated soft target)으로 바꿉니다. 이것이 일반화(generalization)에 도움이 되는 이유는 무엇입니까?

2.CIFAR-10에서 `RandomCrop(32, padding=4, padding_mode='zeros')`를 `padding_mode='reflect'`로 바꿉니다. 거울 반사(reflect) 방식이 더 나은 이유는 무엇입니까?

3.분류기의 혼동 행렬에서 대부분의 오류가 클래스 3을 클래스 5로, 클래스 5를 클래스 3으로 예측하는 형태입니다. 가장 영향이 큰 다음 조치는 무엇입니까?

0/3 답변 완료