Somebody showed me the mistake I was making…
All those Conv2D and BatchNorm operations I was using in the forward() method needed to be moved into the __init__() constructor. In general, any learnable layers should go in the constructor. That way, Pytorch is (somehow) able to keep track of all the model parameters.
N.B. It’s OK to put non-trainable operations like activations and reshapes etc. in the forward() method.