Not sure if I should write on this topic but I have the same problem, the loss I get doesn’t seem to decrease.
I have played around with learning rate and batch size and at best I can get it to be around constant.  Only when I train on a very small size with a specific rate and batch size I can get it to decrease.
My data length is ~200K, how many epochs and layers would I normally need for that?
Also, what is the loss I should be happy with? Is it relative with respect to the initial loss or should I aim for an absolute interval close to zero?
import torch
import numpy as np
import torchvision
from torchvision import transforms, datasets
import torch.nn as nn
import torch.nn.functional as F
import pickle
import torch.optim as optim
from tqdm import tqdm
import time

class Net(nn.Module):

    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(5, 128)
        self.bn1 = nn.BatchNorm1d(128)
        self.fc2 = nn.Linear(128, 128)    
        self.bn2 = nn.BatchNorm1d(128)
        self.fc3 = nn.Linear(128, 128) 
        self.bn3 = nn.BatchNorm1d(128)
        self.fc4 = nn.Linear(128, 2) 

    def forward(self,x):
        x = F.relu(self.bn1(self.fc1(x)))
        x = F.relu(self.bn2(self.fc2(x)))
        x = F.relu(self.bn3(self.fc3(x)))
        x = self.fc4(x)
        return F.log_softmax(x,dim=1)
    
    
    
with open('x-u-data.pkl','rb') as f:  
    training_data = pickle.load(f)    
    
 
 
net = Net()    


optimizer = optim.Adam(net.parameters(),lr=10**(-3))
EPOCHS = 10_000

X = torch.zeros( (len(training_data), 5) )
Y = torch.zeros( (len(training_data), 2) )

for ii in range(len(training_data)):
    cur_data = training_data[ii]
    X[ii,:] = torch.FloatTensor(cur_data[0:5]).view(-1,5)
    Y[ii,:] = torch.FloatTensor(cur_data[5:7])

BATCH_SIZE = 200
for epoch in tqdm(range(EPOCHS)):
    for ii in tqdm(range(0, len(training_data), BATCH_SIZE)):    
        #barch of feature sets and labels
       
        batch_X = X[ii:ii+BATCH_SIZE].view(-1, 5)
        batch_Y = Y[ii:ii+BATCH_SIZE]        
        
        net.zero_grad() #resetting gradient
        output = net(batch_X)
        
        loss = F.mse_loss(output,batch_Y)
        loss.backward()
        optimizer.step()
  
    print(loss)