It seems that the values of params a, b do change if they are incorporated into the input to the loss since loss.backward() computes gradients and changes the values of a, b accordingly.
E.g.:
logits = model(X)
logits += a * b
loss = cross_entropy(logits, y)
loss.backward()

But what if we want to optimise over params which implicitly affect the loss and are not a direct input to the loss function?