Your code profiles the complete training including all epochs:
start_time = datetime.now()
for epoch in range(n_epochs):
  train_loss=0
  model.train()
  for iteration,(Xstage1,X_batch) in enumerate(Train_Loader):
    optimizer.zero_grad()
    ...
  print('Epoch: {} \tTraining loss:{:.6f}'.format(epoch+1,train_loss))
end_time = datetime.now()

start_time is called before the first epoch and iteration is executed, while end_time is called after all epochs are done.
@albanD’s code profiles just the backward call:
start = time.time()
out.sum().backward()
print("Took: ", time.time() - start)

So not a single batch is taking one hour, but the complete training, i.e. 10 epochs.