미세 조정: 계층 해제와 낮은 학습률
학습자는 초기 헤드 학습 후 이전 계층의 고정을 해제하고, 사전 학습된 특성이 손상되지 않도록 더 낮은 학습률을 적용하며 정확도 향상을 관찰합니다.
미세 조정: 계층 해제와 낮은 학습률은(는) CoddyKit의 무료 Machine Learning Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Machine Learning Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Machine Learning Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why Fine-Tune After Feature Extraction?
Feature extraction adapts only the classification head, leaving the backbone frozen. Fine-tuning goes further by also updating some or all backbone layers, allowing the model to adapt its representations to your specific data distribution.
Fine-tuning is most beneficial when your domain differs from ImageNet — for example, satellite images, medical scans, or industrial defect photos. The ImageNet features partially transfer, but adapting them yields measurably higher accuracy. The risk is catastrophic forgetting: if you fine-tune with a high learning rate, the new data overwrites the carefully pre-learned features, causing performance to collapse.
The Two-Phase Fine-Tuning Strategy
The standard fine-tuning recipe has two phases. Phase 1: Freeze the backbone completely and train only the new classification head for several epochs until it converges. This ensures the head starts from a reasonable state before backbone gradients mix in.
Phase 2: Unfreeze all or some backbone layers and continue training with a very low learning rate (typically 10–100× smaller than phase 1). This gently nudges the pre-learned features toward your domain without destroying them. Skipping phase 1 and fine-tuning from the start with a high LR is the most common mistake that causes poor results.
import torchvision.models as models
import torch.nn as nn
import torch.optim as optim
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
# --- Phase 1: Freeze backbone, train head ---
for param in model.parameters():
param.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, num_classes)
optimizer = optim.Adam(model.fc.parameters(), lr=1e-3)
# Train for 5-10 epochs...
# --- Phase 2: Unfreeze and fine-tune with low LR ---
for param in model.parameters():
param.requires_grad = True
optimizer = optim.Adam(model.parameters(), lr=1e-5) # 100x lowerSelective Unfreezing: Layer-by-Layer
Rather than unfreezing the entire backbone at once, you can unfreeze it layer by layer starting from the top (closest to the output). This is because later layers contain the most task-specific features, while early layers learn universal features (edges, textures) that rarely need updating.
For ResNet-50, the typical selective unfreezing order is: (1) fc (already trainable), (2) layer4, (3) layer3, and finally (4) layer1 and layer2 if needed. Each step requires evaluating whether the additional unfreezing improves validation accuracy, since more trainable parameters increase overfitting risk on small datasets.
import torchvision.models as models
import torch.nn as nn
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
# Freeze everything first
for param in model.parameters():
param.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, num_classes)
# After phase 1 training, selectively unfreeze layer4
for param in model.layer4.parameters():
param.requires_grad = True
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f'Trainable (fc + layer4): {trainable:,}')
# Much fewer parameters to fine-tune than the full 25MDifferential Learning Rates
Differential learning rates assign different learning rates to different parts of the network. The new head trains with the highest rate (1e-3), recently unfrozen backbone layers with a moderate rate (1e-4), and early backbone layers with the lowest rate (1e-5 or frozen entirely).
PyTorch's optimiser accepts a list of parameter groups, each with its own lr. This is the standard technique in transfer learning literature and is used in fast.ai's discriminative learning rates. The intuition: parts of the network with more general features need fewer adjustments than task-specific layers.
import torch.optim as optim
import torchvision.models as models
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
model.fc = nn.Linear(model.fc.in_features, num_classes)
# Unfreeze everything
for p in model.parameters():
p.requires_grad = True
# Differential learning rates per layer group
optimizer = optim.Adam([
{'params': model.fc.parameters(), 'lr': 1e-3}, # Head: highest LR
{'params': model.layer4.parameters(), 'lr': 1e-4}, # Top block
{'params': model.layer3.parameters(), 'lr': 5e-5}, # Middle
{'params': list(model.layer1.parameters()) +
list(model.layer2.parameters()), 'lr': 1e-5} # Early layers
])Monitoring for Catastrophic Forgetting
Catastrophic forgetting occurs when fine-tuning with a large learning rate overwrites the pre-learned features, causing validation accuracy to drop below even the feature-extraction baseline. You will see it as a sharp spike in training loss early in phase 2, followed by slow recovery.
To prevent it: (1) always start phase 2 with a very low learning rate, (2) monitor validation accuracy every epoch and stop immediately if it drops below the phase 1 best, (3) use a learning rate scheduler that gradually warms up the backbone LR, and (4) save the best model checkpoint during phase 1 so you can restore it if phase 2 fails.
import torch
best_val_acc = 0.0
best_state = None
for epoch in range(20):
train_one_epoch(model, train_loader, optimizer, criterion, device)
val_acc = evaluate(model, val_loader, device)
if val_acc > best_val_acc:
best_val_acc = val_acc
best_state = {k: v.clone() for k, v in model.state_dict().items()}
# Stop if validation drops more than 2% below best
if val_acc < best_val_acc - 0.02:
print('Early stop: possible catastrophic forgetting')
model.load_state_dict(best_state) # Restore best
breakLearning Rate Schedulers for Fine-Tuning
A fixed learning rate is rarely optimal throughout fine-tuning. Learning rate schedulers adjust the LR during training to improve convergence. For fine-tuning, two schedulers work especially well: CosineAnnealingLR smoothly decays the LR from initial value to near zero following a cosine curve, and ReduceLROnPlateau reduces the LR whenever validation metric stops improving.
ReduceLROnPlateau with patience=2 is a safe default: if validation loss does not improve for 2 consecutive epochs, the LR is multiplied by factor=0.1. This often recovers progress when training plateaus.
import torch.optim as optim
from torch.optim.lr_scheduler import ReduceLROnPlateau, CosineAnnealingLR
optimizer = optim.Adam(model.parameters(), lr=1e-4)
# Option 1: Reduce on plateau
scheduler = ReduceLROnPlateau(optimizer, mode='max', factor=0.1,
patience=2, verbose=True)
# Call after each validation: scheduler.step(val_acc)
# Option 2: Cosine annealing over T_max epochs
scheduler = CosineAnnealingLR(optimizer, T_max=20, eta_min=1e-7)
# Call after each epoch: scheduler.step()
print('Initial LR:', optimizer.param_groups[0]['lr'])Batch Size and Regularisation During Fine-Tuning
Fine-tuning with a larger batch size reduces gradient noise, which is beneficial when making small adjustments to pre-learned features. However, very large batches can degrade generalisation for fine-tuning (the 'sharp minima' problem). A batch size of 32-64 is a good starting point.
Add weight decay (L2 regularisation) to the optimiser: optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-4). Weight decay prevents any single weight from growing too large, which is especially important for fine-tuning where the pre-trained weights already have sensible magnitudes that we do not want to perturb too aggressively.
import torch.optim as optim
# Fine-tuning optimiser with weight decay
optimizer = optim.AdamW(
model.parameters(),
lr=1e-4,
weight_decay=1e-4 # L2 regularisation
)
# AdamW separates weight decay from gradient adaptation
# (standard Adam incorrectly applies decay to adaptive gradient scaling)
# AdamW is preferred for fine-tuning transformer models especially
print('Optimizer:', optimizer)Data Augmentation During Fine-Tuning
Data augmentation applies random transformations to training images at each epoch, effectively multiplying the dataset size. This is especially important during fine-tuning when you have few images per class. Common augmentations for natural images: random horizontal flip, random rotation (±15°), random crop, colour jitter (brightness, contrast, saturation).
Apply augmentation only during training, not during validation or inference. Use a separate transform for the validation set that only applies deterministic preprocessing (resize, centre crop, normalise). PyTorch's transforms.Compose makes this easy to manage.
from torchvision import transforms
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
transforms.RandomRotation(degrees=15),
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])
])Practical Fine-Tuning Results
How much improvement does fine-tuning give over feature extraction? On a typical custom dataset with ~1000 images per class, feature extraction might yield 88% accuracy while fine-tuning reaches 92-95%. The improvement depends heavily on how different your data is from ImageNet.
Track these four numbers to understand what is happening: phase 1 val accuracy (feature extraction baseline), phase 2 initial val accuracy (should not drop below phase 1 if LR is correct), phase 2 best val accuracy (fine-tuning benefit), and test accuracy (final, reported only once at the very end to avoid test set leakage).
# Example fine-tuning progression
results = {
'Phase 1 (frozen backbone, 10 epochs)': '88.2%',
'Phase 2 initial (unfroze layer4, epoch 1)': '88.5%',
'Phase 2 best (epoch 15)': '92.7%',
'Phase 2 (unfroze layer3 too, epoch 20)': '93.1%',
'Final test accuracy': '92.8%', # Reported only at the end
}
for stage, acc in results.items():
print(f'{stage}: {acc}')
# Key takeaway: fine-tuning added ~4.5% over feature extractionFine-Tuning ViT vs CNN Backbones
Fine-tuning ViT (Vision Transformer) models requires some additional care compared to CNNs. ViT models contain Layer Normalisation rather than Batch Normalisation, so the model.eval() / model.train() modes primarily affect dropout rather than normalisation statistics — this makes fine-tuning slightly simpler.
ViT models also benefit from a lower peak learning rate during fine-tuning (1e-5 to 5e-5 vs 1e-4 for CNNs) because the attention weights are sensitive to large updates. The AdamW optimiser is strongly preferred for ViT fine-tuning. With careful fine-tuning, ViT-B/16 can surpass CNN baselines on many tasks.
import torchvision.models as models
import torch.nn as nn
import torch.optim as optim
# Fine-tuning ViT-B/16
vit = models.vit_b_16(weights=models.ViT_B_16_Weights.IMAGENET1K_V1)
vit.heads = nn.Linear(768, num_classes)
# Very low LR for ViT fine-tuning
optimizer = optim.AdamW(
vit.parameters(),
lr=2e-5,
weight_decay=0.01
)
# Warm-up scheduler (common for transformers)
from torch.optim.lr_scheduler import LinearLR, CosineAnnealingLR, SequentialLR
warmup = LinearLR(optimizer, start_factor=0.01, total_iters=5)
cosine = CosineAnnealingLR(optimizer, T_max=25)
scheduler = SequentialLR(optimizer, schedulers=[warmup, cosine], milestones=[5])Quick Check
Test your understanding of fine-tuning strategies from this lesson.
Lesson Recap
In this lesson you learned: two-phase fine-tuning trains the head first (frozen backbone), then unfreezes the backbone with a very low learning rate to avoid catastrophic forgetting, differential learning rates assign higher rates to the head and lower rates to deeper backbone layers, and selective unfreezing from the top layers down gives the best accuracy-efficiency trade-off. Next up we apply these techniques to a real medical imaging challenge with scarce labels.
자주 묻는 질문
“미세 조정: 계층 해제와 낮은 학습률” 강의는 무료인가요?
네 — “미세 조정: 계층 해제와 낮은 학습률” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Machine Learning Academy 강의 전체를 잠금 해제할 수 있습니다. Machine Learning Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“미세 조정: 계층 해제와 낮은 학습률”에서 뭘 배우나요?
학습자는 초기 헤드 학습 후 이전 계층의 고정을 해제하고, 사전 학습된 특성이 손상되지 않도록 더 낮은 학습률을 적용하며 정확도 향상을 관찰합니다. 브라우저에서 직접 실행하는 실습 코드로 Machine Learning Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Machine Learning Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Machine Learning Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“미세 조정: 계층 해제와 낮은 학습률” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Machine Learning Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Machine Learning Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- torchvision의 사전 학습 모델: ResNet, EfficientNet 및 ViT
- 특성 추출: 백본 고정하기
- 미세 조정: 계층 해제와 낮은 학습률
- 도메인 적응: 레이블이 부족한 의료 영상