Thanks for your reply! I think you are right. In my code, the parmaters inside the mini-batch loop could be updated multiple times:
for idx in range(Y_net.size()[0]):
      dydx[idx,:] = torch.autograd.grad(Y_net[idx,0],X,create_graph=True)[0][idx,:]

I have changed the code by defining another tensor loss_g, and it can run smoothly now:



 XiaodongMa-MRI:

for epoch in range(Nepochs):
  print('starting epoch ' + str(epoch) + ', Learning rate = ' + str(LR))
  for batch_idx, (X, Y) in enumerate(loader):
    X.requires_grad = True
    Y_net = net(X)
    loss_g = torch.zeros([0.0])
    # loop over minibatch
    for idx in range(Y_net.size()[0]):
      dydx[idx,:] = torch.autograd.grad(Y_net[idx,0],X,create_graph=True)[0][idx,:]
      loss_g = loss_g + (dydx[:,torch.arange(0,Nin)]).sum()
    loss = loss_f(Y_net, Y) + loss_g
    optimizer.zero_grad()
    loss.backward(retain_graph=True)
    optimizer.step()



The reason for this implementation is that I wan to make sure the gradient of nn output wrt. the input is similar to some known values. I used (dydx[:,torch.arange(0,Nin)]).sum() here just for simplicity.
Xiaodong