MUnique:

        # backward step
        loss_ele.backward()



You may have to tell pytorch to retain the computation graph for future use. i.e., for gradient calculation of decoder.
        loss_ele.backward(retain_graph=True)

You may need retain_graph=True for loss_decoder.backward() as well.
But not for lossD.backward(), so that memory is released at the end.
Can you try with this?