Engineering / Transfer Learning
Transfer Learning with CNNs: A Complete Guide to Fine-Tuning Pretrained Models
Introduction
Training a CNN from scratch on a small dataset is rarely practical. ImageNet, the standard benchmark, has 1.2 million labelled images. A typical medical imaging dataset might have 1,000. A custom defect detection dataset might have 500. Training a 50-layer ResNet on 500 images from scratch would overfit catastrophically — the model would memorise the training set and fail to generalise.
Transfer learning solves this by starting from a model pretrained on a large, general dataset (usually ImageNet) and adapting it to the target task. The intuition is that early CNN layers learn generic features — edges, textures, colour blobs — that are useful for any visual task. Only the later layers, which learn task-specific semantic concepts, need significant adaptation. This dramatically reduces the amount of data and compute required for new tasks.
This guide covers the full transfer learning workflow: choosing a pretrained model, feature extraction vs fine-tuning, gradual unfreezing, discriminative learning rates, domain adaptation, and best practices for production deployment.
Why Transfer Learning Works
Transfer learning works because visual features are hierarchical and reuseable. Early layers in a CNN trained on ImageNet learn Gabor-like filters (oriented edge detectors), colour blobs, and centre-surround patterns — the same low-level visual features that exist in virtually every image dataset. These features are not specific to ImageNet classes; they are fundamental building blocks of visual perception.
Multiple studies have confirmed this empirically. Yosinski et al. (2014) showed that features learned on ImageNet transfer well to other datasets, with the degree of transferability decreasing for higher layers. The first few layers are nearly universally applicable. Middle layers require moderate adaptation. The final layers are highly task-specific and must be retrained.
The practical implication: you can freeze early layers (treating them as fixed feature extractors) and only train later layers, dramatically reducing the number of trainable parameters and the amount of data needed.
Pretrained Model Zoos
Several standard model zoos provide pretrained weights for common architectures:
- Torchvision (PyTorch): Provides pretrained weights for ResNet, DenseNet, VGG, Inception, MobileNet, EfficientNet, and ConvNeXt on ImageNet-1K and ImageNet-21K.
- Keras Applications (TensorFlow): Similar collection with ResNet, EfficientNet, MobileNet, and NASNet variants.
- TIMM (PyTorch Image Models): A comprehensive library with 500+ pretrained models, including modern architectures like ConvNeXt, EfficientNet, and NFNet.
- Hugging Face Hub: Community-contributed models for specific domains (medical imaging, satellite imagery, etc.).
When choosing a pretrained model, consider the pretraining dataset. ImageNet-21K models have seen more diverse data and typically transfer better than ImageNet-1K models, especially for tasks that differ significantly from ImageNet's 1,000 classes.
Feature Extraction vs Fine-Tuning
Two main strategies exist for transfer learning:
Feature Extraction
Freeze the entire pretrained backbone and only train a new classifier head on top. This treats the CNN as a fixed feature extractor, converting images into high-dimensional feature vectors that are then classified by the new head. This approach is fast (only the head has trainable parameters), requires little data (the head typically has 0.5-5M parameters vs 25M+ for the full model), and is resistant to overfitting.
Feature extraction works well when the target dataset is similar to ImageNet (natural images of objects) and when the dataset is very small (fewer than 1,000 images per class).
Fine-Tuning
Unfreeze some or all of the pretrained layers and continue training with a low learning rate. This allows the pretrained features to adapt to the target domain, which is essential when the target domain differs significantly from ImageNet (e.g., medical images, satellite imagery, thermal imaging).
Fine-tuning requires more data and care to avoid overfitting. The standard approach starts with feature extraction (train the new head for several epochs), then unfreezes the last few layers and continues training with a 10-100x lower learning rate.
import torch
import torch.nn as nn
from torchvision import models
# Load pretrained ResNet-50, remove classifier head
backbone = models.resnet50(weights='IMAGENET1K_V2')
num_features = backbone.fc.in_features # 2048
# Freeze all backbone parameters
for param in backbone.parameters():
param.requires_grad = False
# Replace classifier head for new task (5 classes)
backbone.fc = nn.Sequential(
nn.Linear(num_features, 512),
nn.ReLU(inplace=True),
nn.Dropout(0.3),
nn.Linear(512, 5),
)
# Only the new head parameters will be updated
trainable_params = sum(p.numel() for p in backbone.parameters() if p.requires_grad)
total_params = sum(p.numel() for p in backbone.parameters())
print(f'Trainable: {trainable_params:,} / {total_params:,}')
# Output: Trainable: 1,050,629 / 25,557,032 (only 4% trainable)Gradual Unfreezing and Discriminative Learning Rates
Gradual unfreezing is a strategy that progressively exposes more layers to training as training progresses. The intuition: early layers learn generic features that need minimal adaptation, so they should be trained last (and with the lowest learning rate) to avoid destroying the pretrained representations.
A typical schedule:
- Train the new classifier head for 5 epochs (all backbone layers frozen). Learning rate: 1e-3.
- Unfreeze the last residual block (layer4 in ResNet). Train for 5 epochs. LR: 1e-4 for the new block, 1e-4 for the head.
- Unfreeze layer3. Train for 5 epochs with LR 1e-5.
- Optionally unfreeze all layers with a very low LR (1e-6) for final convergence.
Discriminative learning rates assign different learning rates to different layers. Earlier layers get lower rates (they need minimal changes) and later layers get higher rates (they need more adaptation). This technique was popularised by the ULMFiT approach and is now standard in transfer learning workflows.
class GradualUnfreezeTrainer:
def __init__(self, model, stages):
self.model = model
self.stages = stages # [(layer_names, epochs, lr), ...]
def train(self, train_loader, criterion):
for layer_names, epochs, lr in self.stages:
# Unfreeze specified layers
for name, param in self.model.named_parameters():
if any(layer in name for layer in layer_names):
param.requires_grad = True
optimizer = torch.optim.Adam(
filter(lambda p: p.requires_grad, self.model.parameters()),
lr=lr, weight_decay=1e-4
)
for epoch in range(epochs):
for inputs, labels in train_loader:
optimizer.zero_grad()
loss = criterion(self.model(inputs), labels)
loss.backward()
optimizer.step()
print(f'Stage {layer_names}, epoch {epoch+1}: loss={loss:.4f}')
# Usage: train new head -> unfreeze last block -> unfreeze all
trainer = GradualUnfreezeTrainer(model, [
(['fc'], 5, 1e-3), # Train new head
(['layer4', 'fc'], 5, 1e-4), # Unfreeze last conv block
(['layer3'], 5, 1e-5), # Unfreeze earlier block
(['layer2'], 5, 1e-5), # Continue unfreezing
])Domain Adaptation Techniques
When the target domain differs significantly from ImageNet, additional techniques help bridge the gap:
- Input channel adaptation: For grayscale or thermal images, modify the first convolution layer to accept single-channel input by summing the pretrained RGB weights.
- Input resolution adjustment: If your images have different resolution, adjust the first conv layer stride or add interpolation. Many architectures expect 224x224 inputs; you may need to change the pooling or FC layers for different sizes.
- Data augmentation: Domain-specific augmentations that simulate the target domain's variations. For medical images, this might include elastic deformations and intensity shifts. For satellite imagery, rotation augmentation is essential since there is no "up" direction.
- Self-supervised pretraining: If you have unlabelled data from the target domain, pretrain on that data using self-supervised methods (SimCLR, MoCo, DINO) before fine-tuning on labels.
# Example: Thermal image classifier using RGB-pretrained backbone
# 1. Handle input channels (thermal has 1 channel, model expects 3)
original_conv1 = models.resnet50(weights='IMAGENET1K_V2').conv1
# Sum pretrained RGB weights to accept single channel
new_conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)
with torch.no_grad():
new_conv1.weight.data = original_conv1.weight.data.sum(dim=1, keepdim=True)
# 2. Freeze early layers (low-level features transfer well)
model.conv1 = new_conv1
for param in model.conv1.parameters():
param.requires_grad = False
for param in model.bn1.parameters():
param.requires_grad = False
# 3. Train with domain-specific augmentation
from torchvision import transforms
thermal_aug = transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.RandomAffine(degrees=5, translate=(0.05, 0.05)),
transforms.ColorJitter(brightness=0.2, contrast=0.2), # grayscale
transforms.ToTensor(),
])Practical Transfer Learning Recipe
Based on extensive empirical experience, here is a reliable workflow for fine-tuning CNNs:
- Start with a ResNet-50 or EfficientNet-B3 pretrained on ImageNet-21K (not just 1K). These offer a good balance of capacity and transferability.
- Replace the classifier head. Use global average pooling followed by a single FC layer with dropout (p=0.3-0.5). Avoid deep classifiers — they increase overfitting risk.
- Normalise inputs to match ImageNet statistics (mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) unless your data is fundamentally different (e.g., grayscale).
- Train only the head for 5-10 epochs with a learning rate of 1e-3. Use Adam or SGD with momentum. Monitor validation loss to detect overfitting.
- Unfreeze the last 1-2 blocks. Reduce the learning rate to 1e-4 to 1e-5. Train for 10-20 epochs with early stopping. Use cosine learning rate decay.
- If performance plateaus, unfreeze more layers with an even lower LR (1e-5 to 1e-6). Be aggressive with early stopping — fine-tuning too many layers for too long causes catastrophic forgetting.
- Evaluate on a held-out test set. If the gap between validation and test accuracy is more than 2%, you are overfitting — add regularisation or reduce the number of unfrozen layers.
Common Mistakes and Pitfalls
- Using too high a learning rate: The most common mistake. Pretrained features are fragile — a learning rate above 1e-4 for unfrozen layers can destroy them within a few iterations. Always use a lower LR for pretrained layers than for the new head.
- Not freezing batch norm: If you freeze convolution layers but leave batch norm unfrozen, the running statistics will be corrupted by your small dataset. Freeze batch norm layers along with convolutions, or keep them in train mode with a small momentum.
- Catastrophic forgetting: When fine-tuning too aggressively, the model forgets the general features learned during pretraining. The symptoms: training loss decreases but validation loss increases. The fix: reduce the learning rate, freeze more layers, or add a feature space regularisation loss.
- Incorrect input preprocessing: Using the wrong mean/std normalisation or forgetting to resize inputs to the expected size. Always verify that your preprocessing pipeline matches what the pretrained model expects.
- Training the new head for too few epochs: The random initialisation of the new head produces noisy gradients that can corrupt the pretrained backbone even when it is frozen. Train the head until convergence before unfreezing.
Conclusion
Transfer learning is one of the most practical and impactful techniques in modern computer vision. It reduces the data requirements for new visual tasks by orders of magnitude, enables individuals and small teams to build production-quality vision systems, and is supported by an extensive ecosystem of pretrained models and libraries.
The key to successful transfer learning lies in understanding the trade-off between adaptation and forgetting. Too little adaptation (pure feature extraction) may miss task-specific patterns. Too much adaptation (full fine-tuning with high LR) destroys the pretrained features. The optimal strategy — gradual unfreezing with discriminative learning rates — navigates this trade-off by progressively exposing more layers to training with appropriately scaled learning rates.
For teams deploying vision models in production, we offer infrastructure that handles model serving, scaling, and monitoring. For a deeper understanding of CNN architecture, see our guide on CNN architecture from the ground up.
References
- Yosinski, J., et al. "How transferable are features in deep neural networks?" NeurIPS 2014. arXiv:1411.1792
- Howard, J. and Ruder, S. "Universal Language Model Fine-tuning for Text Classification." ACL 2018. arXiv:1801.06146
- Tan, C., et al. "A Survey on Deep Transfer Learning." ICANN 2018. Springer
- Kornblith, S., et al. "Do Better ImageNet Models Transfer Better?" CVPR 2019. arXiv:1805.08974
- He, K., et al. "Deep Residual Learning for Image Recognition." CVPR 2016. arXiv:1512.03385
- Chen, T., et al. "A Simple Framework for Contrastive Learning of Visual Representations." ICML 2020. arXiv:2002.05709
- Caron, M., et al. "Emerging Properties in Self-Supervised Vision Transformers." ICCV 2021. arXiv:2104.14294
- He, K., et al. "Momentum Contrast for Unsupervised Visual Representation Learning." CVPR 2020. arXiv:1911.05722
- Zhuang, F., et al. "A Comprehensive Survey on Transfer Learning." IEEE 2021. IEEE
- Smith, L. "Cyclical Learning Rates for Training Neural Networks." WACV 2017. arXiv:1506.01186