<< extracting stats from activated layers - training >>

The (2) corresponds to the layer name.
If you just pass the layers into an nn.Sequential module, the name will correspond to the index in this sequential block.
However, you could also pass custom names:

model = nn.Sequential(OrderedDict([
          ('conv1', nn.Conv2d(1,20,5)),
          ('relu1', nn.ReLU()),
          ('conv2', nn.Conv2d(20,64,5)),
          ('relu2', nn.ReLU())
        ]))
    
print(model)
> Sequential(
  (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
  (relu1): ReLU()
  (conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
  (relu2): ReLU()
)
print(id(model.conv1) == id(model[0]))
> True

In your example code, you just register the hook to the complete model, so that input will correspond to the input data and output to the result of your last layer.