전이학습
전이학습(Transfer Learning)은 대규모 데이터셋(ImageNet 등)에서 사전학습된 모델의 지식을 새로운 태스크에 활용하는 기법입니다. 이 문서에서는 Feature Extraction 방식의 전이학습을 실습합니다.Feature Extraction 방식
백본(Backbone)의 가중치를 동결(Freeze)하고, 분류 헤드(Head)만 새로 학습하는 방식입니다. 데이터가 적거나 빠른 프로토타이핑이 필요할 때 효과적입니다.import torch
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import ImageFolder
# 데이터 변환 정의
train_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
val_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
# 폴더 구조 기반 데이터셋 로드
train_dataset = ImageFolder('data/train', transform=train_transform)
val_dataset = ImageFolder('data/val', transform=val_transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=4)
print(f"클래스: {train_dataset.classes}")
print(f"학습 이미지: {len(train_dataset)}장")
import timm
# 사전학습 모델 로드 + 커스텀 클래스 수 설정
model = timm.create_model('resnet50', pretrained=True, num_classes=len(train_dataset.classes))
# 백본 가중치 동결
for param in model.parameters():
param.requires_grad = False
# 분류 헤드만 학습 가능하도록 설정
for param in model.get_classifier().parameters():
param.requires_grad = True
# 학습 가능한 파라미터 수 확인
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
total = sum(p.numel() for p in model.parameters())
print(f"학습 파라미터: {trainable:,} / 전체: {total:,} ({trainable/total*100:.1f}%)")
import torch.nn as nn
import torch.optim as optim
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.get_classifier().parameters(), lr=1e-3)
def train_one_epoch(model, loader, criterion, optimizer, device):
"""1에폭 학습을 수행합니다."""
model.train()
running_loss = 0.0
correct = 0
total = 0
for images, labels in loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item() * images.size(0)
_, preds = outputs.max(1)
correct += preds.eq(labels).sum().item()
total += labels.size(0)
epoch_loss = running_loss / total
epoch_acc = correct / total
return epoch_loss, epoch_acc
def evaluate(model, loader, criterion, device):
"""검증 세트에서 성능을 평가합니다."""
model.eval()
running_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for images, labels in loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
loss = criterion(outputs, labels)
running_loss += loss.item() * images.size(0)
_, preds = outputs.max(1)
correct += preds.eq(labels).sum().item()
total += labels.size(0)
return running_loss / total, correct / total
# 학습 실행
num_epochs = 10
for epoch in range(num_epochs):
train_loss, train_acc = train_one_epoch(model, train_loader, criterion, optimizer, device)
val_loss, val_acc = evaluate(model, val_loader, criterion, device)
print(f"Epoch {epoch+1}/{num_epochs} | "
f"Train Loss: {train_loss:.4f} Acc: {train_acc:.4f} | "
f"Val Loss: {val_loss:.4f} Acc: {val_acc:.4f}")
Feature Extraction vs Fine-tuning
| 비교 항목 | Feature Extraction | Fine-tuning |
|---|---|---|
| 백본 가중치 | 동결 | 학습 (전체 또는 일부) |
| 학습 속도 | 빠름 | 느림 |
| 필요 데이터량 | 적음 (수백 장) | 중간 이상 (수천 장) |
| GPU 메모리 | 적음 | 많음 |
| 성능 상한 | 제한적 | 높음 |
| 추천 상황 | 프로토타이핑, 소규모 데이터 | 최종 모델 학습 |
트러블슈팅
검증 정확도가 학습 정확도보다 높습니다
검증 정확도가 학습 정확도보다 높습니다
학습 시에만 Dropout과 Data Augmentation이 적용되므로 정상적인 현상입니다. 학습 초기에 자주 발생하며, 에폭이 진행되면 해소됩니다.
Loss가 줄지 않습니다
Loss가 줄지 않습니다
Feature Extraction에서는 학습률이 너무 낮으면 수렴이 느립니다. 1e-3 정도로 시작하세요. 그래도 개선이 없다면 사전학습 데이터와 대상 도메인의 차이가 클 수 있으므로 Fine-tuning을 시도하세요.

