07. ์ ์ดํ์ต (Transfer Learning)
07. ์ ์ดํ์ต (Transfer Learning)¶
ํ์ต ๋ชฉํ¶
- ์ ์ดํ์ต์ ๊ฐ๋ ๊ณผ ์ด์
- ์ฌ์ ํ์ต ๋ชจ๋ธ ํ์ฉ
- ๋ฏธ์ธ ์กฐ์ (Fine-tuning) ์ ๋ต
- ์ค์ ์ด๋ฏธ์ง ๋ถ๋ฅ ํ๋ก์ ํธ
1. ์ ์ดํ์ต์ด๋?¶
๊ฐ๋ ¶
ImageNet์ผ๋ก ํ์ต๋ ๋ชจ๋ธ
โ
์ ์์ค ํน์ง (์์ง, ํ
์ค์ฒ) โ ์ฌ์ฌ์ฉ
โ
๊ณ ์์ค ํน์ง โ ์ ๋ฐ์ดํฐ์ ๋ง๊ฒ ์กฐ์
โ
์๋ก์ด ๋ถ๋ฅ ์์
์ด์ ¶
- ์ ์ ๋ฐ์ดํฐ๋ก๋ ๋์ ์ฑ๋ฅ
- ๋น ๋ฅธ ํ์ต
- ๋ ๋์ ์ผ๋ฐํ
2. ์ ์ดํ์ต ์ ๋ต¶
์ ๋ต 1: ํน์ฑ ์ถ์ถ (Feature Extraction)¶
# ์ฌ์ ํ์ต ๋ชจ๋ธ์ ๊ฐ์ค์น ๊ณ ์
for param in model.parameters():
param.requires_grad = False
# ๋ง์ง๋ง ์ธต๋ง ๊ต์ฒด
model.fc = nn.Linear(2048, num_classes)
- ์ฌ์ ํ์ต๋ ํน์ง ๊ทธ๋๋ก ์ฌ์ฉ
- ๋ง์ง๋ง ๋ถ๋ฅ์ธต๋ง ํ์ต
- ๋ฐ์ดํฐ๊ฐ ์ ์ ๋ ์ ํฉ
์ ๋ต 2: ๋ฏธ์ธ ์กฐ์ (Fine-tuning)¶
# ์ ์ฒด ๋๋ ์ผ๋ถ ์ธต ํ์ต
for param in model.parameters():
param.requires_grad = True
# ๋ฎ์ ํ์ต๋ฅ ์ฌ์ฉ
optimizer = torch.optim.Adam(model.parameters(), lr=1e-5)
- ์ฌ์ ํ์ต ๊ฐ์ค์น๋ฅผ ์์์ ์ผ๋ก
- ์ ์ฒด ๋คํธ์ํฌ ๋ฏธ์ธ ์กฐ์
- ๋ฐ์ดํฐ๊ฐ ์ถฉ๋ถํ ๋ ์ ํฉ
์ ๋ต 3: ์ ์ง์ ํด๋ (Gradual Unfreezing)¶
# 1๋จ๊ณ: ๋ง์ง๋ง ์ธต๋ง
for param in model.parameters():
param.requires_grad = False
model.fc.requires_grad_(True)
train_for_epochs(5)
# 2๋จ๊ณ: ๋ง์ง๋ง ๋ธ๋ก๋
model.layer4.requires_grad_(True)
train_for_epochs(5)
# 3๋จ๊ณ: ์ ์ฒด
model.requires_grad_(True)
train_for_epochs(10)
3. PyTorch ๊ตฌํ¶
๊ธฐ๋ณธ ์ ์ดํ์ต¶
import torch
import torch.nn as nn
import torchvision.models as models
from torchvision import transforms, datasets
# 1. ์ฌ์ ํ์ต ๋ชจ๋ธ ๋ก๋
model = models.resnet50(weights='IMAGENET1K_V2')
# 2. ํน์ฑ ์ถ์ถ๊ธฐ๋ก ์ฌ์ฉ (๊ฐ์ค์น ๊ณ ์ )
for param in model.parameters():
param.requires_grad = False
# 3. ๋ง์ง๋ง ์ธต ๊ต์ฒด
num_features = model.fc.in_features
model.fc = nn.Sequential(
nn.Dropout(0.5),
nn.Linear(num_features, 256),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(256, num_classes)
)
๋ฐ์ดํฐ ์ ์ฒ๋ฆฌ¶
# ImageNet ์ ๊ทํ ์ฌ์ฉ
normalize = transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
normalize
])
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
normalize
])
4. ํ์ต ์ ๋ต¶
์ฐจ๋ฑ ํ์ต๋ฅ (Discriminative Learning Rates)¶
# ์ธต๋ณ ๋ค๋ฅธ ํ์ต๋ฅ
optimizer = torch.optim.Adam([
{'params': model.layer1.parameters(), 'lr': 1e-5},
{'params': model.layer2.parameters(), 'lr': 5e-5},
{'params': model.layer3.parameters(), 'lr': 1e-4},
{'params': model.layer4.parameters(), 'lr': 5e-4},
{'params': model.fc.parameters(), 'lr': 1e-3},
])
ํ์ต๋ฅ ์ค์ผ์ค๋ง¶
# Warmup + Cosine Decay
scheduler = torch.optim.lr_scheduler.OneCycleLR(
optimizer,
max_lr=1e-3,
epochs=epochs,
steps_per_epoch=len(train_loader),
pct_start=0.1 # 10% warmup
)
5. ๋ค์ํ ์ฌ์ ํ์ต ๋ชจ๋ธ¶
torchvision ๋ชจ๋ธ¶
# ๋ถ๋ฅ์ฉ
resnet50 = models.resnet50(weights='IMAGENET1K_V2')
efficientnet = models.efficientnet_b0(weights='IMAGENET1K_V1')
vit = models.vit_b_16(weights='IMAGENET1K_V1')
# ๊ฐ์ฒด ๊ฒ์ถ์ฉ
fasterrcnn = models.detection.fasterrcnn_resnet50_fpn(weights='DEFAULT')
# ์ธ๊ทธ๋ฉํ
์ด์
์ฉ
deeplabv3 = models.segmentation.deeplabv3_resnet50(weights='DEFAULT')
timm ๋ผ์ด๋ธ๋ฌ๋ฆฌ¶
import timm
# ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ธ ํ์ธ
print(timm.list_models('*efficientnet*'))
# ๋ชจ๋ธ ๋ก๋
model = timm.create_model('efficientnet_b0', pretrained=True, num_classes=10)
6. ์ค์ ํ๋ก์ ํธ: ๊ฝ ๋ถ๋ฅ¶
๋ฐ์ดํฐ ์ค๋น¶
# Flowers102 ๋ฐ์ดํฐ์
from torchvision.datasets import Flowers102
train_data = Flowers102(
root='data',
split='train',
transform=train_transform,
download=True
)
test_data = Flowers102(
root='data',
split='test',
transform=val_transform
)
๋ชจ๋ธ ๋ฐ ํ์ต¶
class FlowerClassifier(nn.Module):
def __init__(self, num_classes=102):
super().__init__()
self.backbone = models.efficientnet_b0(weights='IMAGENET1K_V1')
# ๋ง์ง๋ง ์ธต ๊ต์ฒด
in_features = self.backbone.classifier[1].in_features
self.backbone.classifier = nn.Sequential(
nn.Dropout(0.3),
nn.Linear(in_features, num_classes)
)
def forward(self, x):
return self.backbone(x)
# ํ์ต
model = FlowerClassifier()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
7. ์ฃผ์์ฌํญ¶
๋ฐ์ดํฐ ํฌ๊ธฐ๋ณ ์ ๋ต¶
| ๋ฐ์ดํฐ ํฌ๊ธฐ | ์ ๋ต | ์ค๋ช |
|---|---|---|
| ๋งค์ฐ ์ ์ (<1000) | ํน์ฑ ์ถ์ถ | ๋ง์ง๋ง ์ธต๋ง ํ์ต |
| ์ ์ (1000-10000) | ์ ์ง์ ํด๋ | ํ๋ฐ ์ธต๋ถํฐ ํด๋ |
| ๋ณดํต (10000+) | ์ ์ฒด ๋ฏธ์ธ ์กฐ์ | ๋ฎ์ ํ์ต๋ฅ ๋ก ์ ์ฒด ํ์ต |
๋๋ฉ์ธ ์ ์ฌ์ฑ¶
ImageNet๊ณผ ์ ์ฌ (๋๋ฌผ, ์ฌ๋ฌผ):
โ ์์ ์ธต๋ ๊ทธ๋๋ก ์ฌ์ฉ ๊ฐ๋ฅ
ImageNet๊ณผ ๋ค๋ฆ (์๋ฃ, ์์ฑ):
โ ๊น์ ์ธต๊น์ง ๋ฏธ์ธ ์กฐ์ ํ์
์ผ๋ฐ์ ์ธ ์ค์¶
- ImageNet ์ ๊ทํ ๋๋ฝ
- ๋๋ฌด ๋์ ํ์ต๋ฅ
- ํ๋ จ/ํ๊ฐ ๋ชจ๋ ์ ํ ์์
- ๊ฐ์ค์น ๊ณ ์ ํ optimizer์ ํฌํจ
8. ์ฑ๋ฅ ํฅ์ ํ¶
๋ฐ์ดํฐ ์ฆ๊ฐ¶
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.RandomRotation(15),
transforms.ColorJitter(0.2, 0.2, 0.2, 0.1),
transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),
transforms.ToTensor(),
normalize
])
Label Smoothing¶
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
Mixup / CutMix¶
def mixup(x, y, alpha=0.2):
lam = np.random.beta(alpha, alpha)
idx = torch.randperm(x.size(0))
mixed_x = lam * x + (1 - lam) * x[idx]
y_a, y_b = y, y[idx]
return mixed_x, y_a, y_b, lam
์ ๋ฆฌ¶
ํต์ฌ ๊ฐ๋ ¶
- ํน์ฑ ์ถ์ถ: ์ฌ์ ํ์ต ํน์ง ์ฌ์ฌ์ฉ
- ๋ฏธ์ธ ์กฐ์ : ๋ฎ์ ํ์ต๋ฅ ๋ก ์ ์ฒด ์กฐ์
- ์ ์ง์ ํด๋: ํ๋ฐ ์ธต๋ถํฐ ์์ฐจ์ ํ์ต
์ฒดํฌ๋ฆฌ์คํธ¶
- [ ] ImageNet ์ ๊ทํ ์ฌ์ฉ
- [ ] ์ ์ ํ ํ์ต๋ฅ ์ ํ (1e-4 ~ 1e-5)
- [ ] model.train() / model.eval() ์ ํ
- [ ] ๋ฐ์ดํฐ ์ฆ๊ฐ ์ ์ฉ
- [ ] ์กฐ๊ธฐ ์ข ๋ฃ ์ค์
๋ค์ ๋จ๊ณ¶
13_RNN_Basics.md์์ ์ํ ์ ๊ฒฝ๋ง์ ํ์ตํฉ๋๋ค.