의미적 분할 — U-Net

세그멘테이션(Segmentation)은 모든 픽셀에 대해 분류를 수행하는 일입니다. U-Net은 다운샘플링하는 인코더(Encoder)와 업샘플링하는 디코더(Decoder)를 짝지은 뒤, 둘 사이에 스킵 연결(Skip Connection)을 이어 이 문제를 작동하게 만듭니다.

유형: Build 언어: Python 선수 학습: Phase 4 Lesson 03 (CNN), Phase 4 Lesson 04 (이미지 분류) 소요 시간: 약 75분

학습 목표

  • 의미론적 세그멘테이션(Semantic Segmentation), 인스턴스 세그멘테이션(Instance Segmentation), 팬옵틱 세그멘테이션(Panoptic Segmentation)을 구분하고 문제에 맞는 과제를 선택합니다.
  • PyTorch로 인코더 블록, 병목(Bottleneck), 전치 합성곱(Transposed Convolution) 또는 업샘플링 기반 디코더, 스킵 연결을 갖춘 U-Net을 직접 만듭니다.
  • 픽셀 단위 교차 엔트로피(Pixel-wise Cross-Entropy), Dice 손실(Dice Loss), 의료·산업 세그멘테이션에서 널리 쓰이는 결합 손실(Combined Loss)을 구현합니다.
  • 클래스별 IoU와 Dice 지표를 읽고, 나쁜 점수가 작은 객체 재현율, 경계 정확도, 클래스 불균형 중 어디에서 오는지 진단합니다.

문제

분류(Classification)는 이미지마다 하나의 레이블을 출력합니다. 탐지(Detection)는 이미지마다 몇 개의 박스를 출력합니다. 세그멘테이션은 픽셀마다 하나의 레이블을 출력합니다. 입력 크기가 H x W라면 출력은 의미론적 세그멘테이션에서는 H x W, 인스턴스 세그멘테이션에서는 H x W x N_instances 형태가 됩니다. 이는 이미지 하나에 대해 하나의 예측이 아니라, 수백만 개의 예측을 수행한다는 뜻입니다.

이 구조 덕분에 세그멘테이션은 거의 모든 밀집 예측(Dense Prediction) 비전 제품의 핵심이 됩니다. 의료 영상의 종양 마스크, 자율주행의 도로·차선·장애물, 위성 영상의 건물 윤곽과 작물 경계, 문서 파싱의 레이아웃 영역, 로보틱스의 집을 수 있는 영역이 모두 여기에 포함됩니다. 이런 문제는 객체 주변에 박스만 그어서 풀 수 없습니다. 정확한 실루엣이 필요합니다.

아키텍처 관점의 문제는 말로는 단순하지만 실제로는 어렵습니다. 네트워크는 이미지의 전역 맥락, 예를 들어 어떤 장면인지, 그리고 로컬 픽셀 디테일, 예를 들어 정확히 어떤 픽셀이 도로이고 어떤 픽셀이 보도인지, 이 둘을 동시에 봐야 합니다. 표준 CNN은 맥락을 얻기 위해 공간 해상도를 압축하는데, 그 과정에서 디테일이 사라집니다. U-Net은 이 둘을 함께 얻기 위해 등장한 설계입니다.

사전 테스트

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

1.U-Net에 인코더와 디코더 사이의 스킵 연결이 필요한 이유는 무엇인가요?

2.한 세그멘테이션 과제에서 배경 픽셀이 99%, 종양 픽셀이 1%입니다. 일반 교차 엔트로피만으로 학습했더니 픽셀 정확도 99%에 도달했습니다. 무슨 일이 일어난 것인가요?

0/2 답변 완료

개념

의미론적, 인스턴스, 팬옵틱

flowchart LR
    IN["입력 이미지"] --> SEM["의미론적<br/>(픽셀 → 클래스)"]
    IN --> INS["인스턴스<br/>(픽셀 → 객체 ID,<br/>전경 클래스만)"]
    IN --> PAN["팬옵틱<br/>(모든 픽셀 → 클래스 + ID)"]

    style SEM fill:#dbeafe,stroke:#2563eb
    style INS fill:#fef3c7,stroke:#d97706
    style PAN fill:#dcfce7,stroke:#16a34a
  • 의미론적(Semantic) 세그멘테이션은 "이 픽셀은 도로, 저 픽셀은 자동차"라고 말합니다. 서로 붙어 있는 두 자동차는 하나의 덩어리로 합쳐집니다.
  • 인스턴스(Instance) 세그멘테이션은 "이 픽셀은 자동차 #3, 저 픽셀은 자동차 #5"라고 말합니다. 배경 사물, 즉 하늘·도로·잔디 같은 배경 부류(stuff) 클래스는 보통 무시합니다.
  • 팬옵틱(Panoptic) 세그멘테이션은 두 방식을 통합합니다. 모든 픽셀은 클래스 레이블을 받고, 모든 인스턴스는 고유 ID를 받으며, 배경 부류(stuff)와 개체 부류(things)가 모두 세그멘테이션됩니다.

이 레슨은 의미론적 세그멘테이션을 다룹니다. 다음 레슨인 Mask R-CNN에서는 인스턴스 세그멘테이션을 다룹니다.

U-Net의 형태

flowchart LR
    subgraph ENC["인코더(수축 경로)"]
        E1["64<br/>H x W"] --> E2["128<br/>H/2 x W/2"]
        E2 --> E3["256<br/>H/4 x W/4"]
        E3 --> E4["512<br/>H/8 x W/8"]
    end
    subgraph BOT["병목"]
        B1["1024<br/>H/16 x W/16"]
    end
    subgraph DEC["디코더(확장 경로)"]
        D4["512<br/>H/8 x W/8"] --> D3["256<br/>H/4 x W/4"]
        D3 --> D2["128<br/>H/2 x W/2"]
        D2 --> D1["64<br/>H x W"]
    end
    E4 --> B1 --> D4
    E1 -. 스킵 .-> D1
    E2 -. 스킵 .-> D2
    E3 -. 스킵 .-> D3
    E4 -. 스킵 .-> D4
    D1 --> OUT["1x1 합성곱<br/>클래스"]

    style ENC fill:#dbeafe,stroke:#2563eb
    style BOT fill:#fef3c7,stroke:#d97706
    style DEC fill:#dcfce7,stroke:#16a34a

인코더는 공간 해상도를 네 번 절반으로 줄이고 채널 수를 두 배로 늘립니다. 디코더는 반대로 공간 해상도를 네 번 두 배로 키우고 채널 수를 절반으로 줄입니다. 스킵 연결은 같은 해상도의 인코더 특징과 디코더 특징을 각 단계에서 이어 붙입니다. 마지막 1x1 합성곱(1x1 Convolution)은 전체 해상도에서 64 -> num_classes로 매핑합니다.

스킵 연결이 필요한 이유는 명확합니다. 디코더가 픽셀 단위 예측을 만들려고 할 때, 디코더가 본 것은 이미 매우 작은 특징 맵입니다. 스킵 연결이 없으면 인코더에서 압축되며 사라진 경계 정보를 복원할 수 없어서, 가장자리를 정확히 위치시키기 어렵습니다. 스킵 연결은 인코더가 내려가는 과정에서 계산한 고해상도 특징 맵을 디코더에게 건네줍니다.

전치 합성곱과 이중선형 업샘플(bilinear upsample)

디코더는 공간 차원을 키워야 합니다. 대표적인 선택지는 두 가지입니다.

  • 전치 합성곱(Transposed Convolution, nn.ConvTranspose2d) — 학습 가능한 업샘플링입니다. 역사적인 U-Net 기본값입니다. 보폭(stride)과 커널 크기(kernel size)가 고르게 맞지 않으면 체커보드 아티팩트(Checkerboard Artifact)가 생길 수 있습니다.
  • 이중선형 업샘플(Bilinear upsample) + 3x3 합성곱 — 매끄럽게 업샘플링한 뒤 합성곱을 적용합니다. 아티팩트가 적고 파라미터도 적어서, 현재는 더 안전한 기본 선택지로 많이 쓰입니다.

두 방식 모두 실제 프로젝트에서 볼 수 있습니다. 첫 U-Net을 만들 때는 이중선형(bilinear) 방식이 더 안전합니다.

픽셀 격자 위의 교차 엔트로피

C개 클래스가 있는 의미론적 세그멘테이션에서 모델 출력은 (N, C, H, W)입니다. 정답은 정수 클래스 ID를 담은 (N, H, W)입니다. 교차 엔트로피는 분류 문제와 같지만 모든 공간 위치에 적용됩니다.

Loss = (n, h, w)에 대해 평균한 -log( softmax(logits[n, :, h, w])[target[n, h, w]] )

PyTorch의 F.cross_entropy는 이 형상(shape)을 기본으로 처리합니다. 별도 형상 변환(reshape)은 필요 없습니다.

Dice 손실이 필요한 이유

교차 엔트로피는 모든 픽셀을 동등하게 다룹니다. 한 클래스가 화면 대부분을 차지할 때, 예를 들어 의료 영상에서 99%는 배경이고 1%만 종양일 때는 이 접근이 문제가 됩니다. 네트워크는 모든 픽셀을 배경으로 예측하고도 99% 정확도를 얻을 수 있지만, 실제로는 쓸모없는 모델입니다.

Dice 손실(Dice Loss)은 예측 마스크와 정답 마스크의 겹침을 직접 최적화합니다.

Dice(p, y) = 2 * sum(p * y) / (sum(p) + sum(y) + epsilon)
Dice_loss = 1 - Dice

여기서 p는 한 클래스의 sigmoid/softmax 확률 맵이고, y는 이진 정답 마스크입니다. 두 마스크가 완벽히 겹칠 때만 손실이 0이 됩니다. 비율 기반 지표이므로 클래스 불균형의 영향을 훨씬 덜 받습니다.

실전에서는 보통 결합 손실(Combined Loss)을 씁니다.

L = L_cross_entropy + lambda * L_dice       (lambda ~ 1)

교차 엔트로피는 학습 초기에 안정적인 그래디언트(gradient)를 주고, Dice는 학습 후반부에 실제 마스크 형태를 맞추는 데 집중하게 합니다. 이 조합은 의료 영상의 기본값에 가깝고, 클래스 불균형이 있는 데이터셋에서도 강력한 기준선입니다.

평가 지표

  • 픽셀 정확도(Pixel Accuracy) — 맞춘 픽셀의 비율입니다. 계산은 쉽지만, 분류의 정확도처럼 불균형 데이터에서는 깨집니다.
  • 클래스별 IoU(IoU per Class) — 각 클래스 마스크의 교집합 대비 합집합(intersection over union)입니다. 클래스 평균은 mIoU입니다.
  • Dice(F1 on Pixels) — IoU와 비슷하며 Dice = 2 * IoU / (1 + IoU) 관계가 있습니다. 의료 영상에서는 Dice, 자율주행 커뮤니티에서는 IoU를 선호하는 편이며, 두 지표는 단조적으로 연결됩니다.
  • 경계 F1(Boundary F1) — 예측 경계가 정답 경계에 얼마나 가까운지 측정합니다. 작은 경계 이동도 벌점으로 잡기 때문에 반도체 검사처럼 정밀도가 중요한 작업에서 중요합니다.

mIoU만 보고하지 말고 클래스별 IoU도 함께 보고해야 합니다. 평균 IoU는 아홉 클래스가 85%이고 한 클래스가 15%인 상황을 숨길 수 있습니다.

입력 해상도 트레이드오프

U-Net의 인코더는 해상도를 네 번 절반으로 줄이므로 입력 크기는 16으로 나누어떨어지는 것이 좋습니다. 의료 이미지는 512x512 또는 1024x1024가 많습니다. 자율주행에서 잘라낸(crop) 이미지는 2048x1024도 흔합니다. U-Net의 메모리 비용은 H * W * C_max에 비례하며, 1024x1024 입력과 1024개 병목 채널만으로도 순전파(forward pass)에 이미 수 GB의 VRAM이 필요합니다.

표준적인 우회 방법은 두 가지입니다.

  1. 입력을 타일(Tile)로 나눕니다. 겹침이 있는 256x256 타일을 처리한 뒤 다시 이어 붙입니다.
  2. 병목을 팽창 합성곱(Dilated Convolution)으로 바꿉니다. 공간 해상도를 더 높게 유지하면서 수용 영역(Receptive Field)을 넓힙니다. DeepLab 계열이 이 방향입니다.

첫 모델이라면 256x256 입력과 기준 채널(base channel) 64를 갖춘 U-Net은 8GB VRAM에서도 비교적 편하게 학습됩니다.

만들어보기

Step 1: 인코더 블록

배치 정규화(Batch Normalization)와 ReLU를 붙인 3x3 합성곱 두 개입니다. 첫 합성곱은 채널 수를 바꾸고, 두 번째 합성곱은 채널 수를 유지합니다.

import torch
import torch.nn as nn
import torch.nn.functional as F

class DoubleConv(nn.Module):
    def __init__(self, in_c, out_c):
        super().__init__()
        self.net = nn.Sequential(
            nn.Conv2d(in_c, out_c, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_c),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_c, out_c, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_c),
            nn.ReLU(inplace=True),
        )

    def forward(self, x):
        return self.net(x)

이 블록은 전체 네트워크에서 반복해서 사용됩니다. bias=False를 두는 이유는 BN의 beta가 bias 역할을 처리하기 때문입니다.

Step 2: Down 블록과 Up 블록

class Down(nn.Module):
    def __init__(self, in_c, out_c):
        super().__init__()
        self.net = nn.Sequential(
            nn.MaxPool2d(2),
            DoubleConv(in_c, out_c),
        )

    def forward(self, x):
        return self.net(x)


class Up(nn.Module):
    def __init__(self, in_c, out_c):
        super().__init__()
        self.up = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=False)
        self.conv = DoubleConv(in_c, out_c)

    def forward(self, x, skip):
        x = self.up(x)
        if x.shape[-2:] != skip.shape[-2:]:
            x = F.interpolate(x, size=skip.shape[-2:], mode="bilinear", align_corners=False)
        x = torch.cat([skip, x], dim=1)
        return self.conv(x)

공간 차원만 확인하는 shape[-2:] 검사는 입력 크기가 16으로 나누어떨어지지 않을 때를 처리합니다. 이어 붙이기(concat) 전에 F.interpolate로 안전하게 정렬합니다. 전체 형상(shape)을 비교하면 채널 수 차이까지 잡히는데, 채널 수 불일치는 조용히 보간(interpolate)할 문제가 아니라 크게 실패해야 하는 문제입니다.

Step 3: U-Net

class UNet(nn.Module):
    def __init__(self, in_channels=3, num_classes=2, base=64):
        super().__init__()
        self.inc = DoubleConv(in_channels, base)
        self.d1 = Down(base, base * 2)
        self.d2 = Down(base * 2, base * 4)
        self.d3 = Down(base * 4, base * 8)
        self.d4 = Down(base * 8, base * 16)
        self.u1 = Up(base * 16 + base * 8, base * 8)
        self.u2 = Up(base * 8 + base * 4, base * 4)
        self.u3 = Up(base * 4 + base * 2, base * 2)
        self.u4 = Up(base * 2 + base, base)
        self.outc = nn.Conv2d(base, num_classes, kernel_size=1)

    def forward(self, x):
        x1 = self.inc(x)
        x2 = self.d1(x1)
        x3 = self.d2(x2)
        x4 = self.d3(x3)
        x5 = self.d4(x4)
        x = self.u1(x5, x4)
        x = self.u2(x, x3)
        x = self.u3(x, x2)
        x = self.u4(x, x1)
        return self.outc(x)

net = UNet(in_channels=3, num_classes=2, base=32)
x = torch.randn(1, 3, 256, 256)
print(f"출력: {net(x).shape}")
print(f"파라미터 수: {sum(p.numel() for p in net.parameters()):,}")

출력 shape는 (1, 2, 256, 256)입니다. 입력과 같은 공간 크기를 유지하고, 채널 수는 num_classes가 됩니다. base=32에서는 약 770만 개의 파라미터를 갖습니다.

Step 4: 손실

def dice_loss(logits, targets, num_classes, eps=1e-6):
    probs = F.softmax(logits, dim=1)
    targets_one_hot = F.one_hot(targets, num_classes).permute(0, 3, 1, 2).float()
    dims = (0, 2, 3)
    intersection = (probs * targets_one_hot).sum(dim=dims)
    denom = probs.sum(dim=dims) + targets_one_hot.sum(dim=dims)
    dice = (2 * intersection + eps) / (denom + eps)
    return 1 - dice.mean()


def combined_loss(logits, targets, num_classes, lam=1.0):
    ce = F.cross_entropy(logits, targets)
    dc = dice_loss(logits, targets, num_classes)
    return ce + lam * dc, {"ce": ce.item(), "dice": dc.item()}

Dice는 클래스별로 계산한 뒤 평균냅니다. 이를 매크로 Dice(Macro Dice)라고 볼 수 있습니다. eps는 배치(batch) 안에 특정 클래스가 없을 때 0으로 나누는 일을 막습니다.

Step 5: IoU 지표

@torch.no_grad()
def iou_per_class(logits, targets, num_classes):
    preds = logits.argmax(dim=1)
    ious = torch.zeros(num_classes)
    for c in range(num_classes):
        pred_c = (preds == c)
        true_c = (targets == c)
        inter = (pred_c & true_c).sum().float()
        union = (pred_c | true_c).sum().float()
        ious[c] = (inter / union) if union > 0 else torch.tensor(float("nan"))
    return ious

길이 C의 벡터를 반환합니다. nan은 배치(batch) 안에 없는 클래스를 표시합니다. mIoU를 계산할 때는 이런 클래스를 평균에 넣지 않아야 합니다.

Step 6: 종단 간(end-to-end) 검증용 합성 데이터셋

색이 있는 배경 위에 도형을 생성합니다. 네트워크가 단순한 픽셀 색이 아니라 도형을 학습해야 합니다.

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

def synthetic_segmentation(num_samples=200, size=64, seed=0):
    rng = np.random.default_rng(seed)
    images = np.zeros((num_samples, size, size, 3), dtype=np.float32)
    masks = np.zeros((num_samples, size, size), dtype=np.int64)
    for i in range(num_samples):
        bg = rng.uniform(0, 1, (3,))
        images[i] = bg
        masks[i] = 0
        num_shapes = rng.integers(1, 4)
        for _ in range(num_shapes):
            cls = int(rng.integers(1, 3))
            color = rng.uniform(0, 1, (3,))
            cx, cy = rng.integers(10, size - 10, size=2)
            r = int(rng.integers(4, 12))
            yy, xx = np.meshgrid(np.arange(size), np.arange(size), indexing="ij")
            if cls == 1:
                mask = (xx - cx) ** 2 + (yy - cy) ** 2 < r ** 2
            else:
                mask = (np.abs(xx - cx) < r) & (np.abs(yy - cy) < r)
            images[i][mask] = color
            masks[i][mask] = cls
        images[i] += rng.normal(0, 0.02, images[i].shape)
        images[i] = np.clip(images[i], 0, 1)
    return images, masks


class SegDataset(Dataset):
    def __init__(self, images, masks):
        self.images = images
        self.masks = masks

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

    def __getitem__(self, i):
        img = torch.from_numpy(self.images[i]).permute(2, 0, 1).float()
        mask = torch.from_numpy(self.masks[i]).long()
        return img, mask

클래스는 배경(0), 원(1), 사각형(2) 세 가지입니다. 네트워크는 도형을 구분하는 법을 배워야 합니다.

Step 7: 학습 루프

def train_one_epoch(model, loader, optimizer, device, num_classes):
    model.train()
    loss_sum, total = 0.0, 0
    iou_sum = torch.zeros(num_classes)
    for x, y in loader:
        x, y = x.to(device), y.to(device)
        logits = model(x)
        loss, _ = combined_loss(logits, y, num_classes)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        loss_sum += loss.item() * x.size(0)
        total += x.size(0)
        iou_sum += iou_per_class(logits, y, num_classes).nan_to_num(0)
    return loss_sum / total, iou_sum / len(loader)

합성 데이터셋에서 10-30 에폭(epoch) 실행하면 도형 클래스의 mIoU가 0.9를 넘는 것을 볼 수 있습니다. 여기서 nan_to_num(0)은 배치(batch)에 없는 클래스를 0으로 처리합니다. 정확한 클래스별 IoU를 원한다면 평가 단계에서 클래스 존재 여부를 마스킹하고 배치 전체에 대해 torch.nanmean을 사용해야 합니다.

활용하기

프로덕션에서는 segmentation_models_pytorch("smp")가 torchvision 또는 timm의 백본(backbone)과 함께 표준 세그멘테이션 아키텍처를 감싸 제공합니다. 세 줄이면 시작할 수 있습니다.

import segmentation_models_pytorch as smp

model = smp.Unet(
    encoder_name="resnet34",
    encoder_weights="imagenet",
    in_channels=3,
    classes=3,
)

실무에서 함께 알아두면 좋은 모델은 다음과 같습니다.

  • DeepLabV3+는 최대 풀링(max-pool) 기반 다운샘플링을 팽창 합성곱으로 바꾸어 병목에서 해상도를 더 유지합니다. 위성·주행 데이터의 경계를 더 빠르게 잡는 데 유리합니다.
  • SegFormer는 합성곱 인코더를 계층적 트랜스포머(Transformer)로 바꿉니다. 여러 벤치마크에서 현재 강력한 SOTA 기준선입니다.
  • Mask2Former / OneFormer는 의미론적, 인스턴스, 팬옵틱 세그멘테이션을 하나의 아키텍처로 통합합니다.

세 모델 모두 smp 또는 transformers에서 같은 데이터 로더(data loader)로 교체해 사용할 수 있습니다.

산출물 만들기

이 레슨의 최종 산출물은 다음과 같습니다.

  • outputs/prompt-segmentation-task-picker.md — 주어진 과제에 대해 의미론적, 인스턴스, 팬옵틱 세그멘테이션 중 무엇을 선택할지 정하고 아키텍처를 제안하는 프롬프트입니다.
  • outputs/skill-segmentation-mask-inspector.md — 클래스 분포, 예측 마스크 통계, 과소 예측되거나 경계가 흐려진 클래스를 보고하는 skill입니다.

연습문제

  1. (쉬움) 이진 세그멘테이션 과제, 즉 전경과 배경을 나누는 문제를 위한 bce_dice_loss를 구현합니다. 전경이 픽셀의 5%인 합성 이진 데이터셋에서 결합 손실이 BCE만 사용할 때보다 더 빠르게 수렴하는지 확인합니다.
  2. (보통) nn.Upsample + conv Up 블록을 nn.ConvTranspose2d Up 블록으로 바꿉니다. 합성 데이터셋에서 두 모델을 모두 학습하고 mIoU를 비교합니다. 전치 합성곱 버전에서 체커보드 아티팩트가 어디에 나타나는지 관찰합니다.
  3. (어려움) 실제 세그멘테이션 데이터셋(Oxford-IIIT Pets, Cityscapes mini split, 의료 영상 subset 등)을 가져와 U-Net을 학습합니다. smp.Unet 기준 모델과 2 IoU point 이내가 되도록 맞춥니다. 클래스별 IoU를 보고하고, 손실에 Dice를 추가했을 때 어떤 클래스가 가장 크게 좋아졌는지 식별합니다.

핵심 용어

용어흔한 설명실제 의미
의미론적 세그멘테이션(Semantic Segmentation)"모든 픽셀에 레이블 붙이기"픽셀 단위로 C개 클래스 중 하나를 분류합니다. 같은 클래스의 인스턴스는 합쳐집니다.
인스턴스 세그멘테이션(Instance Segmentation)"모든 객체에 레이블 붙이기"같은 클래스 안의 서로 다른 객체를 분리합니다. 보통 전경 객체 중심입니다.
팬옵틱 세그멘테이션(Panoptic Segmentation)"Semantic + instance"모든 픽셀은 클래스를 받고, thing 인스턴스는 고유 ID도 받습니다.
스킵 연결(Skip Connection)"U-Net bridge"같은 해상도의 인코더 특징을 디코더 특징에 concatenate합니다. 고주파 디테일을 보존합니다.
전치 합성곱(Transposed Convolution)"Deconvolution"학습 가능한 업샘플링입니다. 체커보드 아티팩트를 만들 수 있습니다.
Dice 손실(Dice Loss)"Overlap loss"`1 - 2
mIoU"Mean intersection over union"클래스별 IoU의 평균입니다. 세그멘테이션 커뮤니티의 표준 지표입니다.
경계 F1(Boundary F1)"Boundary accuracy"경계 픽셀에 대해서만 계산한 F1 점수(score)입니다. 정밀도가 중요한 작업에서 중요합니다.

더 읽을거리

실습 코드

이 강의의 실습 코드 1개

main
Code

산출물

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

skill-segmentation-mask-inspector

Report class distribution, predicted-mask statistics, and the classes most likely to be under-predicted or boundary-blurred

Skill
prompt-segmentation-task-picker

Pick semantic vs instance vs panoptic segmentation and name the architecture for a given task

Prompt

확인 문제

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

1.같은 클래스에 속한 개별 자동차를 서로 분리하는 과제 유형은 무엇인가요?

2.U-Net의 이중선형 업샘플(bilinear upsample) + 3x3 합성곱을 `kernel_size=2`, `stride=2`인 `ConvTranspose2d`로 바꿨더니 출력에 체커보드 아티팩트가 보였습니다. 이유는 무엇인가요?

3.10개 클래스 세그멘테이션 과제에서 mIoU = 0.78을 보고했습니다. 그래도 클래스별 IoU를 함께 공개해야 하는 이유는 무엇인가요?

0/3 답변 완료