본문 바로가기

딥러닝/딥러닝 아키텍쳐

사전학습 모델 활용 (Transfer Learning)

들어가며

딥러닝을 처음 공부하면서 가장 실용적으로 느낀 부분이 사전학습 모델 활용이다. 수억 개의 데이터로 학습된 모델을 가져다가 내 문제에 맞게 조금만 수정하면 처음부터 학습하는 것보다 훨씬 좋은 성능이 나온다. 웹 개발로 비유하면 npm 패키지를 가져다 쓰는 것과 비슷하다. 바퀴를 다시 발명하지 않아도 된다.


Transfer Learning이란?

일반적인 학습:
랜덤 초기화 → 내 데이터로 처음부터 학습

Transfer Learning:
사전학습 모델 로드 → 내 데이터로 미세조정 (Fine-tuning)

효과:
- 적은 데이터로도 좋은 성능
- 학습 시간 대폭 감소
- 사전학습 모델의 일반적 지식 활용

사전학습 모델의 종류

이미지 (Vision)

ResNet-50/101/152   : 안정적, 범용적
EfficientNet        : 정확도-효율 균형 좋음
VGG-16/19           : 단순한 구조, 특징 추출 용이
MobileNet           : 경량화, 모바일/엣지 배포
ViT                 : 최신, 대용량 데이터에 강함
CLIP                : 이미지-텍스트 멀티모달

텍스트 (NLP)

BERT 계열:
- BERT-base/large   : 양방향, 이해 태스크
- RoBERTa           : BERT 개선판
- klue/bert-base    : 한국어 특화

GPT 계열:
- GPT-2/3/4         : 텍스트 생성

한국어:
- klue/roberta      : 한국어
- snunlp/KR-ELECTRA : 한국어

이미지 Transfer Learning

Feature Extraction (특징 추출)

사전학습 모델의 가중치를 고정하고 마지막 레이어만 교체한다.

import torch
import torch.nn as nn
import torchvision.models as models

model = models.resnet50(pretrained=True)

# 모든 파라미터 고정
for param in model.parameters():
    param.requires_grad = False

# 마지막 FC 레이어만 교체
num_features = model.fc.in_features  # 2048
model.fc = nn.Sequential(
    nn.Linear(num_features, 256),
    nn.ReLU(),
    nn.Dropout(0.3),
    nn.Linear(256, 10)
)

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:,}")
# 약 2.2%만 학습

Fine-tuning (미세조정)

레이어별 다른 학습률을 사용하는 방식이 효과적이다.

model = models.resnet50(pretrained=True)
model.fc = nn.Linear(model.fc.in_features, 10)

# 레이어별 다른 학습률 (Discriminative Learning Rate)
optimizer = torch.optim.Adam([
    {'params': model.layer1.parameters(), 'lr': 1e-5},   # 앞 레이어: 매우 작은 lr
    {'params': model.layer2.parameters(), 'lr': 1e-5},
    {'params': model.layer3.parameters(), 'lr': 1e-4},
    {'params': model.layer4.parameters(), 'lr': 1e-4},
    {'params': model.fc.parameters(),     'lr': 1e-3}    # 새 레이어: 큰 lr
])

전체 학습 파이프라인

from torchvision import transforms, datasets
from torch.utils.data import DataLoader

# ImageNet 통계로 정규화 (필수)
train_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.RandomCrop(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(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225]
    )
])

# 폴더 구조: data/train/class1/..., data/val/class1/...
train_dataset = datasets.ImageFolder('data/train', transform=train_transform)
val_dataset   = datasets.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)

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = models.resnet50(pretrained=True)

for param in model.parameters():
    param.requires_grad = False
for param in model.layer4.parameters():
    param.requires_grad = True

model.fc = nn.Linear(model.fc.in_features, len(train_dataset.classes))
model = model.to(device)

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam([
    {'params': model.layer4.parameters(), 'lr': 1e-4},
    {'params': model.fc.parameters(),     'lr': 1e-3}
])
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)

# 최적 모델 저장
best_acc = 0
for epoch in range(20):
    model.train()
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)
        optimizer.zero_grad()
        loss = criterion(model(images), labels)
        loss.backward()
        optimizer.step()

    # 검증
    model.eval()
    correct = total = 0
    with torch.no_grad():
        for images, labels in val_loader:
            images, labels = images.to(device), labels.to(device)
            _, pred = model(images).max(1)
            correct += pred.eq(labels).sum().item()
            total += labels.size(0)

    val_acc = 100. * correct / total
    scheduler.step()

    if val_acc > best_acc:
        best_acc = val_acc
        torch.save(model.state_dict(), 'best_model.pth')

NLP Transfer Learning: HuggingFace

from transformers import AutoTokenizer, AutoModelForSequenceClassification

model_name = "klue/bert-base"  # 한국어 BERT

tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(
    model_name, num_labels=5
)

# 토크나이징
texts = ["이 영화 정말 재미있었어요!", "별로였습니다."]
encoding = tokenizer(
    texts,
    max_length=128,
    padding=True,
    truncation=True,
    return_tensors='pt'
)

# 추론
model.eval()
with torch.no_grad():
    outputs = model(**encoding)
    predictions = outputs.logits.argmax(dim=-1)
    print(f"예측 클래스: {predictions}")

HuggingFace Trainer로 Fine-tuning

from transformers import TrainingArguments, Trainer, DataCollatorWithPadding
from datasets import load_dataset

# 데이터셋 로드 (NSMC 영화 리뷰)
dataset = load_dataset("nsmc")

def tokenize_function(examples):
    return tokenizer(examples['document'], max_length=128, truncation=True)

tokenized_dataset = dataset.map(
    tokenize_function, batched=True, remove_columns=['id', 'document']
)

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=3,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    learning_rate=2e-5,
    weight_decay=0.01,
    evaluation_strategy='epoch',
    save_strategy='epoch',
    load_best_model_at_end=True,
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset['train'],
    eval_dataset=tokenized_dataset['test'],
    tokenizer=tokenizer,
    data_collator=DataCollatorWithPadding(tokenizer),
)

trainer.train()

다양한 NLP 태스크

from transformers import pipeline

# 감성 분석
sentiment = pipeline("sentiment-analysis", model="snunlp/KR-FinBert-SC")
print(sentiment("이 제품 너무 좋아요!"))
# [{'label': 'positive', 'score': 0.99}]

# 질의응답 (QA)
qa = pipeline("question-answering",
    model="bert-large-uncased-whole-word-masking-finetuned-squad")
print(qa(
    question="What is the capital of France?",
    context="Paris is the capital and most populous city of France."
))
# {'answer': 'Paris', 'score': 0.99}

# Named Entity Recognition (NER)
ner = pipeline("ner", model="klue/bert-base", aggregation_strategy="simple")
print(ner("삼성전자는 서울에 본사를 두고 있습니다."))
# [{'entity_group': 'ORG', 'word': '삼성전자'},
#  {'entity_group': 'LOC', 'word': '서울'}]

모델 저장과 로드

# PyTorch 모델 저장
torch.save(model.state_dict(), 'model_weights.pth')

# 체크포인트 저장 (학습 재개 가능)
torch.save({
    'epoch': epoch,
    'model_state_dict': model.state_dict(),
    'optimizer_state_dict': optimizer.state_dict(),
    'best_acc': best_acc
}, 'checkpoint.pth')

# 체크포인트에서 재개
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
start_epoch = checkpoint['epoch'] + 1

# HuggingFace 모델 저장/로드
model.save_pretrained('./my_model')
tokenizer.save_pretrained('./my_model')

loaded_model = AutoModelForSequenceClassification.from_pretrained('./my_model')
loaded_tokenizer = AutoTokenizer.from_pretrained('./my_model')

Transfer Learning 전략 선택

데이터 양에 따른 전략:

데이터 매우 적음 (< 1,000개):
→ Feature Extraction
→ 사전학습 모델 고정, 마지막 레이어만 학습

데이터 적음 (1,000 ~ 10,000개):
→ 부분 Fine-tuning
→ 마지막 몇 개 레이어만 학습 (lr: 1e-4)

데이터 보통 (10,000 ~ 100,000개):
→ 전체 Fine-tuning
→ 모든 레이어 학습, 다른 lr 사용 (앞: 1e-5, 뒤: 1e-3)

데이터 많음 (> 100,000개):
→ 처음부터 학습 또는 Fine-tuning 모두 가능

도메인 유사도에 따른 전략:
유사한 도메인 → 더 많은 레이어 고정
다른 도메인   → 더 많은 레이어 학습

정리

항목 설명 핵심 포인트
Feature Extraction 사전학습 모델 고정, 마지막 레이어만 교체 데이터 적을 때 효과적
Fine-tuning 일부 또는 전체 레이어 학습 도메인 유사할수록 적게 수정
Discriminative LR 레이어별 다른 학습률 앞 레이어 < 뒷 레이어
HuggingFace NLP 사전학습 모델 생태계 AutoModel, Trainer, pipeline
ImageNet 정규화 mean=[0.485, 0.456, 0.406] Vision 모델 필수 전처리
모델 저장 state_dict 또는 save_pretrained 체크포인트로 학습 재개 가능

'딥러닝 > 딥러닝 아키텍쳐' 카테고리의 다른 글

Transformer / Attention  (0) 2026.05.16
RNN / LSTM (시퀀스, 텍스트)  (0) 2026.05.16
CNN (합성곱 신경망)  (0) 2026.05.15