Freezing Model Layers for Fine-Tuning
Replacing model.out_head changes the final projection from the vocabulary size of 50,257 to a size set by the task, here num_classes equal to 2.
One Swapped Layer in a Pre-Trained Model
This lesson looks at a single assignment that changes what a pre-trained GPT-2 model is for. The model keeps almost all of its structure. Its internal layers, which build representations of the input text, stay as they are. Only the final layer, the one that turns those internal representations into outputs, is replaced with a new layer built for a different task.
The swap is a single assignment, model.out_head = followed by a constructor call. Nothing else in the model is rewritten. That means every question about what changes comes down to two things: what the new object is, and how it is sized. The most useful habit here is to know which line changed the model and what the model looked like just before that line ran.
What the Original Output Layer Computes
Before you can judge the replacement, you need to know what it replaces. The original model.out_head takes the internal vector produced for each position and maps it to 50,257 values. That number is the size of the vocabulary, which means there is one output value for every token the model can produce.
This makes the original layer a vocabulary projection. It answers the question of which token comes next by scoring every entry in the vocabulary. Each position gets its own vector of 50,257 scores, and the highest-scoring entry is the model's candidate for the next token.
A classification task does not need a score for every vocabulary token. If the goal is to choose between two categories, the model only needs two outputs. The replacement is the change that matches the output size to the task instead of to the vocabulary.
Read the diagram from the top down. The hidden vector on the left and the hidden vector on the right are the same kind of object, because the layers before the output layer are untouched. Only the last projection and the number of scores it produces have changed. The original projection emits a score for every vocabulary token, while the new projection emits one score for each class.
Reading the Replacement Line
The replacement creates a new torch.nn.Linear layer and stores it in the model's out_head attribute, overwriting the original. The call takes two named arguments. Reading the line means understanding what each argument controls, so the constructor call is the place to start.
num_classesis set to 2 just before the replacement- model.out_head is the attribute being overwritten
in_featurestakes its value from the embedding dimension in the configout_featurestakes the number of classes
Predicting the New Weight Shape
A Linear layer stores its weight matrix with one row per output feature and one column per input feature. Use that rule and the two arguments you just read to predict the shape of model.out_head.weight. Then check your answer against the reveal.
What do you think happens?
What is the shape of model.out_head.weight after the replacement, given in_features equal to 768 and out_features equal to 2?
Reveal answer
2 by 768: two rows, one per output feature (num_classes), and 768 columns, one per input feature (the embedding dimension).
The weight matrix has one row per output feature and one column per input feature. Because out_features is 2 and in_features is 768, the matrix has 2 rows and 768 columns. The original layer, with 50,257 outputs and 768 inputs, had a weight of shape 50,257 by 768. Only the row count changed.
Sizing the Replacement Layer
The embedding dimension for gpt2-small is 768 and num_classes is 2. Work out the shape of the new weight and the shape of the output for a batch of one sequence with 5 positions.
- 1Identify in_features
in_featurescomes from BASE_CONFIG['emb_dim'], which is 768 for gpt2-small. - 2Identify out_features
out_featuresisnum_classes, which is set to 2. - 3Apply the weight rule
The weight has one row per output feature and one column per input feature, so the shape is 2 rows by 768 columns, written as W in R2 x 768.
- 4Apply the output rule
The layer is applied to the vector at each position. Five positions each produce 2 outputs, so the output has one row of 2 values per position.
The weight shape is 2 by 768. The output for one sequence of 5 positions has shape 5 by 2, with 2 class scores per position.
torch.Size([2, 768])- The first dimension is
out_features, the number of classes - The second dimension is
in_features, the embedding width
Keeping the Width in the Configuration
The number 768 could have been typed directly into the call. The source instead reads it from BASE_CONFIG['emb_dim']. The stated reason is generality: the same replacement line then works for larger GPT-2 variants, because each variant's embedding dimension is stored in the configuration rather than written into the line itself.
- The config-based version reads the width from the settings for whichever model is loaded
- The hardcoded version fixes the width to the value for gpt2-small
The hardcoded version is not wrong for gpt2-small. It becomes wrong only when the same code is pointed at a model with a different embedding dimension. The config-based version keeps the replacement line unchanged across model sizes, and the size-specific information lives in one place.
Finding Where a Mismatch Shows Up
When the output layer is replaced incorrectly, the error usually does not appear on the replacement line itself. The line is accepted, because creating a Linear layer with those arguments is valid. The problem shows up later, when data first passes through the new layer or when a shape is inspected. The useful debugging question is therefore not only what the line says but where its result is first used.
Trace from the replacement to the first place a shape is checked, and compare what you see there with the shape rule you derived earlier. If the input width of the new layer does not match the width of the vector arriving from the earlier layers, the first mismatch appears at the first forward pass through the new layer. If the output has a size you did not expect, the mismatch appears when you inspect the output or the weight shape.
Common mistake Swapping in_features and out_features
What it looks liketorch.nn.Linear(in_features=num_classes, out_features=BASE_CONFIG['emb_dim'])Why it failsThe input width must match the embedding vector coming out of the earlier layers, and the output width must match the number of classes. Swapping them makes the input width wrong, so the mismatch appears on the first forward pass.
Do this insteadKeep in_features set to the embedding dimension and out_features set to num_classes.
Common mistake Expecting the replacement line to raise the error
What it looks likeRunning the replacement line, seeing no error, and assuming the shapes are correct
Why it failsThe constructor accepts the arguments without checking them against the data that will later flow through the layer.
Do this insteadPass a sample input through the model and check the output shape, then confirm the weight shape matches the rule.
Common mistake Keeping the vocabulary size as the output width
What it looks likeLeaving out_features at 50,257 when the task has two classes
Why it failsThe original layer scored every vocabulary token. A classifier that needs two categories does not need that many outputs, so the output width no longer matches the task.
Do this insteadSet out_features to num_classes so the number of outputs matches the task.
Common mistake Hardcoding the embedding width
What it looks likein_features=768 written directly into the replacement lineWhy it failsThe line then only works for gpt2-small. A larger GPT-2 variant has a different embedding dimension stored in the configuration, and the hardcoded value no longer matches it.
Do this insteadRead the width from BASE_CONFIG['emb_dim'] so the line matches whichever model is loaded.
Practice: Sizing a New Output Head
A pre-trained model's configuration sets emb_dim to 768. You want to adapt the model to a task with four categories. Write the replacement line for model.out_head using BASE_CONFIG['emb_dim'] for the input width, then state the shape of the new weight matrix and where the first mismatch would appear if the input width were wrong.
Hints
in_featuresshould be the embedding dimension, read from BASE_CONFIG['emb_dim'].out_featuresshould be the number of categories, so setnum_classesto 4 before the replacement.- A Linear weight has one row per output feature and one column per input feature.
Without changing the replacement line, explain what happens to the new weight's shape if the configuration is switched to a model whose emb_dim is different from 768, and identify the first place in the forward pass where a shape check would fail if the input width no longer matched.
Hints
- Think about what
in_featuresis set to when the line reads from the config. - Think about which layer receives the vector first after the earlier layers run.
Key Takeaways
- The swap replaces only model.out_head, a single assignment that creates a new torch.nn.Linear layer, leaving the rest of the network unchanged.
- The original output layer maps each position's hidden vector to 50,257 scores, one per vocabulary token. The replacement maps it to
num_classesscores, which is 2 in this example. in_featuresis the embedding dimension read from BASE_CONFIG['emb_dim'] (768 for gpt2-small), andout_featuresisnum_classes. The weight has shape 2 by 768, with one row per output and one column per input.- Reading the width from the configuration keeps the same line usable across GPT-2 sizes. Shape errors usually surface at the first forward pass through the new layer, not on the replacement line, and the new layer must be trained for the task.