I new to Pytorch so I decided to start with cats vs dogs dataset. The problem which I don’t understand is that for some reason no matter how I change my model my loss doesn’t decrease below 0.69. Loss reaches this number after first epoch and after that no matter how many epochs it is not changing (in 10 epochs it reached 0.687). I have tried different layer sizes, diferent in-out chanels numbers, different loss functions (BCELoss, CrossEntropyLoss), different optimizers(Adam, SGD). Is there possibility that my training data is badly constructed, or maybe something else?
CODE:
import os
import cv2
import numpy as np
import torch
from torch.utils.data import DataLoader, TensorDataset
from torch import nn
import torch.nn.functional as F
from torch.optim import Adam, SGD
from pkbar import Kbar
import pickle


class DogCatClassifier(nn.Module):

    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 64, 5)
        self.conv2 = nn.Conv2d(64, 128, 5)
        self.conv3 = nn.Conv2d(128, 256, 5)
        self.conv4 = nn.Conv2d(256, 512, 3)

        self.fc1 = nn.Linear(512, 64)
        self.fc2 = nn.Linear(64, 32)

        self.fc_out = nn.Linear(32, 1) # For BCLoss
        # self.fc_out = nn.Linear(32, 2)

    def forward(self, tensor):
        tensor = F.max_pool2d(F.relu(self.conv1(tensor)), (2, 2))
        tensor = F.max_pool2d(F.relu(self.conv2(tensor)), (2, 2))
        tensor = F.max_pool2d(F.relu(self.conv3(tensor)), (2, 2))
        tensor = F.max_pool2d(F.relu(self.conv4(tensor)), (2, 2))

        tensor = torch.flatten(tensor, start_dim=1)

        tensor = F.relu(self.fc1(tensor))
        tensor = F.relu(self.fc2(tensor))

        tensor = self.fc_out(tensor)
    
        # return tensor # For Cross Etropy loss
        return torch.sigmoid(tensor)


    def create_train_data(path, img_size):
        labels = []
        images = []

        for file in os.listdir(path):
            if 'cat' in file:
                labels.append(0)
            else:
                labels.append(1)

        image = cv2.imread(path + file, cv2.IMREAD_GRAYSCALE)
        image = cv2.resize(image, (img_size, img_size))
        image = np.array(image)

        images.append(image)

        images = np.array(images)

        x_train = torch.tensor(images, dtype=torch.float32)
        y_train = torch.tensor(labels, dtype=torch.long)

        x_train = x_train / 255.0
        x_train = x_train.unsqueeze(1)  # Because it is grayscale image

        return x_train, y_train


EPOCHS = 5
BATCH_SIZE = 100
IMG_SIZE = 64

# x_train, y_train = create_train_data('cats_vs_dogs/train/train/', IMG_SIZE)
# with open('x_train.pickle', 'wb') as file:
#     pickle.dump(x_train, file)
#
# with open('y_train.pickle', 'wb') as file:
#     pickle.dump(y_train, file)

with open('x_train.pickle', 'rb') as file:
    x_train = pickle.load(file)

with open('y_train.pickle', 'rb') as file:
    y_train = pickle.load(file)

y_train = torch.tensor(y_train, dtype=torch.float)

train_dataset = TensorDataset(x_train, y_train)
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)

model = DogCatClassifier().cuda()

optimizer = Adam(model.parameters(), lr=0.01)
# optimizer = SGD(model.parameters(), lr=0.01, momentum=0.9) # Tried momentum: 0.1-0.9

# loss_function = nn.CrossEntropyLoss()
# loss_function = nn.MSELoss()
loss_function = nn.BCELoss()

for epoch in range(EPOCHS):
    print()
    print(f"Epoch: {epoch + 1}/{EPOCHS}")
    kbar = Kbar(target=len(x_train) / BATCH_SIZE, width=32)
    i = 0

    for images, labels in train_loader:
        images = images.cuda()
        labels = labels.cuda()

        preds = model(images)
    
        loss = loss_function(preds, labels)

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

        kbar.update(i, values=[("loss", loss)])
        i += 1