Concepts / Inspecting Model Parameters

Inspecting Model Parameters

The total number of trainable parameters is the sum of numel() over every parameter whose requires_grad attribute is True.

  • Programming

Why Count the Numbers a Model Can Change

Once a neural network has been built, a natural first question is how big it is. Not how many layers it has or how many neurons each layer contains, but how many individual numbers the training process is actually allowed to adjust. That count is the total number of trainable parameters. It tells you how much the model can change to fit data, and it is one of the simplest checks you can run on a model you have just constructed.

This article follows a short path. You will read a two-line piece of code that performs the count, predict what each part contributes, and then check that reading against an explanation of each piece. After that, you will see where the trainable numbers live, what happens when a layer is frozen, and how a trained model is saved to a file with a single torch.save call.

What do you think happens?

Before reading further, what do you think a single tensor with 1000 numbers adds to a trainable total: 1, or 1000?

  • 1, because the count is of tensors

  • 1000, because the count is of individual numbers

Reveal answer
Answer:

1000, because the count is of individual numbers

The total is a count of numbers, not of tensors or layers. Each tensor contributes its numel(), which is the number of individual elements it holds.

Tracing the Count Expression

The count is produced by a single line that uses sum() with a generator expression. Read it first, then predict what each part contributes before you read the explanation that follows.

python
Output (expected)
2213 (for the source model used in this lesson)
Notes on the code
  • model.parameters() yields every learnable tensor the model holds
  • if p.requires_grad discards any tensor whose requires_grad flag is False
  • p.numel() converts each surviving tensor into the count of elements it contains
  • sum() adds those element counts into one total

The expression inside sum() has three parts, and each does a separate job. The first part, model.parameters(), is a sequence of parameter tensors that walks through every learnable tensor the model holds. The second part, the condition if p.requires_grad, throws away any tensor whose requires_grad attribute is False. The third part, p.numel(), turns each surviving tensor into a number: the count of individual elements inside it.

Formula

The formula and the Python code express the same rule. The code is simply the formula written with a generator expression. Because the sum adds element counts rather than tensor counts, the total answers the question of how many numbers can change. Each tensor's contribution is its numel(), and tensors that are not trainable contribute nothing.

model.parameters()every parameter tensorkeep requires_gradTruedrop frozen tensorsnumel() per tensorelements in each tensorsum to totalnumber of trainable valueseach tensorsurviving tensorselement counts
What sequence of steps takes model.parameters(), keeps only the tensors with requires_grad True, and sums their element counts to get the total?

Trainable Numbers in Linear Layers

In the multilayer networks of this chapter, the trainable parameters sit inside torch.nn.Linear layers. A Linear layer takes its inputs, multiplies them by a weight matrix, and adds a bias vector. Written compactly, its output is $y = Wx + b$, where $W$ is the weight matrix, $x$ is the input, and $b$ is the bias vector. Both $W$ and $b$ are stored as parameters of the layer, which is why both are counted.

Linear layers are sometimes called feedforward or fully connected layers. These names describe the same layer, not a different kind of parameter. Whatever name you use, the weight matrix and bias vector inside it are the trainable parameters that the count picks up.

Worked example

Counting a two-layer network

A model has two Linear layers in sequence: the first is Linear(3, 2), mapping 3 inputs to 2 outputs, and the second is Linear(2, 1), mapping 2 inputs to 1 output. All parameters have requires_grad set to True. How many trainable elements does the model have?

  1. 1
    Size the first weight matrix

    Linear(3, 2) has a weight matrix of shape 2 by 3, which holds 6 numbers.

  2. 2
    Size the first bias vector

    Its bias vector has one entry per output, so 2 numbers.

  3. 3
    Size the second weight matrix

    Linear(2, 1) has a weight matrix of shape 1 by 2, which holds 2 numbers.

  4. 4
    Size the second bias vector

    Its bias vector has 1 number.

  5. 5
    Sum the element counts

    6 + 2 + 2 + 1 = 11. Each tensor contributes its numel(), and the total is 11 trainable elements.

Result

11 trainable elements (8 in the first layer, 3 in the second).

Freezing Parameters and the Count

The requires_grad attribute is the switch. A parameter does not need any special type or placement to be trainable. What makes it trainable is that its requires_grad flag is set to True. When the flag is False, the same tensor is still part of the model, but it is left out of the count and is not updated during training.

python
Notes on the code
  • Setting requires_grad to False freezes the tensor without removing it from the model
  • The same counting expression then skips the frozen tensors
Layer 1 parameters8 trainableLayer 1 parametersfrozen, 0 trainableLayer 2 parameters3 trainableLayer 2 parameters3 trainableTotal11Total3adds 8adds 3adds 0adds 3
How does setting requires_grad to False on the first layer change the trainable total?

Inside the State Dictionary

After a model has been trained, its learned parameters are the result of all that work. If those parameters exist only in the running Python session, they are lost when the session ends. The state dictionary is the tool that captures them. Calling model.state_dict() returns a dictionary containing the state of the model, including its parameters. On its own, it does not write a file.

Each entry in the dictionary pairs a string key with a tensor. The key is the name of one parameter, and the value is the tensor of numbers that holds its current learned values. For the two-layer example above, the first layer's weight matrix would appear under one key and its bias vector under another. The diagram shows how a key maps to its tensor for a small generated layer with a 2 by 3 weight matrix and a 2-element bias.

0.weightkey2 by 3 tensorlearned weight values0.biaskey2-element tensorlearned bias valuesmaps tomaps to
What does model.state_dict() contain, and how does each string key map to its parameter tensor?

Following the Data Through torch.save

The source for this topic is a single line of code, run as the last line of a training script where model is a trained PyTorch model:

python
Notes on the code
  • torch.save is the function that writes data to a file
  • model.state_dict() is the first argument: the data to be written
  • "model.pth" is the second argument: the name of the file

What do you think happens?

Does torch.save(model.state_dict(), "model.pth") write the model object itself, or a dictionary of its state?

  • The model object itself

  • A dictionary of the model's state

Reveal answer
Answer:

A dictionary of the model's state

model.state_dict() returns a dictionary containing the state of the model, and only that dictionary is passed to torch.save. The file stores the learned values, not the model object.

The data moves in a fixed order. The model holds its parameters. The call model.state_dict() reads those parameters and returns them as a dictionary. That dictionary is passed as the first argument to torch.save(), which writes it to the file named by the second argument. The model itself never appears as the thing being written. Only the dictionary produced by state_dict() is written.

live modelholds its parametersmodel.state_dict()reads parametersstate dictionarysnapshot of learned valuestorch.save()writes the datamodel.pthfile on diskparametersreturnsfirst argumentsecond argument
How does data move from the live model, through state_dict(), into the file on disk?

When a save does not produce the result you expect, locate the failure by checking the three roles in order. First check the data: is the first argument a state dictionary produced by model.state_dict()? Second check the function: is the call to torch.save()? Third check the destination: is the second argument the file name you intended? Most failures sit in one of these three places, and the first check is where the result most often diverges.

Mistakes in Saving and Counting

  • Common mistake

    Expecting model.state_dict() to write a file

    Why it fails

    The call returns a dictionary in memory. It does not write anything to disk on its own.

    Do this instead

    Pass the dictionary to torch.save(), which writes it to the file named by the second argument.

  • Common mistake

    Expecting the saved file to hold the model object

    What it looks like
    torch.save(model, "model.pth") versus torch.save(model.state_dict(), "model.pth")
    Why it fails

    The data being written should be the state dictionary. The file stores the learned values as a dictionary, not the live model object.

    Do this instead

    Keep model.state_dict() as the first argument so the data written is the snapshot of the parameters.

  • Common mistake

    Counting tensors instead of elements

    What it looks like

    Reporting a layer with a 2 by 3 weight matrix and a bias as 2 parameters

    Why it fails

    The total is a count of individual numbers. The weight matrix alone contributes 6 elements.

    Do this instead

    Sum p.numel() over the trainable tensors, which gives 8 for that layer.

  • Common mistake

    Assuming a frozen parameter has been removed from the model

    Why it fails

    Setting requires_grad to False only removes the tensor from the trainable count and from updates.

    Do this instead

    Check the count with the requires_grad filter, and remember the tensor is still part of the model.

Best practice

Run the counting expression right after building a model and again after freezing layers. Comparing the two totals confirms that the requires_grad switch is doing what you intended.

Practice: Predict the Saved Contents

MEDIUM

A model is built as Linear(4, 3) followed by Linear(3, 2). Every parameter has requires_grad set to True. First, compute the trainable total by counting the elements in each weight matrix and bias vector. Then freeze the first layer and recompute the total. Finally, state what torch.save(model.state_dict(), "model.pth") writes and identify which of the three roles, data, function, or destination, you would check first if the saved file did not contain the expected values.

Hints
  • Linear(4, 3) has a weight matrix of shape 3 by 4 and a bias of 3 elements
  • Linear(3, 2) has a weight matrix of shape 2 by 3 and a bias of 2 elements
  • Freezing a layer sets requires_grad to False on each of its tensors, so they drop out of the count
  • The data is the state dictionary; check it first

Key Takeaways

  • The total number of trainable parameters is the sum of numel() over every parameter whose requires_grad attribute is True, so the count is of individual numbers, not tensors or layers.
  • Trainable parameters in torch.nn.Linear layers are the weight matrix and the bias vector, and freezing a parameter removes it from the count and from training updates without removing it from the model.
  • model.state_dict() returns a dictionary of the model's state, mapping string keys to parameter tensors, and does not write a file by itself.
  • torch.save(model.state_dict(), "model.pth") writes the state dictionary to model.pth; check the data argument first when a saved result differs from what you expected.

Related concepts

More concepts starting with I