Thanks a lot I really appreciate your kind help. but why does it work when I backprop from loss1 !?
for example
def loss_function(output_e, outputs, imgs, device):
 
    criterion = nn.MSELoss()
    assert outputs.shape == imgs.shape ,f'outputs.shape : {outputs.shape} != imgs.shape : {imgs.shape}'
    
    imgs.requires_grad = True 
    loss1 = criterion(outputs, imgs)
    loss1.backward(retain_graph=True)
    #output_e.backward(torch.ones(outputs_e.size()).to(device), retain_graph=True)
    print(imgs.grad)
    loss2 = torch.mean(pow(imgs.grad,2))
    imgs.requires_grad = False 
    imgs.grad.data.zero_()
    loss = loss1 + loss2 
    return loss

If I use loss1.backward(retain_graph=True) I dont get any errors at all! but when I comment this and instead try  to do output_e.backward(torch.ones(outputs_e.size()).to(device), retain_graph=True) it fails.
The only way it works is that I set the imgs.requires_grad inside the training loop that is :
the training loop :
for e in range(epochs):
    for i, (imgs, labels) in enumerate(dataloader_train):
        imgs = imgs.to(device)
        labels = labels.to(device)

        imgs.requires_grad_(True)

        outputs_e, outputs = model(imgs)

        loss = loss_function(outputs_e, outputs, imgs, lam,device)
        imgs.requires_grad_(False)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    print(f'epoch/epochs: {e}/{epochs} loss: {loss.item():.4f}')

and the loss :
def loss_function(output_e, outputs, imgs, device):
 
    criterion = nn.MSELoss()
    assert outputs.shape == imgs.shape ,f'outputs.shape : {outputs.shape} != imgs.shape : {imgs.shape}'
    
    loss1 = criterion(outputs, imgs)
    #loss1.backward(retain_graph=True)
    output_e.backward(torch.ones(outputs_e.size()).to(device), retain_graph=True)
    loss2 = torch.mean(pow(imgs.grad,2))
    imgs.grad.data.zero_()
    loss = loss1 + loss2 
    return loss

and now both loss.backward and outputs_e.backward() work without a hitch!