Oh, thanks, what you said helped me a lot. I always think wrongly that it is only separating softmax and NLLLoss than will lead to numerical instability.
I also found the reason why torch combines log and softmax in documentation 21 of torch.nn.functional.log_softmax.

torch.nn.functional.log_softmax(input, dim=None, _stacklevel=3)
Applies a softmax followed by a logarithm.
While mathematically equivalent to log(softmax(x)), doing these two operations separately is slower, and numerically unstable. This function uses an alternative formulation to compute the output and gradient correctly.

So would you please explain why separating softmax and log is numerically unstable ? giving me some materials about that is also welcome.
Thank you.