들어가며
머신러닝 프로젝트에서 가장 큰 장벽 중 하나는 라벨링 데이터 확보입니다. 특히 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 |
클러스터 수 | 도메인 지식 기반 설정 |
후처리 전략
- 작은 마스크 필터링: 노이즈 제거를 위해 일정 크기 이하 마스크 제외
- 중복 마스크 병합: IoU 기반으로 겹치는 영역 통합
- 경계 다듬기: 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 메모리 소비가 큼
- 클러스터 수 설정: 사전 지식 없이는 최적 클래스 수 결정 어려움
극복 전략
- Few-Shot으로 전환: 소량(10-20장)의 라벨로 DINOv2 fine-tuning
- 경량 모델 사용: SAM ViT-B/L 버전 또는 MobileSAM 활용
- 계층적 클러스터링: 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
답글 남기기