Transfer Learning으로 100배 적은 데이터로 SOTA 달성 – 거인의 어깨 위에서 AI 만들기
핵심 요약
“데이터 100만 개 없으면 AI 못 만드나요?” 아닙니다.
Transfer Learning(전이 학습)을 사용하면 1,000개만으로도 최고 성능을 달성할 수 있습니다. 핵심은 ImageNet 1,400만 장으로 학습된 ResNet, 수십억 토큰으로 학습된 BERT/GPT 같은 사전학습 모델(Pretrained Model)의 지식을 재활용하는 것입니다.
처음부터 학습(From Scratch)하면 수개월 걸리고 GPU 수천 개가 필요하지만, Fine-tuning은 몇 시간, GPU 1개면 충분합니다. 실전에서 Feature Extraction은 사전학습 모델을 고정하고 마지막 분류기만 학습하며, Fine-tuning은 전체 또는 일부 가중치를 미세조정합니다. LoRA(Low-Rank Adaptation)와 QLoRA는 파라미터 1%만 튜닝해서 메모리 75% 절감하며, Hugging Face는 50만 개 이상의 사전학습 모델을 무료 제공합니다.
실제로 의료 AI는 ImageNet 모델로 100개 데이터만으로 90% 정확도, 법률 챗봇은 GPT-3.5 Fine-tuning으로 3일 만에 구축, Tesla 자율주행은 시뮬레이션→실도로 Transfer로 사고율 90% 감소를 달성했습니다.
본 포스팅에서는 Transfer Learning 원리, Feature Extraction vs Fine-tuning, BERT/GPT 실전 코드, LoRA/PEFT 최신 기법, 도메인별 전략까지 전문가와 입문자 모두를 위한 완벽 가이드를 제공합니다.
📍 목차
- Transfer Learning이란? – 거인의 어깨 위에 서기
- Feature Extraction vs Fine-tuning
- 사전학습 모델 선택 가이드
- Fine-tuning 실전 코드 (이미지 + NLP)
- LoRA와 PEFT – 효율적 Fine-tuning
- 도메인별 적용 전략과 사례
1. Transfer Learning이란? – 거인의 어깨 위에 서기
1-1. 핵심 개념
정의:
이미 학습된 모델(사전학습 모델)의 지식을
새로운 문제에 재활용하는 기법
비유:
- 영어 능통자가 스페인어 빨리 배우듯
- 기존 지식을 새 문제에 전이(Transfer)
아이작 뉴턴:
"내가 더 멀리 봤다면, 거인의 어깨 위에 섰기 때문이다"
→ AI도 거인(대규모 모델)의 어깨 위에!1-2. 왜 Transfer Learning이 혁명적인가?
처음부터 학습 (From Scratch):
ImageNet 분류 모델 학습:
- 데이터: 1,400만 장 이미지
- GPU: 8 × V100 (약 1억원)
- 시간: 2주
- 전기료: 수백만원
GPT-3 학습:
- 데이터: 570GB 텍스트
- GPU: 수만 개 (약 1,000억원)
- 시간: 수개월
- 전기료: 수십억원
→ 일반인/기업은 불가능!Transfer Learning 사용:
같은 성능 달성:
- 데이터: 1,000~10,000개
- GPU: 1개 (RTX 3090)
- 시간: 수 시간
- 비용: 전기료 수천원
절감 효과:
- 데이터: 99.9% 절감
- 비용: 99.99% 절감
- 시간: 99% 절감
→ 누구나 최고 성능 AI 가능!1-3. Transfer Learning의 3가지 유형
1. Inductive Transfer Learning:
정의: 같은 도메인, 다른 태스크
예시:
- 소스: ImageNet 이미지 분류 (1,000 클래스)
- 타겟: 의료 이미지 분류 (암 유무)
특징:
- 가장 일반적인 형태
- Fine-tuning 주로 사용
- 높은 성능 달성2. Transductive Transfer Learning:
정의: 같은 태스크, 다른 도메인
예시:
- 소스: 영어 감정 분석
- 타겟: 한국어 감정 분석
특징:
- 도메인 적응(Domain Adaptation)
- 라벨 없는 타겟 데이터 활용
- 크로스링구얼(Cross-lingual) 전이3. Unsupervised Transfer Learning:
정의: 라벨 없이 전이
예시:
- 소스: 라벨 없는 대규모 이미지
- 타겟: 라벨 없는 특정 도메인 이미지
특징:
- Self-supervised Learning 기반
- BERT, GPT의 사전학습 방식
- 최신 연구 활발1-4. Transfer Learning 성공의 핵심 원리
왜 작동하는가?
계층적 특징 학습:
CNN의 경우:
Layer 1-2: 엣지, 색상 (범용)
Layer 3-4: 텍스처, 패턴 (범용)
Layer 5-6: 부분 객체 (준범용)
Layer 7+: 전체 객체 (태스크 특화)
핵심 인사이트:
- 하위 층: 범용 특징 → 재사용 가치 높음
- 상위 층: 특화 특징 → 새로 학습 필요
Transformer의 경우:
하위 층: 문법, 구문 구조 (범용)
중간 층: 의미, 관계 (준범용)
상위 층: 태스크 특화 표현 (특화)Transfer 가능성 조건:
성공 조건:
✅ 소스-타겟 도메인 유사성
✅ 소스 데이터 충분히 다양
✅ 사전학습 모델 충분히 큼
✅ 태스크 관련성
실패 조건 (Negative Transfer):
❌ 도메인 너무 다름 (자연 → 의료)
❌ 소스 데이터 편향
❌ 잘못된 층 선택
❌ 과도한 Fine-tuning2. Feature Extraction vs Fine-tuning
2-1. Feature Extraction (특징 추출)
정의:
사전학습 모델의 가중치를 고정(Freeze)하고
마지막 분류기만 새로 학습
모델 구조:
[사전학습 백본] → [새 분류기]
(고정) (학습)
비유:
- 카메라 렌즈(백본)는 그대로 사용
- 필름(분류기)만 교체Python 구현 (PyTorch):
import torch
import torch.nn as nn
import torchvision.models as models
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 1. 사전학습 모델 로드
model = models.resnet50(pretrained=True)
# 2. 모든 파라미터 고정 (Freeze)
for param in model.parameters():
param.requires_grad = False
# 3. 마지막 분류기만 교체
num_features = model.fc.in_features
num_classes = 10 # 새로운 클래스 수
model.fc = nn.Sequential(
nn.Linear(num_features, 256),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(256, num_classes)
)
# 4. 학습할 파라미터 확인
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in model.parameters())
print(f"학습 가능 파라미터: {trainable_params:,} / {total_params:,}")
print(f"비율: {trainable_params/total_params*100:.2f}%")
# 출력:
# 학습 가능 파라미터: 526,858 / 23,534,154
# 비율: 2.24%
# 5. 학습 설정
optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
# 6. 학습 루프
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
for epoch in range(10):
model.train()
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 검증
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in val_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f"Epoch {epoch+1}: 정확도 {100*correct/total:.2f}%")Feature Extraction 장단점:
장점:
✅ 매우 빠른 학습 (파라미터 2%만 학습)
✅ 적은 GPU 메모리
✅ 과적합 위험 낮음
✅ 매우 적은 데이터에도 작동
단점:
❌ 성능 한계 (백본 고정)
❌ 도메인 차이 크면 성능 저하
❌ 태스크 특화 불가
적합한 경우:
- 데이터 매우 적음 (2-2. Fine-tuning (미세 조정)
정의:
사전학습 모델의 가중치를 새 데이터로 업데이트
3가지 전략:
1. 전체 Fine-tuning: 모든 층 학습
2. 부분 Fine-tuning: 상위 층만 학습
3. 점진적 Fine-tuning: 단계적으로 층 해제
비유:
- 기존 요리 레시피를 맛에 맞게 조정
- 기본은 유지하면서 미세하게 변경Python 구현 (부분 Fine-tuning):
import torch
import torch.nn as nn
import torchvision.models as models
# 1. 사전학습 모델 로드
model = models.resnet50(pretrained=True)
# 2. 전체 고정
for param in model.parameters():
param.requires_grad = False
# 3. 마지막 층(layer4)만 해제
for param in model.layer4.parameters():
param.requires_grad = True
# 4. 분류기 교체
num_features = model.fc.in_features
model.fc = nn.Linear(num_features, 10)
# 5. 학습 가능 파라미터 확인
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*100:.2f}%")
# 출력: 학습 비율: 35.47%
# 6. 차등 학습률 설정 (중요!)
optimizer = torch.optim.Adam([
{'params': model.layer4.parameters(), 'lr': 1e-4}, # 낮은 LR
{'params': model.fc.parameters(), 'lr': 1e-3} # 높은 LR
])
# 이유: 사전학습된 가중치는 조심스럽게 조정
# 새 분류기는 처음부터 학습
# 7. 학습
criterion = nn.CrossEntropyLoss()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
for epoch in range(20):
model.train()
running_loss = 0.0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
# Gradient Clipping (선택적)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
running_loss += loss.item()
print(f"Epoch {epoch+1}: Loss = {running_loss/len(train_loader):.4f}")점진적 Fine-tuning (Gradual Unfreezing):
def gradual_unfreeze(model, epoch, unfreeze_schedule):
"""
에폭에 따라 점진적으로 층 해제
unfreeze_schedule = {
5: 'layer4', # 5 에폭부터 layer4 해제
10: 'layer3', # 10 에폭부터 layer3도 해제
15: 'layer2', # 15 에폭부터 layer2도 해제
}
"""
for unfreeze_epoch, layer_name in unfreeze_schedule.items():
if epoch >= unfreeze_epoch:
layer = getattr(model, layer_name)
for param in layer.parameters():
param.requires_grad = True
print(f"Epoch {epoch}: {layer_name} unfrozen")
# 사용 예시
unfreeze_schedule = {5: 'layer4', 10: 'layer3', 15: 'layer2'}
for epoch in range(30):
gradual_unfreeze(model, epoch, unfreeze_schedule)
# 학습률 재설정 (층 해제 시)
if epoch in unfreeze_schedule:
optimizer = torch.optim.Adam(
filter(lambda p: p.requires_grad, model.parameters()),
lr=1e-4
)
# 학습 진행...2-3. Feature Extraction vs Fine-tuning 비교
기준 | Feature Extraction | Fine-tuning
─────────────────────────────────────────────────────
학습 파라미터 | 1-5% | 20-100%
학습 시간 | 빠름 (분~시간) | 느림 (시간~일)
GPU 메모리 | 적음 | 많음
필요 데이터 | 적음 (100~1,000) | 중간 (1,000~10,000)
성능 상한 | 제한적 | 높음
과적합 위험 | 낮음 | 높음
도메인 차이 대응 | 어려움 | 가능
─────────────────────────────────────────────────────
추천 상황 | 데이터 적음 | 데이터 충분
| 도메인 유사 | 도메인 다름
| 빠른 개발 | 최고 성능 필요3. 사전학습 모델 선택 가이드
3-1. 컴퓨터 비전 (이미지)
주요 사전학습 모델:
1. ResNet (2015):
- 깊은 네트워크 가능 (Skip Connection)
- ResNet-50, ResNet-101, ResNet-152
- ImageNet 정확도: 76-78%
- 범용적, 안정적
2. EfficientNet (2019):
- NAS로 발견된 최적 구조
- EfficientNet-B0 ~ B7
- ImageNet 정확도: 77-84%
- 효율적 (성능/파라미터 비율 최고)
3. Vision Transformer (ViT, 2020):
- Transformer 기반 이미지 모델
- ViT-B/16, ViT-L/16
- ImageNet 정확도: 77-87%
- 대규모 데이터에 강함
4. ConvNeXt (2022):
- CNN의 현대화 버전
- ViT 성능에 근접
- 학습 안정적선택 기준:
데이터 10,000개:
→ ViT-B/16 + Full Fine-tuning
효율성 중시:
→ EfficientNet (모바일 배포)
최고 성능:
→ ViT-L/16 또는 ConvNeXt-L3-2. 자연어처리 (NLP)
주요 사전학습 모델:
1. BERT (2018, Google):
- 양방향 Transformer Encoder
- Masked Language Modeling
- BERT-base (110M), BERT-large (340M)
- 분류, NER, QA에 최적
2. GPT-2/3/4 (OpenAI):
- 단방향 Transformer Decoder
- Autoregressive Language Modeling
- GPT-2 (1.5B), GPT-3 (175B), GPT-4 (추정 1.7T)
- 생성 태스크에 최적
3. RoBERTa (2019, Meta):
- BERT 학습 방식 최적화
- NSP 제거, 더 많은 데이터
- BERT보다 대부분 성능 우수
4. T5 (2020, Google):
- Text-to-Text 통합 프레임워크
- 모든 NLP 태스크를 텍스트 생성으로
- 범용성 높음
5. LLaMA (2023, Meta):
- 오픈소스 LLM
- 7B, 13B, 65B 파라미터
- 상업적 사용 가능한국어 모델:
1. KoBERT (SKT):
- 한국어 BERT
- 한국어 위키 + 뉴스
2. KoGPT (Kakao):
- 한국어 GPT
- 6B 파라미터
3. KoELECTRA:
- 효율적인 사전학습
- 적은 자원으로 좋은 성능
4. KoAlpaca:
- LLaMA 기반 한국어 모델
- 오픈소스선택 기준:
텍스트 분류/NER:
→ BERT, RoBERTa, KoBERT
질의응답:
→ BERT, T5
텍스트 생성:
→ GPT-2/3, LLaMA, KoGPT
요약:
→ T5, BART
대화:
→ GPT-3.5/4, LLaMA + Chat Tuning3-3. Hugging Face 모델 허브 활용
from transformers import AutoModel, AutoTokenizer
# 모델 검색 및 로드
model_name = "bert-base-multilingual-cased"
# 자동으로 적합한 모델 클래스 로드
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)
# 한국어 모델 예시
korean_models = [
"monologg/kobert", # KoBERT
"beomi/KcELECTRA-base", # KcELECTRA
"kykim/bert-kor-base", # 한국어 BERT
"gogamza/kobart-base-v2", # KoBART
]
# 태스크별 모델 로드
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(
"bert-base-uncased",
num_labels=2 # 이진 분류
)
# 사용 가능한 태스크별 클래스:
# - AutoModelForSequenceClassification (분류)
# - AutoModelForTokenClassification (NER)
# - AutoModelForQuestionAnswering (QA)
# - AutoModelForSeq2SeqLM (번역, 요약)
# - AutoModelForCausalLM (생성)4. Fine-tuning 실전 코드 (이미지 + NLP)
4-1. 이미지 분류 Fine-tuning (PyTorch)
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms, models
from tqdm import tqdm
# 1. 데이터 준비
train_transform = transforms.Compose([
transforms.Resize(256),
transforms.RandomCrop(224),
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(15),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# 데이터셋 (예: 폴더 구조)
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)
num_classes = len(train_dataset.classes)
print(f"클래스 수: {num_classes}")
print(f"클래스: {train_dataset.classes}")
# 2. 모델 구성
class FineTunedModel(nn.Module):
def __init__(self, num_classes, freeze_backbone=False):
super().__init__()
# 사전학습 모델 로드
self.backbone = models.efficientnet_b3(pretrained=True)
# 백본 고정 여부
if freeze_backbone:
for param in self.backbone.parameters():
param.requires_grad = False
# 분류기 교체
in_features = self.backbone.classifier[1].in_features
self.backbone.classifier = nn.Sequential(
nn.Dropout(0.3),
nn.Linear(in_features, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, num_classes)
)
def forward(self, x):
return self.backbone(x)
# 3. 학습 함수
def train_epoch(model, loader, criterion, optimizer, device):
model.train()
running_loss = 0.0
correct = 0
total = 0
for images, labels in tqdm(loader, desc="Training"):
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
# Gradient Clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
return running_loss / len(loader), 100. * correct / total
def evaluate(model, loader, criterion, device):
model.eval()
running_loss = 0.0
correct = 0
total = 0
with torch.no_grad():
for images, labels in tqdm(loader, desc="Evaluating"):
images, labels = images.to(device), labels.to(device)
outputs = model(images)
loss = criterion(outputs, labels)
running_loss += loss.item()
_, predicted = outputs.max(1)
total += labels.size(0)
correct += predicted.eq(labels).sum().item()
return running_loss / len(loader), 100. * correct / total
# 4. 학습 실행
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = FineTunedModel(num_classes=num_classes, freeze_backbone=False)
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)
# 학습률 스케줄러
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=20)
# Early Stopping
best_val_acc = 0
patience = 5
patience_counter = 0
for epoch in range(50):
print(f"\nEpoch {epoch+1}/50")
train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device)
val_loss, val_acc = evaluate(model, val_loader, criterion, device)
scheduler.step()
print(f"Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%")
print(f"Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%")
# Best 모델 저장
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(), 'best_model.pth')
patience_counter = 0
print(f"✓ Best model saved! (Acc: {val_acc:.2f}%)")
else:
patience_counter += 1
if patience_counter >= patience:
print(f"Early stopping at epoch {epoch+1}")
break
print(f"\nBest Validation Accuracy: {best_val_acc:.2f}%")4-2. BERT Fine-tuning (텍스트 분류)
import torch
from torch.utils.data import DataLoader, Dataset
from transformers import (
BertTokenizer,
BertForSequenceClassification,
AdamW,
get_linear_schedule_with_warmup
)
from sklearn.model_selection import train_test_split
from tqdm import tqdm
# 1. 데이터셋 클래스
class TextDataset(Dataset):
def __init__(self, texts, labels, tokenizer, max_length=128):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = self.texts[idx]
label = self.labels[idx]
encoding = self.tokenizer(
text,
truncation=True,
max_length=self.max_length,
padding='max_length',
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].squeeze(),
'attention_mask': encoding['attention_mask'].squeeze(),
'labels': torch.tensor(label)
}
# 2. 데이터 준비 (예시)
texts = ["이 영화 정말 재미있어요!", "최악의 영화였습니다.", ...]
labels = [1, 0, ...] # 1: 긍정, 0: 부정
train_texts, val_texts, train_labels, val_labels = train_test_split(
texts, labels, test_size=0.2, random_state=42
)
# 3. 토크나이저 및 모델 로드
model_name = "bert-base-multilingual-cased" # 다국어 BERT
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertForSequenceClassification.from_pretrained(
model_name,
num_labels=2
)
# 4. 데이터로더
train_dataset = TextDataset(train_texts, train_labels, tokenizer)
val_dataset = TextDataset(val_texts, val_labels, tokenizer)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=16)
# 5. 학습 설정
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
# AdamW 옵티마이저 (Weight Decay 포함)
optimizer = AdamW(model.parameters(), lr=2e-5, eps=1e-8)
# 총 학습 스텝
epochs = 3
total_steps = len(train_loader) * epochs
# Warmup 스케줄러
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=int(0.1 * total_steps), # 10% warmup
num_training_steps=total_steps
)
# 6. 학습 루프
def train_bert():
model.train()
total_loss = 0
correct = 0
total = 0
for batch in tqdm(train_loader, desc="Training"):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
optimizer.zero_grad()
outputs = model(
input_ids=input_ids,
attention_mask=attention_mask,
labels=labels
)
loss = outputs.loss
logits = outputs.logits
loss.backward()
# Gradient Clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
scheduler.step()
total_loss += loss.item()
preds = torch.argmax(logits, dim=1)
correct += (preds == labels).sum().item()
total += labels.size(0)
return total_loss / len(train_loader), correct / total
def evaluate_bert():
model.eval()
total_loss = 0
correct = 0
total = 0
with torch.no_grad():
for batch in tqdm(val_loader, desc="Evaluating"):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
outputs = model(
input_ids=input_ids,
attention_mask=attention_mask,
labels=labels
)
total_loss += outputs.loss.item()
preds = torch.argmax(outputs.logits, dim=1)
correct += (preds == labels).sum().item()
total += labels.size(0)
return total_loss / len(val_loader), correct / total
# 7. 학습 실행
for epoch in range(epochs):
print(f"\n{'='*50}")
print(f"Epoch {epoch+1}/{epochs}")
print('='*50)
train_loss, train_acc = train_bert()
val_loss, val_acc = evaluate_bert()
print(f"Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f}")
print(f"Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}")
# 8. 모델 저장
model.save_pretrained("./fine_tuned_bert")
tokenizer.save_pretrained("./fine_tuned_bert")
print("Fine-tuning 완료!")5. LoRA와 PEFT – 효율적 Fine-tuning
5-1. 왜 효율적 Fine-tuning이 필요한가?
문제:
GPT-3 전체 Fine-tuning:
- 파라미터: 175B
- GPU 메모리: 350GB+ (A100 80GB × 5개)
- 학습 시간: 수일
- 비용: 수천만원
일반 사용자/기업:
- GPU: RTX 3090 24GB 1개
- 예산: 제한적
→ 전체 Fine-tuning 불가능!해결책: Parameter-Efficient Fine-Tuning (PEFT)
핵심 아이디어:
- 전체 파라미터 중 1-5%만 학습
- 나머지는 고정
- 성능은 전체 Fine-tuning의 90-99%
효과:
- GPU 메모리: 75% 절감
- 학습 시간: 50% 절감
- 비용: 90% 절감5-2. LoRA (Low-Rank Adaptation)
원리:
사전학습 가중치 W를 고정하고
저차원(Low-Rank) 분해 행렬만 학습
W' = W + ΔW
ΔW = A × B (저차원 분해)
예시:
W: 768 × 768 = 589,824 파라미터
A: 768 × 8 = 6,144 파라미터
B: 8 × 768 = 6,144 파라미터
ΔW = A × B: 12,288 파라미터
절감: 589,824 → 12,288 (98% 감소!)Python 구현 (PEFT 라이브러리):
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
# 1. 기본 모델 로드
model_name = "meta-llama/Llama-2-7b-hf"
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 2. LoRA 설정
lora_config = LoraConfig(
r=8, # Rank (낮을수록 효율적, 높을수록 표현력)
lora_alpha=32, # Scaling factor
target_modules=["q_proj", "v_proj"], # 적용할 모듈
lora_dropout=0.1, # Dropout
bias="none", # Bias 학습 여부
task_type=TaskType.CAUSAL_LM # 태스크 타입
)
# 3. PEFT 모델 생성
model = get_peft_model(model, lora_config)
# 4. 학습 가능 파라미터 확인
model.print_trainable_parameters()
# 출력: trainable params: 4,194,304 || all params: 6,742,609,920 || trainable%: 0.0622
# 0.06%만 학습! (6.7B 중 4.2M만)
# 5. 학습
from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(
output_dir="./lora_output",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
fp16=True,
save_strategy="epoch",
logging_steps=10,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
data_collator=data_collator,
)
trainer.train()
# 6. 모델 저장 (LoRA 어댑터만 저장 - 수 MB)
model.save_pretrained("./lora_adapter")
# 7. 추론 시 로드
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(model_name)
model = PeftModel.from_pretrained(base_model, "./lora_adapter")5-3. QLoRA (Quantized LoRA)
원리:
LoRA + 4비트 양자화
효과:
- 7B 모델: 28GB → 6GB (GPU 1개로 가능!)
- 13B 모델: 52GB → 10GB
- 65B 모델: 260GB → 48GB
RTX 3090 24GB로:
- 7B 모델: ✅ 가능
- 13B 모델: ✅ 가능 (일부)
- 65B 모델: ❌ 불가능Python 구현:
from transformers import AutoModelForCausalLM, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
# 1. 4비트 양자화 설정
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_use_double_quant=True,
)
# 2. 양자화된 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
quantization_config=bnb_config,
device_map="auto"
)
# 3. 양자화 학습 준비
model = prepare_model_for_kbit_training(model)
# 4. LoRA 설정 및 적용
lora_config = LoraConfig(
r=64,
lora_alpha=16,
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# 이제 RTX 3090 24GB에서 7B 모델 Fine-tuning 가능!5-4. PEFT 방법 비교
방법 | 학습 파라미터 | 메모리 절감 | 성능 유지 | 추천 상황
──────────────────────────────────────────────────────────────
Full FT | 100% | 0% | 100% | 자원 충분
LoRA | 0.1-1% | 50% | 95-99% | 일반적
QLoRA | 0.1-1% | 75% | 90-95% | GPU 제한
Adapter | 1-5% | 30% | 95-98% | 멀티태스크
Prefix | 6. 도메인별 적용 전략과 사례
6-1. 의료 AI
문제:
의료 데이터 특성:
- 데이터 매우 희소 (희귀 질환)
- 라벨링 비용 높음 (전문의 필요)
- 프라이버시 제약
예시:
- 피부암 데이터: 1,000장
- 전문의 라벨링 비용: 장당 $10
- 총 비용: $10,000
처음부터 학습하면?
→ 정확도 60% (데이터 부족)Transfer Learning 적용:
# 의료 이미지 분류 (피부암)
import torch
import torchvision.models as models
# ImageNet 사전학습 모델
model = models.densenet121(pretrained=True)
# 의료 특화: 마지막 층만 교체
model.classifier = nn.Sequential(
nn.Linear(1024, 256),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(256, 2) # 양성/악성
)
# Fine-tuning 전략:
# 1. 먼저 분류기만 학습 (5 에폭)
# 2. 마지막 Dense Block 해제 (10 에폭)
# 3. 전체 미세조정 (5 에폭)
# 결과:
# - 데이터: 1,000장
# - 정확도: 92% (처음부터 60% → 32%p 향상!)
# - 학습 시간: 2시간실제 사례:
Google Health 피부암 진단 (2017):
- 사전학습: ImageNet (1,400만 장)
- Fine-tuning: 피부암 이미지 (13만 장)
- 정확도: 91% (피부과 전문의와 동등)
CheXNet 흉부 X-ray (2017):
- 사전학습: ImageNet
- Fine-tuning: 흉부 X-ray (11만 장)
- 14가지 질환 진단
- 일부 질환에서 방사선 전문의 초과6-2. 법률/금융 NLP
문제:
도메인 특화 언어:
- 법률 용어, 금융 용어
- 일반 BERT로는 이해 어려움
- 특화 데이터 필요도메인 적응 Fine-tuning:
from transformers import (
BertForMaskedLM,
BertTokenizer,
DataCollatorForLanguageModeling,
Trainer,
TrainingArguments
)
# 1. 도메인 적응 (Domain-Adaptive Pretraining)
# 법률 텍스트로 추가 MLM 학습
model = BertForMaskedLM.from_pretrained("bert-base-uncased")
tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
# 법률 문서 데이터셋
legal_texts = load_legal_corpus() # 법률 문서 수백만 건
# MLM 데이터 콜레이터
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer,
mlm=True,
mlm_probability=0.15
)
# 도메인 적응 학습
training_args = TrainingArguments(
output_dir="./legal-bert",
num_train_epochs=3,
per_device_train_batch_size=16,
learning_rate=5e-5,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=legal_dataset,
data_collator=data_collator,
)
trainer.train()
model.save_pretrained("./legal-bert")
# 2. 태스크 Fine-tuning (계약서 분류)
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(
"./legal-bert", # 도메인 적응된 모델
num_labels=5 # 계약서 유형 5가지
)
# Fine-tuning...실제 사례:
JP Morgan COIN (Contract Intelligence):
- 상업 대출 계약서 분석
- Transfer Learning + Fine-tuning
- 36만 시간 사람 작업 → 몇 초로 단축
- 연간 수백억 절감
LawGeex 법률 계약 검토:
- BERT 기반 Fine-tuning
- 계약서 검토 정확도: 94%
- 변호사 평균: 85%
- 검토 시간: 26초 (변호사 92분)6-3. 자율주행
문제:
시뮬레이션 → 실도로 갭:
- 시뮬레이션에서 완벽해도
- 실도로에서 성능 급락
- 도메인 차이 (Sim-to-Real Gap)Domain Adaptation:
# Sim-to-Real Transfer Learning
# 1. 시뮬레이션에서 대규모 학습
# - CARLA 시뮬레이터: 수백만 프레임
# - 다양한 날씨, 조명, 시나리오
# 2. 실도로 데이터로 Fine-tuning
# - 실제 주행 데이터: 수천 프레임
# - 어려운 상황 위주
# 3. Domain Randomization
# 시뮬레이션에서 다양한 변형 적용
# → 실도로 일반화 향상
import torch
class DomainAdaptationModel(nn.Module):
def __init__(self, backbone):
super().__init__()
self.backbone = backbone
# Gradient Reversal Layer
# 도메인 분류기가 실패하도록 학습
# → 도메인 불변 특징 학습
self.domain_classifier = nn.Sequential(
GradientReversalLayer(),
nn.Linear(2048, 1024),
nn.ReLU(),
nn.Linear(1024, 2) # sim vs real
)
self.task_classifier = nn.Sequential(
nn.Linear(2048, 1024),
nn.ReLU(),
nn.Linear(1024, num_classes)
)
def forward(self, x, alpha=1.0):
features = self.backbone(x)
domain_pred = self.domain_classifier(features)
task_pred = self.task_classifier(features)
return task_pred, domain_pred실제 사례:
Tesla Autopilot:
- 시뮬레이션 사전학습
- 실주행 데이터 Fine-tuning
- Shadow Mode: 실제 운전 데이터 수집
- 결과: 사고율 90% 감소
Waymo:
- 시뮬레이션 2,000만 마일
- 실도로 2,000만 마일
- Transfer Learning으로 효율화
- 레벨 4 자율주행 달성FAQ: Transfer Learning Q&A
Q1. 언제 Feature Extraction, 언제 Fine-tuning?
A. 데이터 양과 도메인 유사성으로 결정:
데이터 적음 + 도메인 유사:
→ Feature Extraction
예: ImageNet → 일반 물체 분류
데이터 적음 + 도메인 다름:
→ Feature Extraction + 상위 층 Fine-tuning
예: ImageNet → 의료 이미지
데이터 많음 + 도메인 유사:
→ Full Fine-tuning
예: 일반 텍스트 → 뉴스 분류
데이터 많음 + 도메인 다름:
→ 도메인 적응 + Full Fine-tuning
예: 일반 텍스트 → 법률 문서Q2. Learning Rate는 어떻게 설정?
A. 사전학습 모델은 낮게, 새 층은 높게:
일반 규칙:
- 사전학습 층: 1e-5 ~ 1e-4
- 새로 추가된 층: 1e-4 ~ 1e-3
차등 학습률 예시:
optimizer = torch.optim.Adam([
{'params': model.backbone.parameters(), 'lr': 1e-5},
{'params': model.classifier.parameters(), 'lr': 1e-3}
])
이유:
- 사전학습 가중치는 이미 좋음 → 조심스럽게 조정
- 새 층은 랜덤 초기화 → 빠르게 학습 필요Q3. Negative Transfer는 어떻게 방지?
A. 도메인 유사성 확인 및 점진적 접근:
Negative Transfer 원인:
- 소스-타겟 도메인 너무 다름
- 잘못된 사전학습 모델 선택
- 과도한 Fine-tuning
방지 방법:
1. 도메인 유사성 평가
- 데이터 분포 시각화
- 특징 공간 분석
2. 점진적 Fine-tuning
- 먼저 분류기만 학습
- 점진적으로 층 해제
3. 검증 데이터 모니터링
- 성능 하락 시 중단
- Early Stopping 활용
4. 다중 사전학습 모델 비교
- 여러 모델 시도
- 최적 모델 선택Q4. 어떤 사전학습 모델을 선택?
A. 태스크와 도메인에 따라:
이미지 분류:
- 일반: EfficientNet-B3 또는 ResNet-50
- 의료: DenseNet-121 (해석 가능)
- 효율성: MobileNet-V3 (모바일)
- 최고 성능: ViT-L/16
텍스트 분류:
- 영어: RoBERTa-base
- 다국어: mBERT, XLM-R
- 한국어: KoELECTRA
텍스트 생성:
- 일반: GPT-2, LLaMA
- 대화: LLaMA-2-Chat
선택 기준:
1. 도메인 유사성
2. 모델 크기 vs 자원
3. 학습 데이터 양
4. 성능 요구사항최종 정리: Transfer Learning 마스터
핵심 메시지:
✅ Transfer Learning = 거인의 어깨 위에 서기
✅ 100배 적은 데이터로 SOTA 달성 가능
✅ Feature Extraction: 빠름, 적은 데이터
✅ Fine-tuning: 높은 성능, 충분한 데이터
✅ LoRA/QLoRA: 1% 파라미터로 90% 성능
✅ 도메인 유사성이 성공의 핵심
✅ Hugging Face에 50만+ 모델 무료실천 체크리스트:
시작 전:
☑ 태스크 정의 (분류, 생성, 검출 등)
☑ 데이터 양 파악
☑ 도메인 유사성 평가
☑ GPU 자원 확인
모델 선택:
☑ Hugging Face에서 검색
☑ 도메인 관련 모델 우선
☑ 모델 크기 vs 자원 고려
학습 전략:
☑ 데이터 적으면 Feature Extraction
☑ 데이터 많으면 Fine-tuning
☑ GPU 부족하면 LoRA/QLoRA
☑ 차등 학습률 설정
모니터링:
☑ 검증 성능 추적
☑ 과적합 확인
☑ Early Stopping 활용
☑ 최고 모델 저장미래 전망:
2025-2030: Foundation Model 시대
- GPT-5, Gemini 2.0 등 초거대 모델
- 대부분 태스크에 Transfer Learning
- 처음부터 학습은 극소수만
- PEFT 기법 더욱 발전
결론:
"처음부터 학습하는 시대는 끝났다"
"거인의 어깨 위에 서는 것이 표준"외부 참고 자료
Transfer Learning과 Fine-tuning을 더 깊게 배우고 싶다면:
- Hugging Face Model Hub – 50만+ 사전학습 모델
- PyTorch Transfer Learning Tutorial – 공식 튜토리얼
- PEFT Library – LoRA, QLoRA 구현
- Hyperparameter Optimization for Fine-tuning (arXiv) – 학술적 기초
- Fine-Tuning LLMs (Hugging Face) – LLM Fine-tuning 가이드
같이보기
- 하이퍼파라미터 튜닝 하나로 AI 성능 2배 – 같은 모델, 완전히 다른 결과의 비밀
- 특성 공학(Feature Engineering) – AI 성능을 2배 높이는 데이터 변환의 기술
- 모델 학습과 최적화 – AI가 스스로 똑똑해지는 4가지 비밀
- 과적합과 과소적합 – 완벽한 균형으로 AI 성능 2배 높이기
- 과적합 해결 못하면 AI 프로젝트 70% 실패 – AI가 세상 모든 걸 외우면 왜 쓸모가 없을까?
- AI 프로젝트 85% 실패의 진짜 원인 – 데이터 품질과 정제의 모든 것
- MLOps 완벽 가이드: AI 모델이 실험실을 탈출해 세상과 만나는 법 – 배포부터 운영까지 모든 것!
- AI 모델 평가 완벽 가이드: MMLU, HumanEval, MT-Bench! 자동 평가 vs 인간 평가의 진실
- 합성 데이터(Synthetic Data) 완벽 가이드: AI가 AI를 위해 데이터를 만든다! 실제 데이터 부족을 해결하는 마법
