Thank you to point that out for me. I will modify my code shortly. But it still doesn’t explain why the loss is not changing at all. Any ideas?
The modified code now is
def loop_stack(loss, acc):
    if loss == None:
        print(list(map(lambda x: str(x)[1:-1].split(" ")[0], acc)))
        return
    new_acc = acc[:] + [loss]
    try:
        losses_child = list(map(lambda x: x[0], loss.next_functions))
        for l in losses_child:
            loop_stack(l, new_acc)
    except:
        print(list(map(lambda x: str(x)[1:-1].split(" ")[0], acc)))
        return
    
def print_backprop(loss):
    tmp = loss.grad_fn
    loop_stack(tmp, [])

The back-propagated stack is now make sense. But this doesn’t explain why the loss is not updating the backbone model. I can verify this by the following snippet
print(testInput)
print(backboneNet(testInput))
train(testInput)
print(testInput)
print(backboneNet(testInput))

And like I mentioned before if I set lossNet model requires_grad=False, it raise this error
element 0 of tensors does not require grad and does not have a grad_fn and the output of print_backprop is also an empty list.
So I suspect my code isn’t correct, but I am lost on how to solve this problem
Update #1

After some debugging, I found that if I remove o.data.clamp_(0, 1) from my train function, the back-propagate stack is now include the backbone module’s layer. I think it is because clamp operation is not differentiable, so it just stop there. But the loss.backward() is still not updating the backbone model
Update #2

I think I have found the bug. The line where I wrote optim.LBFGS([testInput.requires_grad_()]) need to be changed to optim.LBFGS(backboneNet.parameters())
Thank you for your support @albanD