@ptrblck
After doing some research and making some changes to the model.
I have updated my model architecture as follows. It’s task is now to perform binary classification.
class StatusNN(nn.Module):
    
    def __init__(self, input_size, hidden_size, num_classes=1):
        super().__init__()
        self.fc1 = nn.Linear(input_size, 512)
        self.fc2 = nn.Linear(512, hidden_size)
        self.fc3 = nn.Linear(hidden_size, num_classes)
        self.sigmoid = nn.Sigmoid()
        self.relu = nn.ReLU()
    def forward(self, x):
        x = self.fc1(x)      # [batch_size, 512] 
        x = self.relu(x)     # [batch_size, 512]
        x = self.fc2(x)      # [batch_size, 64]
        x = self.relu(x)     # [batch_size, 64]
        x = self.fc3(x)      # [batch_size, 1]
        x = self.sigmoid(x)  # [batch_size, 1]
        return x
    
    def predict(self,x):
        out = self.forward(x).detach().numpy()
        return np.round(out)

I initiate the model, loss_fn and optmizer:
USE_CUDA = False #torch.cuda.is_available()
device = 'cuda' if USE_CUDA else 'cpu'

input_size = dataset_x.shape[1]
hidden_size = 512
lr = 1e-3

model = StatusNN(input_size, hidden_size).to(device)
criterion = nn.BCELoss().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=lr, momentum=0.6)

And this is my training loop:
def train(model, dataloader, num_epochs):

    model.train()
    losses = list()
    ts = time.time()
    for epoch in range(num_epochs):
        epoch_losses = list()
        for idx, (X, y) in enumerate(trainloader):
            
            X, y = X.to(device), y.to(device)

            out = model(X)
            loss = criterion(out, y.unsqueeze(1))
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            epoch_losses.append(loss.item())
            
        losses.append(np.mean(epoch_losses))
        if epoch % 2 == 0:
            print('Epoch: {}, Loss: {}'.format(epoch, np.mean(epoch_losses)))
    te = time.time()
    
    fig, ax = plt.subplots()
    ax.plot(range(num_epochs), losses)
    plt.show()
    
    mins = int((te-ts) / 60)
    secs = int((te-ts) % 60)
    print('Training completed in {} minutes, {} seconds.'.format(mins, secs))
    
    return losses, model

However, even after all of this I still get this asymptotic, plateauing effect with my loss as it is not converging to 0, but some arbitrary value.
Could the issue lie in my training loop?
I have tried overfitting the model on the first batch to the same result. I have also tried balancing my dataset using SMOTE.
Thank you so much for your help so far, I really appreciate it.