You are calling loss_batch(model, loss_fn, x, y, metric) in val_evaluate inside the torch.no_grad() block, which is the correct thing to do.
However, loss_batch expects the arguments: model, loss_fn, x, y, opt = None, metric = None, so you are currently passing metric as the opt argument.
Inside loss_batch, the opt argument will be checked for not None and the backward call will be executed.
Passing metric=metric solves this issue, as opt is then indeed None.