Inspecting Model Parameters
The total number of trainable parameters is the sum of numel() over every parameter whose requires_grad attribute is True.
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?
Reveal 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.
2213 (for the source model used in this lesson)model.parameters()yields every learnable tensor the model holds- if p.requires_grad discards any tensor whose
requires_gradflag is False p.numel()converts each surviving tensor into the count of elements it containssum()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.
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.
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.
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?
- 1Size the first weight matrix
Linear(3, 2)has a weight matrix of shape 2 by 3, which holds 6 numbers. - 2Size the first bias vector
Its bias vector has one entry per output, so 2 numbers.
- 3Size the second weight matrix
Linear(2, 1)has a weight matrix of shape 1 by 2, which holds 2 numbers. - 4Size the second bias vector
Its bias vector has 1 number.
- 5Sum the element counts
6 + 2 + 2 + 1 = 11. Each tensor contributes its
numel(), and the total is 11 trainable elements.
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.
- Setting
requires_gradto False freezes the tensor without removing it from the model - The same counting expression then skips the frozen tensors
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.
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:
- 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?
Reveal 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.
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 failsThe call returns a dictionary in memory. It does not write anything to disk on its own.
Do this insteadPass 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 liketorch.save(model, "model.pth") versus torch.save(model.state_dict(), "model.pth")Why it failsThe data being written should be the state dictionary. The file stores the learned values as a dictionary, not the live model object.
Do this insteadKeep 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 likeReporting a layer with a 2 by 3 weight matrix and a bias as 2 parameters
Why it failsThe total is a count of individual numbers. The weight matrix alone contributes 6 elements.
Do this insteadSum 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 failsSetting
requires_gradto False only removes the tensor from the trainable count and from updates.Do this insteadCheck the count with the requires_grad filter, and remember the tensor is still part of the model.
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
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 elementsLinear(3, 2)has a weight matrix of shape 2 by 3 and a bias of 2 elements- Freezing a layer sets
requires_gradto 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 whoserequires_gradattribute 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.