Training BERT from scratch costs $7M. Fine-tuning it on your task costs $20. Transfer learning is the difference between writing a research paper and shipping a product. Take a model someone else trained on the entire internet, replace the last layer with your task head, train for a few hours on a thousand examples — and you've got state-of-the-art results for the price of a pizza.
Learning Objectives
After this lesson, you will be able to:
Understand why pretrained models already know useful things and how that knowledge transfers to new tasks
Distinguish feature extraction (frozen backbone) from fine-tuning (unfrozen weights) and explain when each is appropriate
Apply LoRA and explain why it fine-tunes large models with a fraction of the memory and compute of full fine-tuning
Pick the right transfer learning strategy given dataset size, domain similarity, and compute budget
Transfer learning is arguably the most practically important technique in this entire track. Almost nobody trains from scratch anymore. If you learn one thing from this lesson, let it be this: start with a pretrained model. Always. You will save days of work and get better results.
Start with a model already trained on a massive dataset (e.g., ResNet-50 trained on 1.2 million ImageNet images across 1,000 classes). This model has learned a rich hierarchy of visual features: edges, textures, shapes, object parts, and full objects. These features represent months of GPU compute distilled into a set of weights.
The early layers (edge detectors, texture recognizers) are universal -- they transfer to almost any vision task. Freeze these layers by setting requires_grad=False. Their weights will not be updated during fine-tuning. This preserves the general visual knowledge and prevents overfitting when your dataset is small.
The original output layer classifies ImageNet's 1,000 classes -- not useful for your task. Remove it and attach a new classification head matching your number of classes (e.g., 5 categories for medical imaging). This new layer starts with random weights and will learn from scratch during fine-tuning.
Try it! If you have Python and PyTorch installed, load a pretrained ResNet in 3 lines: import torchvision; model = torchvision.models.resnet18(pretrained=True); print(sum(p.numel() for p in model.parameters())). You just loaded 11 million parameters of learned visual knowledge. Now imagine training those from scratch -- transfer learning saved you weeks of GPU time.
Late layers (layers 8+): Class-specific detectors -- most task-specific
When you transfer to a new task (e.g., classifying medical X-rays), the early layers' edge and texture detectors are immediately useful. The middle layers may need slight adjustment. Only the late layers need significant retraining.
Different layers need different learning rates. Early layers contain general features that should change minimally; later layers contain task-specific features that need more adaptation:
Full fine-tuning a 7B-parameter LLM requires storing 7B gradients and optimizer states -- ~100GB of GPU memory. PEFT methods fine-tune only a small fraction of parameters, dramatically reducing memory and compute.
Combine LoRA with 4-bit quantization of the base model. Fine-tune a 65B parameter model on a single 48GB GPU. This democratized LLM fine-tuning for individual researchers and small companies.
The qualitative pitch above -- "LoRA trains 0.1% of the parameters" -- is the marketing line. The actual math is short, elegant, and worth knowing if you ever need to debug a fine-tune, pick a rank, or estimate GPU memory. This section unpacks Hu et al. (2021) end to end.
A pretrained model is a stack of weight matrices. For any single matrix W in R^{d_out x d_in} -- an attention projection, an MLP, an embedding -- fine-tuning replaces it with W + ΔW. Full fine-tuning learns every entry of ΔW directly, which means d_out * d_in new parameters per layer.
The Hu et al. (2021) hypothesis is that ΔW has low intrinsic rank: most of the meaningful change concentrates in a handful of directions, not the full d_out x d_in matrix. Pretraining has already built a dense, near-complete feature basis; fine-tuning mostly rotates and amplifies a few directions in that basis. If that hypothesis holds, we never have to materialize the full ΔW -- we only need a thin slice of it.
Apply this across every attention projection in every layer and the cumulative savings become the headline "train 0.1% of params" number you see in blog posts.
This is the single most important practical detail. Many LoRA-like methods that initialize both matrices randomly suffer an initial loss spike because the model is no longer the pretrained model on step 0.
At inference time you have two equivalent options:
W′=W+rαBA
Keep separate (forward = Wx + (alpha/r)(B(Ax))): two extra small matmuls per layer, but the same base model can serve many different LoRA adapters -- one for code, one for legal, one per customer. This is how LoRAX, S-LoRA, and vLLM's multi-LoRA server work.
Merge once (W_merged = W + (alpha/r) BA): identical FLOPs to the original pretrained model after the one-time merge. Pay zero inference cost, lose hot-swap.
The "0.1% of params" headline undersells LoRA's actual win. The dominant memory cost during training is not parameters -- it is optimizer state. AdamW maintains two fp32 moment buffers per trainable parameter (m and v), which is 2 x 4 = 8 bytes per param, on top of the 4-byte param and 4-byte gradient. For a 7B model in mixed precision:
Component
Full fine-tune (7B)
LoRA r=8
Frozen base weights (bf16)
14 GB
14 GB
Trainable params (fp32 master)
28 GB
~16 MB
Gradients (fp32)
28 GB
~16 MB
AdamW m, v state (fp32, 2x)
56 GB
~32 MB
Activations (per batch)
~10-30 GB
~10-30 GB
Total optimizer overhead
~112 GB
~0.06 GB
The base weights are frozen, so they need no optimizer state, no gradient buffer, no fp32 master copy. That is where the 100x training-memory reduction comes from -- not the headline parameter count.
QLoRA (Dettmers et al. 2023) pushes this further by storing the frozen base in NF4 (a 4-bit data type optimized for normally-distributed weights) and double-quantizing the quantization constants themselves. The LoRA adapters stay in bf16 because they actually receive gradients. The combination:
7B base: 14 GB bf16 -> ~3.5 GB NF4
65B base: 130 GB bf16 -> ~33 GB NF4 -> fits on a single 48GB A6000 with room for adapters and activations
Accuracy cost: roughly 1% degradation versus full bf16 fine-tuning on GLUE/MMLU
QLoRA is the standard recipe today for fine-tuning anything above 30B on consumer or single-GPU server hardware.
DoRA (Liu et al. 2024) noticed that a learned weight update has two parts: it changes the magnitude of each row and it rotates each row's direction. The decomposition is:
W = m * (V / ||V||_c) -- a per-column magnitude scalar m times a unit-norm direction.
DoRA freezes the magnitudes (or learns them separately) and applies LoRA only to the direction matrix V. Empirically this matches full fine-tune on tasks where vanilla LoRA leaves a couple of points on the table. Same parameter budget, slightly better quality, marginally more compute per step.
Hu et al. (2021) swept r in {1, 2, 4, 8, 16, 32, 64} on GLUE (text classification) and E2E NLG (text generation) and found a striking result: r=8 already captures 98%+ of full fine-tune performance, and going higher gives diminishing returns. The intuition lines up with the IntuitionBox above: pretrained representations are already near-complete; fine-tuning amplifies and rotates a small number of existing directions rather than building new ones. Common picks today:
r=4 or r=8: classification, simple instruction tuning
r=16: general chat fine-tuning
r=32 to r=64: domain shifts (code, math, scientific reasoning) where the task is meaningfully different from pretraining
alpha = 2*r: a common heuristic so that alpha/r = 2
Loading visualization...
What Do You Think?
Fine-tune Llama-3-8B with LoRA r=16 on every attention projection. The model has 32 transformer layers, and each layer has 4 attention matrices (Q, K, V, O) of shape 4096x4096. How many trainable LoRA parameters total?
Select a model pretrained on a task similar to yours. For vision: ResNet, EfficientNet, or ViT pretrained on ImageNet. For text: BERT, RoBERTa, or LLaMA. For code: CodeLlama or StarCoder. The closer the pretraining data is to your task, the better transfer works.
How large is your labeled dataset? How similar is it to the pretraining data? Small + similar = feature extraction. Large + different = full fine-tuning. Everything in between = LoRA.
Replace the output head to match your task (number of classes, regression vs. classification). Decide which layers to freeze. Set layer-specific learning rates if using discriminative fine-tuning.
Use a small learning rate (10-100x smaller than training from scratch). Monitor validation loss for early stopping. Use learning rate warmup for the first 5-10% of training. Track both task performance and general capability (if applicable).
Pretrained models contain reusable knowledge. Features learned on large datasets (ImageNet, Common Crawl) transfer to new tasks because early layers learn universal patterns (edges, syntax) that apply broadly
Fine-tuning adapts a pretrained model to your specific task. Freeze early layers (universal features), unfreeze later layers (task-specific features), and train on your smaller dataset with a low learning rate
LoRA and adapters enable parameter-efficient fine-tuning. Instead of updating all weights, these methods learn small low-rank updates, reducing memory and compute by 10-100x while maintaining most of the performance
Dataset size and domain similarity determine the strategy. Small dataset + similar domain: feature extraction (freeze everything); large dataset + different domain: full fine-tuning with low learning rate; limited compute: LoRA
Why does transfer learning work -- what makes pretrained features reusable?
Transfer learning is the most practical technique in modern deep learning. Next: Practical Deep Learning -- a decision framework for when to use deep learning vs. classical ML, and how to avoid the common trap of reaching for a neural network when a simpler model would do.
Train the modified model on your labeled dataset. The frozen early layers act as a fixed feature extractor. The new head and optionally unfrozen later layers adapt to your specific task. Use a small learning rate (10-100x smaller than training from scratch) to avoid destroying the pretrained knowledge.
The result is a model that combines the general visual understanding from ImageNet with task-specific knowledge from your data. It achieves far better accuracy than training from scratch, especially when your dataset is small (100-1,000 examples). The pretrained features provide a powerful starting point that months of training from scratch could not match.
Test on held-out data. Check for catastrophic forgetting. Compare against a from-scratch baseline to quantify the transfer learning benefit. In practice, transfer learning typically improves performance by 5-30% over training from scratch, with the gap larger for smaller datasets.