전이학습과 소수샷 비전 학습의 선택과 운영
전이학습과 소수샷 비전 학습을 비교하고, 사전학습 백본·프로토타입·PEFT를 활용한 적응 전략과 운영 검증 방법을 정리한다.
2026-08-14 · 최초 발행 2024-04-29
소량 데이터에서 다시 학습하지 않는 법
비전 과제는 신규 클래스나 도메인 변경이 생길 때마다 대규모 학습을 반복하기 어렵다. 사전학습된 ResNet, ViT, CLIP 같은 백본을 재활용하고, 데이터 양과 도메인 차이에 맞춰 미세조정·프로토타입 분류·매개변수 효율 튜닝을 조합하는 방식이 이 문제를 다룬다.
전이학습은 대규모 데이터로 학습된 가중치를 하위 과제에 이어 쓰는 접근이다. 백본이 가진 표현력은 유지하고 마지막 레이어나 일부 블록만 다시 학습할 수 있다. 백본을 고정하는 Feature Extraction, 일부 또는 전체를 조정하는 Partial/Full Fine-Tuning, Adapter·LoRA를 삽입하는 튜닝이 여기에 속한다.
소수샷 학습은 N-way K-shot 환경에서 클래스별 K개 샘플로 분류기 또는 임베딩을 학습한다. 에피소드 학습, 거리 기반 분류, 메타러닝을 통해 적은 표본에서도 일반화를 확보하려는 방식이다. Prototypical Networks는 클래스별 평균 임베딩을 프로토타입으로 삼고, Matching Networks와 MAML도 대표적인 접근이다.
CLIP과 ViT 같은 비전 파운데이션 모델은 임베딩과 텍스트 프롬프트를 이용해 제로샷 또는 소수샷 분류에 적용할 수 있다. 프롬프트 튜닝, Adapter, LoRA는 적은 매개변수만 조정하면서 데이터 희소와 도메인 시프트를 완화하는 수단이다.
데이터와 도메인 차이가 전략을 가른다
데이터가 극히 적고 기존 사전학습 도메인과 유사하다면 백본을 고정한 뒤 얕은 분류기만 학습하는 Feature Extraction이 안정적이다. 데이터가 더 많거나 도메인 차이가 크면 상위 블록만 조정하거나 전체 레이어를 미세조정하는 편이 성능에 유리하다.
프로토타입 기반 분류는 클래스별 임베딩 평균을 만든 뒤 가장 가까운 프로토타입으로 예측한다. 계산이 단순하고 온라인 갱신에 적합하다. 반면 MAML은 적은 스텝의 그라디언트 업데이트로 빠르게 적응할 수 있는 초기화를 학습하지만, 구현과 계산 부담이 크다.
에피소드는 N-way K-shot 구조로 구성해 훈련과 평가의 조건을 맞춘다. 이때 클래스 균형과 샘플 다양성이 핵심이다. MixUp, RandAugment, CutMix 같은 강건 증강과 테스트 시간 증강(TTA)은 과적합을 줄이는 데 쓴다.
Adapter, LoRA, Prompt Tuning은 백본을 동결한 상태에서 일부 파라미터만 학습하므로 메모리와 배포 측면에서 유리하다. 다만 도메인이 크게 바뀌면 Full Fine-Tune보다 성능 차이가 생길 수 있다.
평가는 Query set을 분리한 N-way K-shot 에피소드와 클래스 홀드아웃으로 구성한다. Calibrated confidence를 사용하고, 클래스 중복이나 유사 제품군의 교차 누락 여부를 확인해야 한다. 도메인 시프트를 파악하기 위한 OOD 검정도 함께 둔다.
| 방법 | 성능 | 확장성 | 일관성 | 안정성 | 운영 편의 |
|---|---|---|---|---|---|
| Feature Extraction | 중간 | 높음 | 높음 | 높음 | 매우 높음 |
| Partial/Full Fine-Tuning | 높음 | 중간 | 중간 | 중간 | 중간 |
| Adapter/LoRA/Prompt | 중상 | 높음 | 높음 | 높음 | 높음 |
| Meta-learning (Proto/MAML) | 높음(K 작을 때) | 중간 | 중간 | 중간 | 중간 |
| Foundation+Prompt (CLIP) | 중상(제로/소수샷) | 높음 | 높음 | 높음 | 높음 |
적응부터 배포 감시까지 이어지는 흐름
도메인별로 달라지는 적용 방식
제조 불량 탐지에서는 파운데이션 백본과 프로토타입으로 정상·불량 클래스를 정의하고, 신규 라인마다 K-shot 데이터를 수집해 프로토타입 갱신을 자동화할 수 있다. 라인과 소재마다 분포 차이가 클 수 있으므로 미정 클래스는 OOD 탐지로 별도 라우팅한다.
의료 영상의 소분류에는 ViT 또는 ConvNext를 Feature Extractor로 두고 의사결정 규칙과 앙상블을 결합한 뒤 Few-shot 추가 학습을 이어갈 수 있다. 개인정보 보호와 감사 로깅이 필요하며, 연합학습 또는 안전한 특성 공유를 고려한다.
리테일 SKU 확장에서는 CLIP 임베딩과 텍스트 프롬프트로 초기화하고, 신상품 이미지를 K-shot으로 등록해 최근접 프로토타입 분류를 적용한다. 계절성이나 패키징 변경에서 발생하는 분포 변동에는 주기적 재캘리브레이션이 필요하다.
엣지 환경에서는 PEFT(LoRA/Adapter)로 경량 가중치만 전송하고 온디바이스에서 프로토타입을 갱신할 수 있다. 메모리와 전력 제약을 고려해 정수 양자화, 지연 측정, 온디바이스 캐시 정책을 적용한다.
운영 파이프라인에서 확인할 항목
입력 데이터는 클래스당 K(1~20) 이미지와 분리된 검증용 Query set으로 구성한다. 클래스 설명 텍스트인 프롬프트와 품질 태그도 함께 관리한다. 데이터 가공 단계에서는 강건 증강과 클래스 균형 리샘플링을 수행하고, Feature Extract·Fine-Tune·Few-Shot·PEFT 중 적절한 브랜치를 선택한다.
출력 아티팩트에는 학습 가중치, 프로토타입 벡터, 프롬프트 토큰, 배포 스펙이 포함된다. 검증은 에피소드 기반 정확도, Calibration(ECE), OOD 스코어로 진행한다.
클래스 불균형이 과도하면 Focal Loss 또는 가중치 샘플링을 적용한다. 도메인 시프트는 PSI/KL 기반 분포 감시로 탐지하고 임계치를 넘으면 재학습을 트리거한다. Catastrophic Forgetting에는 리플레이 버퍼나 정규화(EWC)를 사용한다.
PII 제거, 온디바이스 임베딩 저장, KMS 기반 키관리는 보안과 프라이버시 측면의 조건이다. 연합학습은 통신과 드리프트 비용을 높일 수 있다. 백본과 어댑터를 분리한 아티팩트 구조는 재사용성을 높이지만 복잡성이 증가할 수 있다. 데이터 수집, 검증, 카나리 배포로 이어지는 스텝 함수형 재적응 파이프라인에서는 성능과 안정성의 균형을 유지해야 한다.
ResNet50 상위 블록만 미세조정하기
환경/전제: Python 3.10, torch 2.2+, torchvision 0.17+, CUDA 11.8. 데이터는 class별 디렉터리 구조.
import torch, torch.nn as nn, torch.optim as optim
from torchvision import models, datasets, transforms
from torch.utils.data import DataLoader
# 데이터 전처리
train_tf = transforms.Compose([
transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(),
transforms.AutoAugment(), transforms.ToTensor(),
transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225])
])
val_tf = transforms.Compose([
transforms.Resize(256), transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225])
])
train_ds = datasets.ImageFolder("data/train", transform=train_tf)
val_ds = datasets.ImageFolder("data/val", transform=val_tf)
train_loader = DataLoader(train_ds, batch_size=32, shuffle=True, num_workers=4)
val_loader = DataLoader(val_ds, batch_size=64, shuffle=False, num_workers=4)
# 사전학습 백본
num_classes = len(train_ds.classes)
model = models.resnet50(weights=models.ResNet50_Weights.DEFAULT)
for name, p in model.named_parameters():
p.requires_grad = ("layer3" in name) or ("layer4" in name) or ("fc" in name) # 상위 블록만 학습
in_feats = model.fc.in_features
model.fc = nn.Linear(in_feats, num_classes)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# 옵티마이저/스케줄러
params = [p for p in model.parameters() if p.requires_grad]
optimizer = optim.AdamW(params, lr=3e-4, weight_decay=1e-4)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
best = 0.0
for epoch in range(10):
model.train()
for x, y in train_loader:
x, y = x.to(device), y.to(device)
optimizer.zero_grad()
logits = model(x)
loss = criterion(logits, y)
loss.backward()
nn.utils.clip_grad_norm_(params, max_norm=2.0)
optimizer.step()
scheduler.step()
# 검증
model.eval()
correct = total = 0
with torch.no_grad():
for x, y in val_loader:
x, y = x.to(device), y.to(device)
pred = model(x).argmax(1)
correct += (pred == y).sum().item()
total += y.size(0)
acc = correct / total
if acc > best:
best = acc
torch.save(model.state_dict(), "resnet50_finetuned.pt")
print(f"epoch {epoch} val_acc={acc:.4f}")
CLIP 임베딩으로 프로토타입 분류하기
환경/전제: Python 3.10, open_clip 2.24+, torchvision 0.17+. 클래스당 K장 샘플 목록 준비.
import torch, torchvision as tv
import open_clip
from PIL import Image
from pathlib import Path
import numpy as np
device = "cuda" if torch.cuda.is_available() else "cpu"
model, _, preprocess = open_clip.create_model_and_transforms(
'ViT-B-32', pretrained='laion2b_s34b_b79k'
)
model.eval().to(device)
def embed_images(paths):
ims = [preprocess(Image.open(p).convert("RGB")) for p in paths]
ims = torch.stack(ims).to(device)
with torch.no_grad():
feats = model.encode_image(ims)
feats = feats / feats.norm(dim=-1, keepdim=True)
return feats
# K-shot 지원셋과 질의셋
support_dir = Path("fewshot/support") # class_name/xxx.jpg
query_dir = Path("fewshot/query") # class_name/xxx.jpg
classes = sorted([d.name for d in support_dir.iterdir() if d.is_dir()])
prototypes = []
for c in classes:
paths = list((support_dir/c).glob("*.jpg"))
feats = embed_images(paths)
proto = feats.mean(0)
proto = proto / proto.norm()
prototypes.append(proto.unsqueeze(0))
prototypes = torch.cat(prototypes, dim=0) # [C, D]
# 질의 예측
def predict(path):
q = embed_images([path])[0] # [D]
sims = (prototypes @ q) # [C]
idx = int(sims.argmax().item())
conf = float(sims[idx].item())
return classes[idx], conf
# 배치 평가/간단한 OOD 차단
correct = total = 0
threshold = 0.25 # 도메인에 따라 캘리브레이션 필요
for c in classes:
for img in (query_dir/c).glob("*.jpg"):
pred, conf = predict(img)
if conf < threshold:
# 미정 클래스 라우팅
continue
total += 1
correct += int(pred == c)
print("few-shot acc:", correct / max(total,1))
텍스트 프롬프트 앙상블에는 “a photo of a {class}”, “a close-up of {class}” 등을 사용해 평균 유사도를 계산할 수 있다. 프로토타입은 가중 이동평균으로 온라인 갱신하며 신제품 등록 자동화에 연결할 수 있다.
라벨 비용과 학습 자원을 줄이는 범위
전이학습과 소수샷 학습은 도메인이 유사할 때 필요 라벨 수를 5090% 절감할 수 있으며, 상한에 가까워질 수 있다. Full 스크래치 학습과 비교하면 학습 시간은 7095% 단축되고, PEFT 사용 시 GPU 메모리는 30~80% 절감될 수 있다.
K=5 기준에서는 전이학습과 프로토타입을 결합해 스크래치 대비 Top-1 정확도가 +5~20pp 향상될 수 있으며, 효과는 도메인 시프트 크기에 의존한다. 파운데이션 임베딩을 바탕으로 프로토타입을 온라인 갱신하면 배포 중 성능 저하 리스크를 낮추고 롤백도 쉽게 할 수 있다.
도메인 유사성, 라벨 비용, 인프라 제약을 기준으로 Feature Extract, Fine-Tune, PEFT, Proto/Meta를 조합해야 한다. 이 선택은 모델 학습 단계에서 끝나지 않으며, 에피소드 평가와 OOD 감시, 재적응 루프를 포함한 운영 체계로 이어져야 한다.