Model training converges to a fixed value of loss with low accuracy
I have been trying to train a simple model on Chinese MNIST Kaggle dataset. However it keeps converging to a CrossEntropyLoss of 2.708050 even with model training setting picked up from pytorch tutorials. The losses change but converge to a high value with low accuracy (everytime &lt; 10%).

There is no error in how the dataset is created.
I had initially tried custom made models, training loops as well as test loops, but those didn't work. Finally, I switched to tested functions to figure out the problem. Here is how I have defined the dataset function, initialized using a list of filepaths and target values converted to indices using a value_idx dictionary.
class Custom_dataset(Dataset):
    def __init__(self,filepath,value):
        data = [cv2.imread(fp,-1) for fp in filepath]
        data = [i/np.max(i) for i in data]
        data = torch.tensor(data, dtype=torch.double)
        data = data.view(-1,1,64,64)
        
        self.data = data
        
        self.size = len(value)
        assert len(filepath)==len(value), print('length mismatch')
        
        self.index = torch.tensor([value_idx[i] for i in value]).reshape(-1,1)
        self.target = torch.zeros((self.size,15)).scatter_(dim=1
                                                           ,index=self.index#.unsqueeze_(dim=1)
                                                           ,value=1)
        
        print('data shape ',self.data.shape)
        
    def __getitem__(self,idx):
        return (self.data[idx,:],self.target[idx])
    
    def __len__(self):
        return self.size
        

Here is my training loop and test loop (also picked up from the pytorch tutorials page)
def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    for batch, (X, y) in enumerate(dataloader):
        X,y = X.to(device), y.to(device)
        # Compute prediction and loss
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * len(X)
            print(f&quot;loss: {loss:&gt;7f}  [{current:&gt;5d}/{size:&gt;5d}]&quot;)


def test_loop(dataloader, model, loss_fn):
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    with torch.no_grad():
        for X, y in dataloader:
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    print(f&quot;Test Error: \n Accuracy: {(100*correct):&gt;0.1f}%, Avg loss: {test_loss:&gt;8f} \n&quot;)

and model definition
class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = Sequential(Linear(64*64,64*32)
                       ,ReLU()
                       ,Linear(64*32,32*32)
                       ,ReLU()
                       ,Linear(32*32,16*32)
                       ,ReLU()
                       ,Linear(32*16,16*16)
                       ,ReLU()
                       ,Linear(16*16,15)
                       ,Sigmoid(),)

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

with the final code being
model_mk_47 = NeuralNetwork().double().to(device)
NUM_EPOCHS=10
for i in tqdm(range(NUM_EPOCHS)):
    train_loop(train_dl,model_mk_47, CrossEntropyLoss(), Adam(model_mk_47.parameters()) )


Calculating accuracy with sigmoid is not an issue as you are using argmax, and softmax and sigmoid will return different values but they will be in the same order. However, one issue i'm seeing with your code is that you are including your activation within your forward pass code. What I might encourage you to do is to remove the Sigmoid from your model. This is because CrossEntropyLoss takes in:

The input is expected to contain raw, unnormalized scores for each class. input has to be a Tensor of size (C)(C) for unbatched input, (minibatch, C)(minibatch,C) or (minibatch, C, d_1, d_2, ..., d_K)(minibatch,C,d1,d2,...,d K) with K \geq 1K≥1 for the K-dimensional case. The last being useful for higher dimension inputs, such as computing cross entropy loss per-pixel for 2D images.

This means it is expecting logits (which are the output of the last layer without any activation). If you want to use a sigmoid (would recommend softmax), use functional softmax after the loss has been calculated (or include a flag which only activates the model when self.training == True in the model code.
I assume you are getting the performance of your model on your train data. If this is not improving it either means your data pipeline is faulty or your approach isn't working. Are you sure that your labels align properly with your images?
