The useful part, up front

What you’ll take away

  • A parameter update, a recorded gradient path and a module’s training mode are separate choices.
  • A frozen later weight can remain on the derivative path needed by an earlier trainable weight.
  • A no-grad boundary is appropriate only when the omitted operations are not needed for the requested gradients.
  • Dropout can change forward outputs without changing weights; preserving an expected value does not preserve every later calculation.
  • Tracked BatchNorm statistics can change while its affine parameters remain fixed.
  • Save the actual configuration and independent state records, then evaluate task quality separately.
01

Ask what the word frozen actually promises

A model can have frozen weights and still produce different outputs on repeated passes. It can also change some of its stored state while those weights remain untouched. Neither observation automatically proves that the freeze setting failed. The setting may control a different part of the calculation from the one you are watching.

Fine-tuning adapts an existing model to a task. Hugging Face’s training guide describes that process and the need to evaluate the resulting task performance [5]. Here we examine a narrower question: when some weights are meant to stay fixed, which other controls still matter? We will separate weight updates, recorded derivatives, training behavior and stored running statistics.

The examples use two scalar weights, a two-number dropout input and a tiny batch-normalization calculation. They are original teaching examples, not measurements from a trained AI system. Their small size lets us follow every number. The main references are PyTorch 2.15 documentation and the Transformers 4.56.0 guide; this article does not claim that these are the newest installed versions.

A frozen suffix can remain on a needed gradient path. Original scalar composition. Only a is trainable, b stays 5, target is 0 and loss is half the squared output. No model or autograd runtime was executed.
Original scalar composition. Only a is trainable, b stays 5, target is 0 and loss is half the squared output. No model or autograd runtime was executed. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
02

Keep four questions separate

First ask which weights the update rule may change. In our examples, the optimizer receives only the weight named as trainable. Second ask which leaf weights should receive calculated gradients. PyTorch provides the requires_grad setting for that purpose [1, 2]. A gradient is a local instruction about how the loss changes when a value changes; it is not itself a weight update.

Third ask whether the operations needed for those gradients are being recorded. A no_grad region suppresses that recording [1]. Fourth ask how a module should behave on its forward pass. The train and eval settings affect modules with different training and evaluation behavior, including dropout and batch normalization [2].

These questions can have different answers in the same system. A weight may stay fixed while its operation remains necessary for a gradient flowing toward an earlier trainable weight. A module may use evaluation behavior while gradients are still being calculated. Writing the four choices separately makes a configuration much easier to understand than a single label saying frozen.

03

A fixed later weight can still carry an earlier gradient

Consider a two-step calculation. Multiply input x by weight a, then multiply the result by weight b. Set x to 2, a to 3 and b to 5. The output is 30. Our target is zero, and the loss is half the squared output, giving 450.

Now freeze b and allow only a to change. The effect of changing a still passes through multiplication by 5. The loss gradient for a is 300. Taking one deliberately chosen step of 0.001 gives a new a of 2.7. With b still exactly 5, the output becomes 27 and the loss becomes 364.5. These are arithmetic results from the stated rule, not evidence that a real model has improved.

The important point is the path. PyTorch records an operation in ordinary grad mode when at least one input requires a gradient [1]. Even if b does not need its own gradient, the input arriving from a does. Freezing b therefore does not mean removing that multiplication from the derivative calculation needed for a.

04

A no-grad boundary depends on where learning must reach

Suppose we keep the same arrangement but perform the final multiplication by b inside no_grad. That tells the framework not to record this operation. The loss no longer has the recorded route through that multiplication back to a. Leaving a marked trainable does not rebuild the missing route afterward [1].

Now change the learning problem. Freeze a at 3 and train only the later weight b. The input x is fixed, and we do not want gradients for x or a. The first step simply provides the fixed feature 3×2=6. We can calculate that feature without recording its history, then multiply it by trainable b in ordinary grad mode. The gradient for b is still available; in our initial example it is 180.

These two cases explain why advice to disable recording around every frozen layer is incomplete. A frozen prefix can be a fixed feature calculation when no learning signal needs to pass through it. A frozen suffix can still sit between the loss and an earlier trainable weight. Draw the required path before choosing the boundary.

The position of the frozen part changes the needed path. Original comparison under ordinary grad mode after the declared boundary. A fixed prefix needs no upstream derivative here; a fixed suffix must carry the path to a.
Original comparison under ordinary grad mode after the declared boundary. A fixed prefix needs no upstream derivative here; a fixed suffix must carry the path to a. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
05

Unchanged weights do not remove training-time dropout

Dropout provides a different reason for changing outputs. During training, the documented PyTorch module randomly zeros elements and scales the surviving values. In evaluation mode, this module acts as an identity operation [4]. Freezing nearby model weights is a separate setting; it does not choose the dropout behavior.

Take the input pair [2, 4]. For this example, each element is independently kept with probability one half. A kept element is doubled. There are four equally likely keep patterns: keep neither, keep only the second, keep only the first, or keep both. The corresponding outputs are [0, 0], [0, 8], [4, 0] and [4, 8]. No trainable weight needs to change for these outputs to differ.

In evaluation mode, the same dropout module returns [2, 4]. That pair is not one of our four training-mode outcomes. We have enumerated the possible masks mathematically; we have not sampled a random generator or measured a model. The example shows why a repeatable set of weights does not, by itself, specify the forward behavior.

06

Matching the average input does not match every later calculation

Average the four dropout outputs one position at a time. The first position averages to 2 and the second to 4. That matches the evaluation output. The scaling rule preserves the expected value of each element in this example, but an average is not the same thing as every possible result.

To see why that matters, add a simple later calculation: square both elements and add them. Applied to the four training outputs, this gives 0, 64, 16 and 80. Their average is 40. Applied once to the evaluation output [2, 4], it gives 4+16=20. The later calculation produces different answers depending on whether we average before or after applying it.

This does not show that dropout is good or bad, or that a real model’s evaluation loss must be lower. Our square-and-add rule is an invented calculation. It shows the narrower mathematical limit: preserving an average at one location does not establish equal behavior after every later transformation. A model’s actual task results still need their own evaluation.

07

Some stored values are buffers rather than learnable weights

A model can store more than weights. PyTorch distinguishes parameters from buffers, and its Module documentation gives a batch-normalization running mean as an example of a buffer [2]. Such a value can be part of the model’s saved state without being a weight that an optimizer learns through gradients.

BatchNorm1d illustrates the difference. With its usual tracking option enabled, it maintains running estimates of the mean and variance during training, then uses those estimates during evaluation [3]. It can also have learnable scale and offset parameters. Preventing gradients for the scale and offset does not, on its own, tell the module to stop maintaining the running estimates.

That distinction matters when someone says that the model did not change. They may have compared only parameter values. For a model containing this layer, selected buffers can still have changed. Conversely, a buffer change is not proof that a frozen parameter was updated. Inspect the kind of value and the rule that changes it before deciding which part of the training setup needs attention.

Four dropout masks have one mean but different later values. Original independent keep masks with probability 1/2 per element. Each training row has probability 1/4. Their mean output is [2, 4] and mean Q is 40. Evaluation returns [2, 4] with Q = 20. Q squares both elements and adds them.
Original independent keep masks with probability 1/2 per element. Each training row has probability 1/4. Their mean output is [2, 4] and mean Q is 40. Evaluation returns [2, 4] with Q = 20. Q squares both elements and adds them. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
08

Follow one batch through the running-statistic update

Set up one BatchNorm1d channel with tracking enabled. Freeze its scale at 1 and offset at 0. Start with running mean 0 and running variance 1, and choose momentum 0.1. Our training batch contains the two values 2 and 4. Their mean is 3.

The squared distances from that mean are 1 and 1. Dividing their sum by the batch size, 2, gives variance 1. Dividing instead by one less than the batch size gives variance 2. PyTorch documents the first, biased estimate for the training forward calculation and the second, unbiased estimate for the running-variance update [3]. Mixing these two denominators would give the wrong running result.

The new running mean is 0.9×0+0.1×3=0.3. The new running variance is 0.9×1+0.1×2=1.1. The frozen scale and offset remain 1 and 0. These selected stored statistics have changed even though the two named parameters have not. We are calculating statistics only, not reporting normalized outputs or running an actual layer.

09

Evaluation behavior does not switch gradients off

The word evaluation can suggest that all learning machinery has stopped. In PyTorch, eval chooses a module’s evaluation behavior. The documentation explicitly separates it from no_grad [1, 2]. For dropout, that selects the identity behavior. For our tracked batch-normalization example, it selects use of the stored running statistics [3, 4].

Whether derivatives are recorded is still a separate question. If a calculation runs in ordinary grad mode with the needed trainable inputs, selecting evaluation behavior does not itself erase the gradient route. Likewise, selecting training behavior does not automatically unfreeze a weight whose gradient flag remains disabled.

This gives useful combinations rather than one universal switch. A pure scoring pass may need evaluation behavior and no gradient recording. Another calculation may deliberately need derivatives while retaining evaluation behavior. The correct combination follows from the task. If a program changes a mode temporarily, it should also restore the intended setup before the next operation; a successful-looking output does not show which controls were active.

Selected running statistics change while affine weights stay fixed. Original BatchNorm1d example: training mode, tracking enabled, one channel with batch [2, 4], momentum 0.1. Batch mean 3; biased variance 1; unbiased variance 2. Not a complete list of layer state.
Original BatchNorm1d example: training mode, tracking enabled, one channel with batch [2, 4], momentum 0.1. Batch mean 3; biased variance 1; unbiased variance 2. Not a complete list of layer state. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
10

A validation pass has its own state policy

Return to the batch-normalization example. If we feed a new batch through the layer with training behavior still selected, its tracked statistics can move toward that batch even if we never perform a weight update. That is why checking for optimizer steps alone is not enough to describe what a validation pass did.

PyTorch’s autograd guidance specifically mentions selecting evaluation mode to avoid updating batch-normalization running statistics on validation data [1]. In our tracked example, the intended evaluation pass uses the previously stored estimates. Disabling gradient recording can be a separate choice when we do not need derivatives.

The records should distinguish the state used to score the data from the state afterward. If the purpose is to compare two checkpoints, quietly modifying one during the comparison changes what is being compared. This is a question about the experiment’s definition, not proof that any particular result is invalid. It also does not replace selecting a suitable task metric or keeping evaluation data appropriate to the actual question [5].

11

Changing what is trainable requires a new explicit plan

A project may first train a small final component, then decide to adjust an earlier component too. That change affects more than the name of a training phase. The newly trainable weight needs an update rule that includes it and a recorded path from the loss back to it.

Our two scalar arrangements make this concrete. When only b learns, the fixed feature 6 is sufficient. When a learns through fixed b, the multiplication by b must remain on a recorded derivative path. Reusing the first arrangement’s no-grad boundary in the second arrangement would remove something the new plan needs.

The same review should state which modules use training behavior and which stored statistics are allowed to change. There is no need to guess from a label such as feature extraction or fine-tuning. Write down the actual weight set, gradient settings, recording boundaries and module behavior. The change is then reviewable before judging task results, and any later comparison can say which setup produced each checkpoint.

12

Save enough information to explain the resulting model

A useful record should say what was adapted, which weights could change, which derivative paths were needed and which modules used training behavior. For the batch-normalization example, it should also record the tracking option and averaging rule. For the dropout example, record its probability and the mode used for scoring.

Saved tensor values do not automatically describe all those choices. PyTorch’s state_dict includes parameters and persistent buffers, but its documentation says the returned dictionary contains references to the module’s values [2]. Keeping that dictionary in memory is therefore not, by itself, a frozen historical copy for a before-and-after comparison. Use an independent captured record when a comparison requires one.

Finally, keep the conclusion proportional to the evidence. Unchanged weights establish something about weights; a working gradient path establishes something about the requested calculation. Neither proves that the model is useful on its task. The small examples here make the controls visible so that later measurements can be interpreted with the right configuration in view.

A useful fine-tuning record names the actual choices. Original record design. Saved parameters and persistent buffers do not, on their own, describe every mode, optimizer choice or evaluation rule.
Original record design. Saved parameters and persistent buffers do not, on their own, describe every mode, optimizer choice or evaluation rule. · Ameer Journal — original explanatory diagramOriginal editorial illustration; no third-party artwork reused.
Follow the evidence

Sources & further reading

Explore the references behind this guide. External pages may change after our review.

  1. Autograd mechanics — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  2. Module — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  3. BatchNorm1d — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  4. Dropout — PyTorch 2.15 documentation
    PyTorch · Accessed 7 Nov 2026
  5. Fine-tuning · Hugging Face
    Hugging Face · Accessed 7 Nov 2026

Article updated 7 Nov 2026.

Found something we should revisit?

Good explanations get better with thoughtful feedback.

Suggest a correction