The change in results is due to randomness. For example, your dataloader will shuffle differently the second time you run it, feeding your network slightly different batches, which results in a slightly different training run. Similarly, on network initialization, Pytorch (or any library for that matter) initializes your network with weights sampled randomly following some distribution. These weights will thus also be slightly different for different runs.

As your goal is to just get the model with the lowest validation loss I recommend that, during training, you track when your model has the lowest score and just save that model. Then, when in a later epoch, your model is slightly better, you just simply save that one again. At the end, instead of using the latest model, you just use the best model from a few epochs before.

If you would like to reduce the randomness of your algorithm or network, you can look into seeding to improve your reproducibility. Pytorch has a short page on how to improve reproducibility.