Concepts / Fine-Tuning a Classifier with LoRA

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.

  • Programming

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.

EASY

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

Definition

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.

python
Output (expected)
0
Notes on the code
  • The loop visits every parameter in the model and sets its requires_grad flag 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.

Load pretrainedmodelCount trainable(baseline)Freeze originalparametersrequires_grad = FalseCount trainable(after freeze)Replace Linear withLinearWithLoRACount trainable(after LoRA)Train LoRA parameters
What is the sequence from loading the pretrained model to training, and where does each trainable count checkpoint sit?

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.

Layer inputLinearWithLoRAlinear childoriginal weights, frozenlora childLoRALayer, trainablecontainscontains
How are the frozen original Linear layer and the trainable LoRA layer arranged inside one wrapper, and how does the input reach each part?

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.

Worked example

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?

  1. 1
    Look for the linear child

    A correct upgrade contains a linear child that holds the original weights. This is the frozen part of the wrapper.

  2. 2
    Look for the lora child

    A correct upgrade also contains a lora child that is a LoRALayer. This is the trainable part.

  3. 3
    Check 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.

Result

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.

Original parameterstrainableOriginal parametersfrozenLoRA parameterstrainablefreezereplace
What is the requires_grad state of each parameter group after the freeze, and which group becomes trainable when LoRA layers are added?

Mistakes That Skew the Counts

  • Common mistake

    Treating the freeze as a switch on the whole model

    Why it fails

    The requires_grad flag belongs to each individual parameter. Freezing is a loop over model.parameters(), not a single setting on the model object.

    Do this instead

    Walk 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 like

    The model is replaced with LinearWithLoRA layers first, and then the freezing loop is run.

    Why it fails

    The loop also reaches the new LoRA parameters, which are meant to be trained. No parameter is left trainable.

    Do this instead

    Freeze 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 fails

    The 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 fails

    Raising 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.

MEDIUM

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_grad to 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.

Related concepts

More concepts starting with F