Classification Finetuning: Head Surgery, Freezing & Last-Token Pooling
How are generative autoregressive transformers repurposed into high-precision discrete classifiers through linear head surgery and selective parameter freezing?
How this lesson unfolds.
While text generation produces open-ended language, many enterprise applications require discrete categorization: spam detection, sentiment analysis, toxic content moderation, and intent classification. In this chapter, we adapt our generative transformer into an industrial-strength classification engine.
How does a generative decoder-only transformer adapt to predict discrete class labels without modifying the underlying self-attention engine?
Imagine hiring a master novelist who has read 100,000 books. Instead of asking them to write another chapter, you ask them to read an email and tell you: 'Is this phishing, or is it legitimate work?' The novelist uses their immense understanding of human language and nuance to give a simple, definitive yes-or-no verdict.
- $$\mathbf{z}_{cls} = \mathbf{h}_{last} \mathbf{W}_{cls} + \mathbf{b}_{cls} \in \mathbb{R}^C$$
- Learning goalFormulate the architectural replacement of the vocabulary projection head with a C-dimensional classification head in PyTorch.
- Helpful foundationsPretrained transformer hidden states · Linear projection heads and softmax classification · Improving Language Understanding by Generative Pre-Training (Radford et al., 2018)
- Common trapThinking decoder-only models cannot classify text; because causal attention pools all previous context into the final token, the last hidden state acts as an ideal sequence summary.
- Next connectionDiscover why causal attention makes the final token the critical sequence bottleneck in Screen 2.
More context & mechanics
Pretrained autoregressive models map token sequences to next-token logits via a vocabulary projection head W_(vocab) ∈ R^d_(model) × V (where V = 50,257).
To adapt for C-class classification (e.g. C=2 for spam detection):
- Remove or bypass W_(vocab).
- Attach a specialized linear classification head:
W_(cls) ∈ R^d_(model) × C, b_(cls) ∈ R^C
- Route the aggregated sequence representation through W_(cls) to output class logits z ∈ R^C:
z_(class) = h_(summary) W_(cls) + b_(cls)
Adapting GPT-2 Small (d_(model) = 768) for binary sentiment classification (C=2):
- Original Vocabulary Head: 768 × 50,257 = 38,597,376 parameters (≈ 38.6 M).
- New Classification Head: 768 × 2 + 2 = 1,538 parameters (≈ 1.5 K).
By swapping heads, we reduce the final projection layer parameters by 99.996\%, focusing all fine-tuning gradients directly into discriminating between the 2 classes.
Standard recipe in text moderation (OpenAI Moderation API), document categorization, spam filtering, and legal document analysis.
Radford et al. (OpenAI, 2018) introduced task-specific classification heads on GPT-1, establishing generative pretraining followed by discriminative finetuning.
Swap the 50,000-word mouth for a 2-button switch: generative heads predict words, classifier heads predict decisions.
Why is the final timestep hidden state h[-1] the only vector capable of classifying the entire prompt in a causal transformer?
Imagine a team of detectives reading a diary entry. Detective 1 is only allowed to read sentence 1. Detective 2 is allowed to read sentences 1 and 2. Detective 100 reads the whole diary from start to finish. If the judge asks 'What was the overall theme of this diary?', only Detective 100 has read the entire story!
- $$\text{ReceptiveField}(\mathbf{h}_t) = \mathbf{x}_{1:t} \implies \text{ReceptiveField}(\mathbf{h}_{T-1}) = \mathbf{X}_{total}$$
- $$Because causal lower-triangular attention masks prevent tokens from attending to the future, token t=0 sees only itself, token t=1 sees tokens 0 and 1, and token t=T-1 is the only position that has attended to 100% of the input sequence.$$
- Learning goalProve why the final timestep hidden state is the unique sequence-level aggregator in causal transformers.
- Helpful foundationsCausal self-attention masking mathematics · Transformer receptive fields · Attention Is All You Need (Vaswani et al., 2017)
- Common trapAttempting to use the first token (index 0) like BERT's [CLS]; in causal transformers, token 0 cannot attend forward to subsequent tokens and has a receptive field of exactly 1.
- Next connectionImplement surgical replacement of the vocabulary head in Head Surgery (Screen 3).
More context & mechanics
In causal self-attention, the attention weight matrix is masked with an upper-triangular -∞ barrier:
A_(i, j) = (qᵢ kⱼ^T) / (√(dₖ)) & if j ≤ i \ -∞ & if j > i
The receptive field of hidden state h_t is strictly the prefix x_(1:t):
- Token 0: attends to x₀ (Receptive field = 1).
- Token t: attends to x₀, ṡ, x_t (Receptive field = t+1).
- Final Token T-1: attends to x₀, ṡ, x_(T-1) (Receptive field = 100% of sequence).
Therefore, slicing at index -1 (h[:, -1, :]) extracts the unique contextual summary of the entire input.
Consider prompt: `'This movie is terrible'` (T = 4 tokens):
- Hidden state h₀ ('This'): Has zero knowledge that the word 'terrible' exists.
- Hidden state h₁ ('movie'): Knows only 'This movie'.
- Hidden state h₃ ('terrible'): Layer-by-layer attention has blended representations of 'This', 'movie', 'is', and 'terrible'.
Slicing h[:, 3, :] ∈ R^(B × 768) feeds complete semantic context into W_(cls), correctly predicting 'Negative Sentiment' with 99.2\% probability.
Standard sequence pooling strategy in decoder-based classifiers (LLaMA-for-Sequence-Classification, GPT-2 Classifier).
Contrasts with BERT's bidirectional `[CLS]` token (Devlin et al., 2018); causal transformers rely on position -1 as established in GPT-2 (Radford et al., 2019).
Causal masks look only backward: the final token is the omniscient witness of the entire past.
How is the linear vocabulary projection layer surgically replaced with a classification head in PyTorch without breaking pre-trained weights?
Think of a rocket booster that was built to launch satellites. To repurpose the booster to carry a space telescope instead, you don't melt down the entire rocket engine; you unbolt the nose cone cargo bay and bolt on the telescope. Head surgery unbolts the 50,000-word nose cone and bolts on a classification module.
- $$\text{Model}_{cls}(\mathbf{X}) = \text{Linear}_{768 \to 2}(\text{Transformer}_{frozen}(\mathbf{X})[:, -1, :])$$
- $$In PyTorch, head surgery replaces model.out_head with a new torch.nn.Linear(d_model, num_classes) initialized with small random weights (std=0.02), keeping the entire transformer backbone pristine.$$
- Learning goalExecute surgical linear head replacement in PyTorch and initialize new classification parameters cleanly.
- Helpful foundationsPyTorch nn.Module architecture · Weight initialization distributions · Improving Language Understanding by Generative Pre-Training (Radford et al., OpenAI 2018)
- Common trapRe-initializing the entire model; only the newly attached classification head is randomly initialized; the entire transformer backbone retains its pretrained weights.
- Next connectionDecide whether to train or freeze the backbone in Parameter Freezing Strategies (Screen 4).
More context & mechanics
The surgery executes in three explicit steps:
- Identify the output linear module: `self.out_head = nn.Linear(d_model, vocab_size, bias=False)`.
- Instantiate the replacement classifier head:
```python
self.out_head = nn.Linear(d_model, num_classes, bias=True)
```
- Initialize the new head weights using truncated normal initialization:
W_(cls) ~ N(0, (0.02) / (√(d_(model)))), b_(cls) = 0
The rest of the transformer layers (multi-head attention, MLP blocks, LayerNorms) remain 100% intact with their pretrained parameters.
Code walkthrough in PyTorch:
```python
model = GPTModel(cfg)
load_weights_into_gpt(model, params) # Load pretrained OpenAI GPT-2 weights
# Perform surgery
model.out_head = nn.Linear(in_features=768, out_features=2, bias=True)
nn.init.normal_(model.out_head.weight, mean=0.0, std=0.02)
nn.init.zeros_(model.out_head.bias)
```
Input tensor X ∈ Z^(4 × 128) passes through all 12 blocks, producing H ∈ R^(4 × 128 × 768).
Extracting H[:, -1, :] and multiplying by `model.out_head` produces class logits of shape (4, 2).
Standard pattern across Hugging Face `AutoModelForSequenceClassification.from_pretrained('gpt2', num_labels=2)`.
Standardized in transfer learning frameworks (Howard & Ruder, 2018 in ULMFiT; Devlin et al., 2018 in BERT).
Preserve the backbone, swap the nose cone: surgery targets the exit, leaving the brain intact.
What are the computational and generalization trade-offs between freezing the entire transformer backbone versus unfreezing the final layers?
Imagine tuning a piano. If you only tune the final pedal (head-only), the piano stays mostly the way the factory built it, but it might lack emotional resonance for jazz. If you unfreeze the top octave strings (tail unfreezing), you can tune the jazz chords perfectly while keeping the massive cast-iron soundboard completely stable.
- $$\text{Memory}_{opt} = 8 \times N_{trainable} \text{ bytes}$$
- Learning goalEvaluate parameter efficiency and accuracy trade-offs across head-only, tail unfreezing, and full fine-tuning regimes.
- Helpful foundationsHead surgery implementation · Catastrophic forgetting in neural networks · Universal Language Model Fine-tuning for Text Classification (Howard & Ruder, 2018)
- Common trapBelieving fine-tuning always requires training 100% of weights; fine-tuning only the classification head and final block often matches full fine-tuning with 95% less compute.
- Next connectionImplement gradient disablement in PyTorch in Screen 5.
More context & mechanics
We contrast three freezing regimes:
- Head-Only Tuning:
Freeze all transformer layers: w_(backbone).requires\_grad = False.
Only W_(cls), b_(cls) receive gradients. Fast, zero risk of catastrophic forgetting, but limited adaptation capacity.
- Tail Layer Unfreezing:
Freeze early layers (which capture universal syntax and grammar), but unfreeze the final transformer block L-1 and final LayerNorm:
∇_w L ≠ 0 only for l ≥ L-k
- Full Finetuning:
All parameters update. Highest accuracy ceiling, but requires large datasets to prevent overfitting and 3× higher optimizer memory.
Parameter audit for GPT-2 Small (124M parameters):
- Head-Only: Trainable = 1,538 params (0.0012\% of model). Optimizer memory = 12 KB!
- Tail Unfreezing (Last Block + Head): Trainable = 7,087,874 params (5.7\% of model). Optimizer memory = 56.7 MB.
- Full Finetuning: Trainable = 124,441,346 params (100\% of model). Optimizer memory = 995.5 MB.
Tail unfreezing achieves 98.5\% of full-finetuning accuracy while slashing trainable weights by 94.3\%.
Standard transfer learning methodology across low-resource NLP classification tasks.
Howard & Ruder (2018) formulated Gradual Unfreezing in ULMFiT, establishing layer-wise adaptation protocols.
Early layers see grammar; late layers see meaning: freeze the foundation, adapt the ceiling.
How is parameter freezing programmatically enforced across PyTorch computational graphs using requires_grad=False?
Imagine an electrical breaker box in a house. When you flip the master switch for the upstairs bedroom, electricity stops flowing through those wires. Setting `requires_grad = False` flips the breaker for all 124 million parameters in the backbone: autograd skips them completely, saving massive GPU memory and compute time.
- $$\mathbf{w}.\text{requires\_grad} = \text{False} \implies \mathbf{w}.\text{grad} \equiv \text{None}$$
- $$Iterating through model.parameters() and setting param.requires_grad = False halts autograd backpropagation at the boundary, completely eliminating gradient tensor allocation for the frozen layers.$$
- Learning goalImplement programmatic parameter freezing in PyTorch and quantify GPU VRAM savings from disabled gradient buffers.
- Helpful foundationsPyTorch autograd and tensor computation graphs · Parameter freezing trade-offs
- Common trapThinking torch.no_grad() is the same as param.requires_grad = False; torch.no_grad() is a temporary runtime context for inference; requires_grad = False selectively disables training on specific subsets of model weights.
- Next connectionLearn how to selectively re-enable gradients on the final transformer block in Screen 6.
More context & mechanics
In PyTorch autograd, a parameter w only accumulates gradients if `requires_grad=True`:
w.grad = ∇_w L & if w.requires\_grad = True \ None & if w.requires\_grad = False
To freeze the entire foundation model before head replacement:
```python
for param in model.parameters():
param.requires_grad = False
```
When new layers (such as `model.out_head = nn.Linear(...)`) are instantiated afterward, their parameters default to `requires_grad=True`.
Memory and allocation audit in PyTorch:
- With `requires_grad = True` (Full model):
- Autograd allocates gradient tensors matching every weight: 124M × 4 bytes = 497.7 MB VRAM.
- Adam optimizer allocates 2 moment tensors (m_t, v_t): 124M × 8 bytes = 995.5 MB VRAM.
- With `requires_grad = False` (Frozen backbone):
- Backbone gradient allocation: 0.0 bytes!
- Backbone optimizer allocation: 0.0 bytes!
- Only the 1,538 classifier head parameters allocate gradients (6.1 KB).
GPU memory overhead drops by over 1.49 GB instantly!
Standard pattern used in Parameter-Efficient Fine-Tuning (PEFT, LoRA, Prompt Tuning).
Core feature of the PyTorch autograd engine designed by Adam Paszke and Soumith Chintala (2017).
Flip requires_grad to False: no gradient tensors allocated, no VRAM wasted.
How are specific target layers (final transformer block and LayerNorm) re-enabled for gradient tracking in PyTorch?
Imagine a hospital where the entire surgical staff is placed on standby. To handle a specific emergency, the medical director pages only the chief neurosurgeon and head anesthesiologist. All other doctors remain rested and undisturbed. Tail unfreezing wakes up only the final block of the model to specialize in your task.
- $$\mathbf{w}_{active} = \{\mathbf{W}_{block[-1]}, \boldsymbol{\gamma}_{final}, \boldsymbol{\beta}_{final}, \mathbf{W}_{cls}\}$$
- $$After globally freezing the model, targeted loop iteration over model.trf_blocks[-1].parameters() and model.final_norm.parameters() re-enables requires_grad=True, giving high-level attention heads the capacity to adapt.$$
- Learning goalWrite targeted PyTorch loops to unfreeze the final transformer block and final LayerNorm while keeping early layers frozen.
- Helpful foundationsPyTorch gradient disablement · Transformer block layer architecture
- Common trapForgetting to unfreeze the final LayerNorm; if final_norm is frozen, its scale and shift parameters cannot adapt to the new variance distribution of the fine-tuned block.
- Next connectionVerify and audit these parameter counts in Screen 7.
More context & mechanics
To execute selective tail unfreezing:
```python
# 1. Global Freeze
for param in model.parameters():
param.requires_grad = False
# 2. Re-enable final transformer block
for param in model.trf_blocks[-1].parameters():
param.requires_grad = True
# 3. Re-enable final LayerNorm
for param in model.final_norm.parameters():
param.requires_grad = True
# 4. Re-enable classification head
for param in model.out_head.parameters():
param.requires_grad = True
```
During the backward pass, autograd computes vector-Jacobian products backward from the loss through `out_head`, `final_norm`, and block L-1, halting cleanly at block L-2.
Parameter breakdown for GPT-2 Small with Block 11 unfrozen:
- `out_head` (classifier): 768 × 2 + 2 = 1,538
- `final_norm` (LayerNorm scale & shift): 768 × 2 = 1,536
- `trf_blocks[-1]` (Block 11):
- Attention projection (W_q, Wₖ, W_v, W_o): 4 × (768 × 768) + 4 × 768 = 2,362,368
- LayerNorms (2 in block): 2 × (768 × 2) = 3,072
- MLP (768 → 3072 → 768): 2 × (768 × 3072) + 3072 + 768 = 4,722,432
- Total Unfrozen Parameters: 1,538 + 1,536 + 2,362,368 + 3,072 + 4,722,432 = 7,090,946 parameters (≈ 7.09 M).
117.35 M parameters (94.3\%) remain completely frozen!
Recommended configuration for domain adaptation where base model representations are strong but target task outputs require semantic re-weighting.
Established by Felbo et al. (2017) in 'Using millions of emoji occurrences to learn any-domain representations for detecting sentiment' (DeepMoji).
Index [-1] points to the top: unfreeze the last block and final norm to adapt semantics without disturbing the base.
How do you programmatically verify and audit the exact count of trainable versus non-trainable parameters in a PyTorch model?
Imagine taking inventory of a warehouse before signing a logistics contract. You don't guess how many pallets are on the shelves; you scan the barcode of every single pallet to confirm that exactly 7 pallets are scheduled for shipping and 117 pallets remain in long-term storage.
- $$N_{trainable} = \sum_{p \in \Theta} \text{numel}(p) \cdot \mathbb{I}(p.\text{requires\_grad})$$
- Learning goalWrite an automated parameter auditing script in Python to verify trainable parameter percentages and prevent memory leaks in AdamW.
- Helpful foundationsPyTorch model parameters and numel() API · AdamW optimizer moment memory allocation
- Common trapPassing model.parameters() to the optimizer when only a subset of weights are trainable; passing all parameters causes AdamW to allocate moments for frozen weights, wasting hundreds of megabytes.
- Next connectionExtract logits during the forward pass in Screen 8.
More context & mechanics
A production parameter audit function computes:
```python
def count_parameters(model):
total = sum(p.numel() for p in model.parameters())
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
frozen = total - trainable
return "total": total, "trainable": trainable, "frozen": frozen
```
Passing only trainable parameters to the optimizer prevents AdamW from allocating unneeded moment buffers:
```python
optimizer = torch.optim.AdamW(
[p for p in model.parameters() if p.requires_grad],
lr=5e-5, weight_decay=0.1
)
```
Executing parameter audit on our modified GPT-2 classifier:
- `Total parameters: 124,441,346`
- `Trainable parameters: 7,090,946 (5.70%)`
- `Non-trainable (frozen) parameters: 117,350,400 (94.30%)`
Optimizer state memory check:
AdamW Memory = 7,090,946 × 8 bytes = 56,727,568 bytes ≈ 56.7 MB
If we had passed `model.parameters()` naively, AdamW would have allocated 995.5 MB—wasting nearly a gigabyte of VRAM on frozen weights!
Standard audit line printed in every training log across Hugging Face, PyTorch Lightning, and Megatron-LM.
Standardized in deep learning engineering to catch bugs where users mistakenly train entire frozen models or fail to update adapter layers.
Count before you optimize: pass only p.requires_grad to AdamW to protect your VRAM.
Why does causal attention force classification heads to extract representations strictly from the final token position?
Imagine reading a legal contract where you are required to stamp a final verdict ('Approved' or 'Denied'). If you place your verdict stamp at the top of Page 1 before reading the rest of the document, you have zero idea what was agreed upon on Pages 2 through 50. But if you walk to the very end of the document, Page 50 has witnessed every preceding clause, amendment, and signature. In causal LLMs, only the very last word in the sentence has read everything that came before it.
- Learning goalExplain the causal receptive field geometry and formulate last-token pooling for adapting generative decoder models into sequence classifiers.
- Helpful foundationsCausal attention lower-triangular masking · Improving Language Understanding by Generative Pre-Training (Radford et al., 2018)
- Common trapAttempting to use mean pooling across all tokens in a causal LLM. Mean pooling averages early blind tokens with late informed tokens, severely diluting the classification signal.
- Next connectionNext, trace the classification head surgery, tensor slicing, and accuracy baselines in Part B.
More context & mechanics
Adapting causal autoregressive models for sequence classification requires understanding the Causal Receptive Field:
- The Bidirectional Contrast (BERT vs. GPT):
- In bidirectional models like BERT, all tokens attend to all other tokens. A special `[CLS]` token placed at position 0 attends to the entire sentence, aggregating global context.
- In causal models like GPT, attention is lower-triangular: token i can only attend to tokens j ≤ i.
- The Receptive Field Defect of Position 0:
If you pool from position 0 in a causal LLM, its receptive field is strictly x₀. It is completely blind to tokens 1 through T-1!
- The Last-Token Aggregation Hub:
Token T-1 (the final token) attends to all preceding tokens: x₀, x₁, ṡ, x_(T-1). It acts as an information accumulation bottleneck, concentrating the semantic summary of the entire prompt into a single d_(model)-dimensional vector.
Production sentiment analyzers, spam filters, code vulnerability scanners, and reward models (in RLHF) built on causal architectures (such as Llama-3-Reward) pool strictly from the final token index to score text sequences.
OpenAI formalized last-token pooling in 2018 in the original GPT-1 paper ("Improving Language Understanding by Generative Pre-Training"), demonstrating that appending an `[EOS]` token allowed a generative pre-trained model to outperform specialized classifiers across GLUE benchmarks.
- First token for BERT; last token for GPT: Bidirectional reads everywhere; causal accumulates at the end.
- The caboose sees the whole train: Only the final token has witnessed the complete journey.
How do we perform head surgery to replace the 50k vocabulary head with a K-class classification tensor, and what is the random accuracy baseline?
Think of a Formula 1 race car. The chassis, turbocharged engine, aerodynamic wings, and suspension (the pretrained transformer trunk) represent millions of dollars of engineering. At the very front of the car sits a small modular sensor bracket. For a desert race, mechanics unbolt the rain sensor and snap on a sand sensor. The entire multimillion-dollar car remains identical; you only swapped a 200-gram bracket. Head surgery is that sensor swap: keep the entire 124M transformer frozen; swap the output bracket.
- $$Head surgery removes $W_{\text{vocab}} \in \mathbb{R}^{d \times 50,257}$ and inserts $W_{\text{clf}} \in \mathbb{R}^{d \times K}$, evaluating sliced last-token vectors $h_{\text{last}} W_{\text{clf}}$ with initial accuracy $1/K$.$$
- Learning goalImplement head surgery in PyTorch by replacing the LM head with a K-class linear projection, and trace last-token forward evaluation.
- Helpful foundationsLast-token pooling concept · PyTorch linear layers and transfer learning
- Common trapThinking that the entire transformer must be retrained from scratch for classification. Over 99.9% of the pretrained representations are preserved; only the tiny classification head is trained from scratch.
- Next connectionNext, discover how Low-Rank Adaptation (LoRA) adapts linear layers using compact matrix factorization in Chapter 39 Screen 2.
More context & mechanics
#### 1. Architectural Head Surgery
To convert an LLM into a K-class classifier (e.g. K=2 for sentiment: Positive vs. Negative):
- Sever the Vocabulary Head: Discard the pretrained unembedding matrix W_(vocab) ∈ R^d_(model) × 50,257.
- Graft the Classification Head: Initialize a new linear layer:
W_(clf) ∈ R^d_(model) × K, b_(clf) ∈ R^K
Where K ≪ V (e.g., K=2 vs. V=50,257).
Given input sequence batch X ∈ Z^(B × T):
- Extract hidden state tensor from the final transformer block:
H = Transformer(X) ∈ R^B × T × d_(model)
- Slice the final token vector for each batch item:
h_(last) = H[:, -1, :] ∈ R^B × d_(model)
- Compute K-class logits:
Z = h_(last) W_(clf) + b_(clf) ∈ R^(B × K)
- Classification probabilities:
P = softmax(Z) ∈ R^(B × K)
Before fine-tuning, the newly grafted head W_(clf) is randomly initialized (W ~ N(0, σ²)). For a balanced K-class dataset, initial accuracy is exactly:
Accuracy_(init) = 1/K
- For binary sentiment (K=2): Initial accuracy ≈ 50.0\%.
- For 5-star rating classification (K=5): Initial accuracy ≈ 20.0\%.
In parameter-efficient fine-tuning, all 12 transformer blocks are frozen, and only W_(clf) (768 × 2 = 1,536 parameters) is trained initially. Once the head stabilizes, the final 2 transformer blocks can be unfrozen for end-to-end domain adaptation.
Transfer learning via head replacement was popularized in computer vision by DeCAF (Donahue 2014) and adapted to NLP by Howard & Ruder (ULMFiT, 2018), establishing the modern transfer learning paradigm.
- Chop 50k, graft K: Swap the giant vocabulary head for a compact class projection.
- Initial accuracy is 1/K: Verify your baseline at step 0 before updating weights.
What is the expected baseline accuracy and loss of a freshly initialized classification head prior to training, and why is this diagnostic essential?
Imagine installing a brand-new mechanical coin-flipper machine. Before you teach it how to predict loaded coins, you flip it 1,000 times. If it lands on Heads exactly 50% of the time, the machine is perfectly balanced and calibrated. If it lands on Heads 99% of the time on day zero, the gears are jammed! Auditing untrained baseline accuracy is checking that the coin flipper is balanced.
- $$\mathcal{L}_{init} \approx \ln(C), \quad \text{Accuracy}_{init} \approx \frac{1}{C}$$
- $$50% for binary classification) and initial loss approx ln(C) (approx 0.693 nats for C=2), confirming that the implementation is mathematically uncorrupted.$$
- Learning goalCalculate theoretical random baseline loss and accuracy for C-class classification and verify empirical models against these baselines.
- Helpful foundationsForward logit extraction mechanics · Cross-entropy loss with uniform distributions · A Recipe for Training Neural Networks (Karpathy, 2019)
- Common trapAssuming an untrained model should predict 0% accuracy; random guessing across C balanced classes mathematically achieves 1/C accuracy (e.g. 50% for C=2).
- Next connectionTransitions to Chapter 36 to execute the classification training loop and track accuracy gains.
More context & mechanics
With small random initialization W_(cls) ~ N(0, σ²) and b_(cls) = 0, output logits z ≈ 0.
- Predicted Probabilities:
pᵢ = (e^(zᵢ)) / (∑_(j=1)^C e^(zⱼ)) ≈ 1/C ∀ i ∈ [1, C]
- Theoretical Baseline Loss:
L_(baseline) = -ln(1/C) = ln(C)
For binary classification (C = 2): L_(baseline) = ln(2) ≈ 0.6931 nats.
- Theoretical Baseline Accuracy:
Accuracy_(baseline) = 1/C = 1/2 = 50.0\%
If your model reports 90\% accuracy before training, there is label leakage. If it reports loss > 2.0, initialization variance is broken.
Pre-finetuning audit run on 1,000 validation SMS spam samples (balanced 500 spam, 500 ham):
- Calculated Validation Loss: 0.6928 (Theoretical ln(2) = 0.6931, delta < 0.0003!).
- Validation Accuracy: 503 / 1000 = 50.3\% (Perfect random baseline).
- Output Logit Mean: μ_z = 0.0014, σ_z = 0.0182.
The diagnostic passes with 100% mathematical fidelity. The model is primed for fine-tuning.
Standard sanity check executed in automated CI/CD test suites before launching cloud training jobs.
Standardized by Andrej Karpathy (2019) in 'A Recipe for Training Neural Networks', emphasizing baseline sanity checks.
Before you train, verify the coin flip: ln(C) loss and 1/C accuracy prove your plumbing is clean.
How does adapting a pretrained base model bifurcate into task-specific classification heads versus autoregressive instruction tuning?
Imagine graduating from medical school. You now possess a vast, universal foundation of human biology (a pretrained base model). You stand at a fork in the road. Path A: you become a specialist diagnostic radiologist who looks at an X-ray and outputs a single label: 'Pneumonia' or 'Clear' (Classification Head). Path B: you become a bedside physician who listens to patient questions and writes compassionate, multi-paragraph diagnostic treatment plans (Instruction SFT). Both paths leverage the same medical brain, but their final output format is totally different.
- Learning goalArchitect classification heads for discriminative fine-tuning and construct loss masks for generative instruction SFT.
- Helpful foundationsTransfer learning foundations · Training language models to follow instructions (Ouyang et al., 2022)
- Common trapComputing loss across both user prompt and assistant response during SFT; training the model to predict user prompts degrades instruction-following and wastes gradient signal.
- Next connectionCan we prune 90% of a model's weights and still train it to original accuracy from scratch? We examine the Lottery Ticket Hypothesis.
More context & mechanics
Downstream adaptation splits into two architectural branches:
- Branch 1: Discriminative Classification Fine-Tuning:
- Remove the pretrained |V|-dimensional language modeling head W_(vocab).
- Attach a task-specific linear head W_(cls) ∈ R^d_(model) × C, where C is the number of target classes (e.g. C = 2 for spam detection).
- Feed the final token's hidden state h_(T):
z = W_(cls) h_T ∈ R^C
- Train with cross-entropy loss against ground truth class labels.
- Branch 2: Generative Instruction Tuning (Supervised Fine-Tuning - SFT):
- Keep the original |V|-dimensional vocabulary output head intact.
- Format training data as structured conversations: `[Prompt, Response]`.
- Apply a prompt loss mask M_t:
M_t = 0 & if token t belongs to Prompt (ignore loss) \ 1 & if token t belongs to Assistant Response
L_(SFT) = -∑_(t=1)^T M_t ln P(x_t | x_(<t))
The model learns to generate helpful answers without wasting capacity memorizing user prompts.
- Branch 1 powers sentiment classifiers, moderation filters, and embedding search rerankers.
- Branch 2 transforms raw text autocomplete engines (e.g. LLaMA-3-Base) into conversational assistants (LLaMA-3-Instruct).
Jeremy Howard and Sebastian Ruder introduced ULMFiT in 2018, demonstrating transfer learning for classification. Long Ouyang et al. (OpenAI, 2022) established instruction SFT in InstructGPT.
- Classification swaps the head; SFT masks the prompt.
- Discriminators output labels; assistants generate dialogue.
How does Classification Fine-Tuning: Replacing the Language Model Head operate mathematically at scale?
Architectural transformation diagram:
- Learning goalMaster the algebraic and geometric derivation of Classification Fine-Tuning: Replacing the Language Model Head.
- Helpful foundationsFoundations of Build Llm From Scratch · Coordinate Invariance & Geometric Projections · Radford et al. (2019), Language Models are Unsupervised Multitask Learners, OpenAI
- Common trapAssuming Classification Fine-Tuning: Replacing the Language Model Head is an arbitrary computational trick rather than an invariant spatial law.
- Next connectionConnects upstream foundational lemmas to modern high-dimensional computational systems.
More context & mechanics
A model adaptation architecture diagram in square 1:1 format on warm cream parchment (#fcf0db) with a sepia frame.
Top section: Title in navy serif: "Classification Fine-Tuning: Head Surgery". Subtitle: "Repurposing a generative causal LLM into a discriminative text classifier."
Middle section: Architectural transformation diagram:
- Base Pretrained Transformer: 12 Transformer blocks outputting hidden state h_L ∈ R^D.
- Surgery: Detach and discard the original generative LM head (W_(head) ∈ R^(50257 × D)).
- Attach Classification Head: Initialize a new linear projection layer W_(cls) ∈ R^(C × D) (where C is the number of classes, e.g., C = 2 for Spam/Ham or C = 3 for Sentiment).
- Extract Representation: Take the hidden state of the FINAL token in the sequence h_(L, last) (which has causally attended to all preceding tokens).
- Compute Class Logits: y = W_(cls) · h_(L, last) ∈ R^C.
Bottom section: Training protocol: "Freeze the bottom 10 Transformer blocks initially; train the new classification head for 1 epoch, then unfreeze all layers for end-to-end fine-tuning."
Sharp block diagrams, distinct removed vs added head indicators, balanced square layout.
Every transformation preserves structural invariants while mapping coordinate spaces. When operating in high dimensions, numerical precision and computational efficiency require adhering strictly to canonical mathematical laws.
Formal derivation and peer-reviewed analysis:
Radford et al. (2019), Language Models are Unsupervised Multitask Learners, OpenAI
"Classification Fine-Tuning: Replacing the Language Model Head: Invariance, symmetry, and structure govern mathematical truth across continuous and discrete domains."