We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← nn.Module Mechanics step 1 of 7
The layers the optimiser never saw
class Net(nn.Module):
def __init__(self, depth):
super().__init__()
self.layers = [nn.Linear(4, 4) for _ in range(depth)]
len(list(Net(3).parameters())) # 0
Not six. Zero.
Why
nn.Module overrides __setattr__. When you assign a Parameter or a
Module to an attribute, it is intercepted and put into the module’s
registry. That registry is what parameters(), state_dict(), .to() and
train() all walk.
A plain Python list is not a Module, so the assignment is ordinary. The
list holds the layers, the layers hold their parameters, and the module knows
about none of it.
What that costs
Everything, quietly:
-
model.parameters()is empty, so the optimiser is constructed over nothing andstep()updates nothing. Training runs. The loss does not move. -
state_dict()is empty, so checkpoints save nothing. -
model.to(device)moves nothing. -
model.eval()does not reach the layers, so dropout stays on during evaluation.
The forward pass works perfectly, which is what makes this hard to spot. Most people find it when the loss is flat and they check whether the optimiser has any parameters at all.
The containers
nn.ModuleList([...]) # a list, registered
nn.ModuleDict({...}) # a dict, registered
nn.Sequential(*layers) # a list that also defines forward
nn.ParameterList([...]) # for bare Parameters
ModuleList holds modules and you write the loop yourself. Sequential
calls them in order for you, which is right when the forward really is just
“apply each in turn” and wrong as soon as there is a skip connection.
The same trap, one level down
self.weights = [torch.nn.Parameter(...) for _ in range(n)] # invisible
self.weights = nn.ParameterList([...]) # registered
and the buffer version of it, which the devices track covered. The rule is the same every time: if the module should know about it, assign something the module can recognise.
Your task
Fix Net so its layers are registered, then:
def net_facts(depth: int) -> dict
builds a Net(depth) and reports the number of parameter tensors, the number
of state_dict entries, and the output length of a forward pass on a
4-element input.
Stuck?
PyTorch reference solution
Sign in to attempt this problem and reveal the reference solution.