SAM과 DINOv2로 라벨 없이 시작하는 Zero-Shot Semantic Segmentation 실전 가이드

들어가며

머신러닝 프로젝트에서 가장 큰 장벽 중 하나는 라벨링 데이터 확보입니다. 특히 Semantic Segmentation은 픽셀 단위로 정교한 라벨링이 필요해 시간과 비용이 막대하게 소요됩니다. 하지만 Meta AI의 SAM(Segment Anything Model)DINOv2를 결합하면, 단 한 장의 라벨 데이터 없이도 고품질 세그멘테이션이 가능합니다.

Zero-Shot 학습은 사전 학습된 모델의 지식을 활용해 새로운 작업을 수행하는 방식으로, 라벨링 비용을 획기적으로 절감할 수 있습니다.

SAM과 DINOv2란?

SAM (Segment Anything Model)

SAM은 Meta에서 2023년 공개한 범용 세그멘테이션 모델입니다. 11억 개 마스크로 학습되어 어떤 이미지든 객체를 분할할 수 있지만, 의미(semantic) 정보는 제공하지 않습니다. 즉, “이게 개인지 고양이인지”를 구분하지 못합니다.

DINOv2 (Distilled iNtuitive Object v2)

DINOv2는 자기지도학습(self-supervised learning) 기반 비전 모델로, 이미지의 의미적 특징(semantic features)을 추출하는 데 특화되어 있습니다. 라벨 없이도 객체의 개념을 이해할 수 있습니다.

두 모델의 시너지

모델 강점 약점
SAM 정확한 경계 분할 의미 정보 부족
DINOv2 강력한 의미 이해 세그멘테이션 능력 없음
SAM + DINOv2 정확한 경계 + 의미 분류

작동 원리: 3단계 파이프라인

1단계: SAM으로 후보 마스크 생성

SAM의 자동 마스크 생성 기능을 사용해 이미지를 여러 영역으로 분할합니다.

from segment_anything import SamAutomaticMaskGenerator, sam_model_registry

# SAM 모델 로드
sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h.pth")
mask_generator = SamAutomaticMaskGenerator(sam)

# 마스크 생성
masks = mask_generator.generate(image)
print(f"생성된 마스크 수: {len(masks)}")

2단계: DINOv2로 특징 추출

각 마스크 영역에서 DINOv2 특징 벡터를 추출합니다.

import torch
from transformers import AutoImageProcessor, AutoModel

# DINOv2 모델 로드
processor = AutoImageProcessor.from_pretrained('facebook/dinov2-base')
model = AutoModel.from_pretrained('facebook/dinov2-base')

def extract_features(image, mask):
    # 마스크 영역만 추출
    masked_img = image * mask[:, :, None]
    inputs = processor(images=masked_img, return_tensors="pt")

    with torch.no_grad():
        outputs = model(**inputs)
    return outputs.last_hidden_state.mean(dim=1)  # 평균 pooling

3단계: 클러스터링으로 의미 그룹화

추출된 특징 벡터를 K-means 등으로 클러스터링하여 유사한 객체끼리 묶습니다.

from sklearn.cluster import KMeans
import numpy as np

# 모든 마스크의 특징 추출
features = []
for mask in masks:
    feat = extract_features(image, mask['segmentation'])
    features.append(feat.numpy())

features = np.vstack(features)

# 클러스터링 (예: 5개 클래스)
kmeans = KMeans(n_clusters=5, random_state=42)
labels = kmeans.fit_predict(features)

# 각 마스크에 라벨 할당
for i, mask in enumerate(masks):
    mask['predicted_class'] = labels[i]

실무 활용 예시

1. 제조업: 불량 부품 탐지

  • 문제: 새로운 부품 라인마다 라벨 데이터 구축 필요
  • 해결: SAM+DINOv2로 정상/불량 영역 자동 분할
  • 효과: 라벨링 시간 90% 단축, 신규 제품 적용 주기 단축

2. 의료: 의료 영상 분석

  • 문제: 전문의 라벨링 비용이 매우 높음
  • 해결: 장기/병변 영역 자동 세그멘테이션으로 초기 스크리닝
  • 효과: 전문의 검토 대상 50% 감소

3. 자율주행: 새로운 환경 적응

  • 문제: 국가별/도시별 도로 환경이 다름
  • 해결: Zero-Shot으로 새로운 환경의 객체 즉시 인식
  • 효과: 신규 지역 배포 기간 단축

성능 향상 팁

파라미터 튜닝

파라미터 설명 권장값
points_per_side SAM 그리드 밀도 32-64 (높을수록 정밀)
pred_iou_thresh 마스크 품질 임계값 0.86-0.92
n_clusters 클러스터 수 도메인 지식 기반 설정

후처리 전략

  1. 작은 마스크 필터링: 노이즈 제거를 위해 일정 크기 이하 마스크 제외
  2. 중복 마스크 병합: IoU 기반으로 겹치는 영역 통합
  3. 경계 다듬기: Morphological operations로 경계 부드럽게
import cv2

def refine_mask(mask, kernel_size=5):
    kernel = np.ones((kernel_size, kernel_size), np.uint8)
    # 노이즈 제거
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
    # 경계 부드럽게
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
    return mask

한계점과 극복 방법

주요 한계점

  • 미세한 클래스 구분 어려움: 비슷한 객체(예: 허스키 vs 늑대) 구분 한계
  • 컴퓨팅 리소스: SAM ViT-H 모델은 GPU 메모리 소비가 큼
  • 클러스터 수 설정: 사전 지식 없이는 최적 클래스 수 결정 어려움

극복 전략

  1. Few-Shot으로 전환: 소량(10-20장)의 라벨로 DINOv2 fine-tuning
  2. 경량 모델 사용: SAM ViT-B/L 버전 또는 MobileSAM 활용
  3. 계층적 클러스터링: Hierarchical clustering으로 자동 클러스터 수 결정

실전 체크리스트

  • [ ] GPU 환경 준비 (최소 16GB VRAM 권장)
  • [ ] SAM 체크포인트 다운로드 (vit_h: 2.4GB)
  • [ ] DINOv2 모델 선택 (base/large/giant)
  • [ ] 도메인 특성에 맞는 전처리 적용
  • [ ] 클러스터 수 초기값 설정 (도메인 지식 활용)
  • [ ] 검증 세트로 품질 확인
  • [ ] 후처리 파이프라인 구성

마무리

SAM과 DINOv2의 결합은 라벨링 없는 세그멘테이션의 새로운 가능성을 열었습니다. 핵심은 SAM의 정확한 경계 분할 능력과 DINOv2의 의미 이해 능력을 시너지화하는 것입니다.

주요 포인트 요약:
– SAM은 마스크 생성, DINOv2는 의미 분류 담당
– 3단계 파이프라인: 마스크 생성 → 특징 추출 → 클러스터링
– 제조/의료/자율주행 등 다양한 도메인 적용 가능
– 파라미터 튜닝과 후처리로 성능 향상
– 한계는 Few-Shot 학습으로 보완 가능

프로젝트 초기 프로토타이핑이나 라벨 데이터 부족 상황에서 이 접근법을 시도해보세요. 완벽하진 않지만, 빠른 실험과 검증에는 충분히 강력한 도구입니다.

이 글이 도움이 되셨나요? ☕

Buy me a coffee

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

TODAY 33 | TOTAL 33