Q:

pytorch F.cross_entropy does not apply gradient to weights

I'm trying to train an MLP from scratch using torch tensors and some of the built-in loss functions. I have IRIS-data downloaded and stored in tensor (100, 4) and labels (100) (integers 0-2) in data_tr and targets_tr.  I have enabled gradients on the input data data_tr.requires_grad=True
I have a 2-layer MLP initialized like this:
W1 = torch.randn([4, 64], requires_grad=True)
W2 = torch.randn([64, 3], requires_grad=True)
b1 = torch.tensor([1.0], requires_grad=True)
b2 = torch.tensor([1.0], requires_grad=True

I understand that I should train like this:
for epoch in range(num_epochs):
    W1.grad = None
    W2.grad = None
    b1.grad = None
    b2.grad = None

    f = torch.relu(data_tr @ W1 + b1) @ W2 + b2
    error = torch.nn.functional.cross_entropy(f, targets_tr)
    error.backward()

    W1 = W1 - lr * W1.grad
    W2 = W2 - lr * W2.grad
    b1 = b1 - lr * b1.grad
    b2 = b2 - lr * b2.grad

Which makes a 2 layer MLP and cross_entropy applies softmax. 
The problem now is that none of the weights or biases (W1, W2, b1, b2) has any gradients after the backward pass. So I get a TypeError: unsupported operand type(s) for *: 'float' and 'NoneType' on the first attempt to updating a weight. 

A:

If you want to update the weights without using an optimizer, you have to either use torch.no_grad() or update their data directly to ensure autograd is not tracking the update operations. 
with torch.no_grad():
    W1 -= lr * W1.grad
    W2 -= lr * W2.grad

b1.data = b1 - lr * b1.grad
b2.data = b2 - lr * b2.grad

Note in the first case, if you do not subtract assign, the requires_grad will be set to False for the weights which again will result in None values for the gradients. 
with torch.no_grad():
    W1 = W1 - lr * W1.grad
    W2 = W2 - lr * W2.grad
print(W1.requires_grad, W2.requires_grad)
>>> False False

