The shape of the params has absolutely no relation to the required shape of the argument to out.backward.
params is a list containing the weight tensors of the various modules in your net, so in this case the net has two Conv2d layers which have 2 tensors of parameters each (.weight and .bias), and three Linear layers which each have 2 tensors of parameters (.weight and .bias). That makes 10 tensors in total.

The last layer of net has 10 outputs which is why out.backward needs 10 inputs. One per output.

Calling out.backward() will --calculate-the-gradient-of-the-output–. EDIT: out.backward() is equivalent to out.backward(torch.Tensor([1]))
Usually we need the gradient of the loss. e.g.

out = net(input)
loss = torch.nn.functional.mse_loss(out, target)
loss.backward()


Each time you run .backward() the stored gradients for each parameter are updated by adding the new gradients. This allows us to cumulate gradients over several samples or several batches before using the gradients to update the weights. Once you have updated the weights you don’t want to keep those gradients around because if you reuse them, then you will push the weights too far.