My opinion, the main reason of such behavior is you are doing one optimiztion step once per epoch. This way you are optimizing for the best “epoch-level” loss. So the loss will converge, but it is not guaranteed to have optimal weights on per-sample basis. Doing the one optimization step per epoch is like doing “batch gradient descent” with all your data as one batch. The opposite will be doing optimiztion step every sample, which is in your case will be “online” or “stochastic” gradient descent. The third way is to try “mini-batch” gradient descent - which is in your case to accumulate gradients for several samples (e.g. 10-20, you decide) and do optimization step after that. As you can see, it is somehow the way of regularizing you model - when optimizing once per epoch the model will have a hard time to learn a concept of single sample, but when optimizing after every sample it would be hard for the model to “see the forest behind the trees”. If none of that doesn’t seem to have some sense for you, I would suggest to familiarize yourself with a concepts of batch, stochastic and mini-batch gradient descent. The great explanations on the topic you can find from the popular Andrew Ng Machine Learning or Deep Learning Courses.