You wouldn’t need to call to(device) on each tensor and module and should make sure to register them properly instead so that a single model.to(device) operation would move all internal data to the specified device.
Some recommendations:

Remove self.m = torch.tensor(Xtr.shape, dtype=int)[1].to(self.device) if this tensor is only needed to provide a shape and pass the shape information as an int instead to the layer.
I’m not sure why you are initializing data inside the model via self.Xtr = torch.from_numpy(Xtr).float().to(self.device), but would define it outside the model.
Remove the to(device) ops on each layer, create the model once and move it to the device via model.to(device).

This should work instead:
class AE(torch.nn.Module):
    def __init__(self, m):
        super(AE, self).__init__()
        
        self.encoder = nn.Sequential(
            nn.Linear(m, 2048), 
            nn.ReLU(),
            nn.Linear(2048, 1024)
        )
        self.decoder = nn.Sequential(
            nn.Linear(1024, 2048),
            nn.ReLU(),
            nn.Linear(2048, m)
        )
        
    def forward(self, X):
        X = self.encoder(X)
        X = self.decoder(X)
        return X
    

model = AE(10)
device = 'cuda'
model.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
x = torch.randn(1, 10, device=device)

out = model(x)
out.mean().backward()
optimizer.step()