Drills
Four tasks. You do not need to create any files — write and run in a python
console or a throwaway scratch.py, in the folder with transfer.py. Each has a
check.
1. The size of the head
Write head_params(num_classes) — how many parameters the new head has (512
inputs into each output, and one bias each).
Check: head_params(4) → 2,052, head_params(7) → 3,591,
head_params(1000) → 513,000 (what the original fc had).
Now compare with count_trainable(build_resnet(7))[0]. They must agree.
2. Which tensors are trained
build_resnet(4, finetune_last_block=True). Print the names of every
parameter whose requires_grad is True (model.named_parameters()), and how
many there are.
Check: 17 tensors; the first three are layer4.0.conv1.weight,
layer4.0.bn1.weight, layer4.0.bn1.bias, and the last two fc.weight and
fc.bias. 45 tensors are frozen.
Notice that there is a bn inside layer4 too — the BatchNorm weights are
unlocked as well.
3. Keep BatchNorm frozen
Write train_keeping_bn_frozen(model): it calls model.train() and then puts
every nn.BatchNorm2d back into eval() mode (model.modules()).
Check: build build_resnet(4), save model.bn1.running_mean.clone(), call your
function, pass torch.randn(8, 3, 224, 224) through and show that the mean did
not change (difference 0.0). With a plain model.train() that difference
was 0.0128.
Then try putting it into your training and compare — over several seeds, because steps 5 and 7 showed that one seed proves nothing.
4. A feature cache
When the backbone is frozen, computing the same 512 numbers again in every epoch is a waste. Write:
extract(model, loader)→(features, labels): goes through all pictures once ineval()mode with no gradients and returns an[N, 512]tensor (to get 512, drop the finalfclayer:nn.Sequential(*list(model.children())[:-1], nn.Flatten())).- Train an
nn.Linear(512, num_classes)only on those numbers — noDataLoader, no ResNet.
Check with standin_data (224 px, the same split): the features are
(640, 512) and (160, 512); in our run the extraction took 11 s, and 30
epochs of the head with Adam lr 0.01 took 0.4 s. Validation accuracy was
93.1 % after 5 epochs, and 93.8 % after 10 and 30.
Compare with step 5's 92.5 % in 118 s. This is not the same set-up: here
BatchNorm is in eval() mode throughout (see task 3) and there is no
augmentation. One seed, 160 pictures — do not conclude "better", but the speed
is real: you can repeat the same experiment hundreds of times.