Fine-Tuning a Classifier with LoRA
Freezing sets requires_grad to False on every original parameter, so none of the pretrained weights are updated during fine-tuning.
Deciding What Is Allowed to Change
Fine-tuning with LoRA does not start by changing the pretrained model. It starts by deciding which numbers are allowed to change. A pretrained GPT model already holds a large set of learned parameters. LoRA is designed so that most of those original values stay exactly as they were, while a small number of new values are learned on top of them.
This means the workflow has a clear first move. Before any LoRA layer exists, you want a measurable checkpoint: a moment where you can count how many parameters are trainable, freeze the original ones, and count again. Those counts are how you confirm that the setup did what you intended. This article follows that sequence step by step, so that when a count looks wrong you know where to look.
Counting Trainable Parameters Before Any Change
The lesson's trace does three things in order: it counts the trainable parameters, runs a loop that sets requires_grad to False on every parameter, and then counts again. The first count is your baseline. It describes the original model as it was loaded, before anything is frozen or replaced.
Notice what the counting step is looking for. It is checking the requires_grad flag on each parameter. A parameter with requires_grad set to True is one the training process is allowed to update. The count is therefore a question about permission, not about how large the model is.
Before you look at any printed output from the trace, commit to a prediction. Once the loop has set requires_grad to False on every original parameter, how many parameters should still be trainable, and what does that tell you about the model's state at that moment? Write down your answer and the reasoning behind it before continuing.
Hints
- Think about what the loop does to each parameter, not to the model as a whole.
- Consider which parameters exist at the moment of the freeze. Do LoRA parameters exist yet?
What Freezing Does to Each Parameter
Freezing sets requires_grad to False on every original parameter, so none of the pretrained weights are updated during fine-tuning.
The flag belongs to each individual parameter, not to the model as a whole. A model is simply a collection of parameters, and freezing is a per-parameter setting that you apply by walking through that collection. This is why the freeze is written as a loop over the model's parameters rather than as a single switch on the model object.
0- The loop visits every parameter in the model and sets its
requires_gradflag to False. - The counting line adds up only the parameters whose flag is still True.
- Immediately after the loop and before any LoRA layer is added, the model has no trainable parameters.
The code above is an illustration of the loop the lesson describes, written for clarity. The output of 0 follows from the logic of the freeze: every original parameter has been switched off, and no new trainable parameters have been created yet. The lesson's own trace is where you should verify this against real printed numbers.
Why the Freeze Must Come Before the Swap
The order of operations is deliberate. The freeze happens first, and the replacement of Linear layers with LinearWithLoRA layers happens second. The reason is that the new LoRA layers are meant to be trained, so they must not be frozen. If the order were reversed, the freezing loop would also reach the newly created LoRA parameters, and no parameter in the model would be left trainable. The fine-tuning step would then have nothing to learn from.
The sequence can be read as a short chain. Load the pretrained model. Count its trainable parameters as a baseline. Freeze the original weights and count again. Replace the Linear layers with LinearWithLoRA layers. Count once more. Train. The diagram below lays out this chain so you can see where each checkpoint sits.
Inside a LinearWithLoRA Layer
A LinearWithLoRA layer is not a brand new kind of layer that replaces the original one in a single step. It is a wrapper with two parts. The first part is the original Linear layer, which has been set to nontrainable. The second part is a new LoRA layer, which is trainable and is the piece that fine-tuning will update. Together these two parts make up the upgraded layer.
The diagram shows the wrapper's structure: one outer LinearWithLoRA layer holding a frozen linear child and a trainable lora child. The lesson's architecture printout confirms this structure, with each upgraded entry showing a linear child that holds the original weights and a lora child that is a LoRALayer. The lesson's material does not spell out the exact arithmetic for combining the two outputs, so the important point here is structural: the original weights are fixed, and the lora child is where the learning happens.
Not every component in the model is replaced. After the swap, the printed architecture shows LinearWithLoRA entries in the attention output projection, the two linear layers of the feed-forward block, and the output head. Components such as the GELU activation, the dropout layers, and the layer norms appear without a lora child, because they were not replaced.
Reading the Architecture Printout
The lesson prints the model architecture after the replacement so that you can check the change by eye. This printout is the second place to look when something seems wrong, after the counts themselves. It answers a structural question: which modules were swapped, and which were left alone.
Checking one module entry in the printout
You see an entry in the printout that is labeled LinearWithLoRA. How do you confirm it is a correct upgrade rather than an error?
- 1Look for the linear child
A correct upgrade contains a linear child that holds the original weights. This is the frozen part of the wrapper.
- 2Look for the lora child
A correct upgrade also contains a lora child that is a LoRALayer. This is the trainable part.
- 3Check the untouched components
GELU, dropout, and layer norm entries should appear without a lora child, because they were not replaced. If one of them has a lora child, the replacement reached a module it should not have.
An entry that has both a linear child and a lora child is a correctly upgraded layer. An entry with a lora child where none was expected points to a replacement step that ran on the wrong module.
Setting Rank and Alpha
The replacement call takes two settings, rank and alpha. The source uses a rank of 16 and an alpha of 16, and describes both values as good default choices. The rank matters for the counts you will check. Raising the rank increases the number of trainable parameters, because the LoRA layers grow with it. This is why a count that is higher than you expected can come from a rank setting as well as from a freeze that did not happen.
Alpha is commonly set to half, double, or equal to the rank. In this lesson the value is equal to the rank, at 16 in both cases.
Tracing the Counts Across the Three Checkpoints
There are three checkpoints in the sequence, and each answers a different question. The first count, taken before the freeze, describes the original model. The second count, taken after the freeze, should show that the original parameters no longer contribute trainable weight. The third count, taken after the replacement, describes the new LoRA parameters. The second and third counts measure different things, and mixing them up is one of the most common ways to misread the output.
Because each count is a checkpoint, a mismatch tells you where the process diverged. If the count after the freeze is not what you expected, the problem is in the freezing loop or in something that ran before it. If the count after the replacement is not what you expected, the problem is in the replacement step, or in the freeze having been applied to the model after the swap.
Mistakes That Skew the Counts
Common mistake Treating the freeze as a switch on the whole model
Why it failsThe
requires_gradflag belongs to each individual parameter. Freezing is a loop overmodel.parameters(), not a single setting on the model object.Do this insteadWalk through the parameters and set the flag on each one, then count to confirm the result.
Common mistake Freezing after the LoRA layers are added
What it looks likeThe model is replaced with LinearWithLoRA layers first, and then the freezing loop is run.
Why it failsThe loop also reaches the new LoRA parameters, which are meant to be trained. No parameter is left trainable.
Do this insteadFreeze the original weights first, then add the LoRA layers.
Common mistake Comparing the second count with the third as if they measure the same thing
Why it failsThe count after the freeze describes the original weights. The count after the replacement describes the new LoRA parameters.
Common mistake Assuming a higher-than-expected trainable count always means the freeze failed
Why it failsRaising the rank increases the number of trainable parameters, because the LoRA layers grow with it.
Locating a Failed Freeze or Replacement
Suppose the trainable count after the freeze is not what you predicted. Work through the checkpoints in order. First, confirm the baseline count was taken before the loop. Second, confirm the loop ran over every parameter, since a freeze applied to only part of the model leaves some original weights trainable. Third, confirm that no replacement ran before the freeze. Only after these checks should you look at the replacement step itself.
You run the trace and find that the count after the replacement shows zero trainable parameters, even though LoRA layers were added. Using only the order of operations described in this article, identify the step most likely at fault and explain why a zero count points there.
Hints
- Ask whether the LoRA parameters were frozen by a loop that ran after they were created.
- Check the order of the freeze and the replacement in the sequence.
Summary
At a glance
- Freezing sets
requires_gradto False on every original parameter, so none of the pretrained weights are updated during fine-tuning. - The freeze must happen before the LinearWithLoRA replacement, because the new LoRA layers are meant to be trained and would be frozen too if the order were reversed.
- A LinearWithLoRA layer is a wrapper holding a frozen original Linear layer and a trainable LoRA layer.
- Trainable parameter counts before the freeze, after the freeze, and after the replacement are three checkpoints; a mismatch at one of them points to the step that diverged.
- Rank and alpha control the LoRA layers, and raising the rank increases the number of trainable parameters.