Потеря CNN в Pytorch не меняется

#python #machine-learning #pytorch #conv-neural-network #loss-function

#python #машинное обучение #pytorch #conv-нейронная сеть #потеря-функция

Вопрос:

Я создаю CNN с использованием Pytorch для решения проблемы классификации изображений между людьми, которые носят маски, а кто нет. Изображения преобразуются в формат 256×256 с 3 каналами.

При запуске кода, независимо от начального значения потерь, останется неизменным. Я попытался изменить скорость обучения, однако изменений нет. Я также попытался использовать простой одиночный линейный слой, чтобы проверить, была ли модель неверной. Я предполагаю, что цикл обучения неверен, но при сравнении с другими образцами циклов обучения мой код выглядит правильно.

Модель

 class NetConv(nn.Module):
    def __init__(self):
        super(NetConv, self).__init__()
        # self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
        # self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)
        # self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)
        self.conv1 = nn.Conv2d(in_channels= 3, out_channels = 8, kernel_size = 3)
        self.conv2 = nn.Conv2d(in_channels= 8, out_channels = 16, kernel_size = 3)
        self.conv3 = nn.Conv2d(in_channels= 16, out_channels = 32, kernel_size = 3)

        self.fc1 = nn.Linear(30*30*32, 1000)
        self.fc2 = nn.Linear(1000, 200)
        self.fc3 = nn.Linear(200, 3)

    def forward(self, x):
        # print(x.shape)
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv3(x))
        x = F.max_pool2d(x, 2)

        x = x.reshape(-1, 30*30*32)

        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        x = F.softmax(x, dim=1)

        # print(x.shape)
 
        return x
 

Загрузчик данных

 class faceMaskDataset(Dataset):
    def __init__(self, img_folder, annot_folder, transform=None):
        # Extracting image name and class from xml file
        desc = []
        for dirname, _, filenames in os.walk(annot_folder):
            for filename in filenames:
                desc.append(os.path.join(dirname, filename))

        img_name,label = [],[]

        for d in desc:
            content = []
            n = []

            with open(d, "r") as file:
                content = file.readlines()
            content = "".join(content)
            soup = BeautifulSoup(content,"html.parser")
            file_name = soup.filename.string
            name_tags = soup.find_all("name")
            
            for t in name_tags:
                n.append(t.get_text())
                
            # selecting tag with maximum occurence in an image (If it has multiple tags)
            name = max(set(n), key = n.count)
        
            img_name.append(file_name)
            label.append(name)

        labels = pd.get_dummies(label)
        print(labels.head())

        # Our target classes
        classes = list(labels.columns)
        print(classes)

        data, target = [],[]
        img_h, img_w = 256, 256

        # Loading images and converting them to pixel array
        for i in range(len(img_name)):
            name = os.path.join("./images/1", img_name[i])
            image = cv2.imread(name)
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
            image = cv2.resize(image, (img_w, img_h), cv2.INTER_AREA)
            
            data.append(image)
            target.append(tuple(labels.iloc[i,:]))

        print(type(data))
        # data = np.array(data)
        data = np.array(data) / 255 # Normalise pixel data to between 0 and 1
        target = np.array(target)
        
        data = np.swapaxes(data, 1, 3)
        data = np.swapaxes(data, 2, 3)

        self.data = data
        
        self.target = target
        self.transform = transform

    def __len__(self):
        return len(self.target)

    def __getitem__(self, index):
        image = self.data[index,:]
        label = (self.target[index])
        return (image, label)
 

Цикл обучения

 def train(model, optimizer, loss_fn, train_dl, val_dl, epochs=20, device='cuda'):

    for epoch in range(epochs):

        model.train()
        train_loss         = 0.0
        num_train_correct  = 0
        num_train_examples = 0

        for i, (image, target) in enumerate(train_dl):
            image = image.to(device).float()
            target = target.to(device)

            optimizer.zero_grad()

            output = model(image)

            loss = loss_fn(output, torch.max(target, 1)[1]) 

            loss.backward()
            optimizer.step()

            train_loss          = loss.data.item() * image.size(0)
            num_train_correct   = (torch.max(output, 1)[1] == torch.max(target, 1)[1]).sum().item()

            num_train_examples  = image.shape[0]

        train_acc   = num_train_correct / num_train_examples
        train_loss  = train_loss / len(train_dl.dataset)
        print("Epoch: -, Loss: %.3f, Acc: %.3f" % (epoch, train_loss, train_acc))
 

Комментарии:

1. Для какой функции потерь вы используете loss_fn ?

2. Если вы используете categorical cross-entropy в качестве функции потерь, то вам нужно удалить softmax из прямого прохода, так как categorical cross-entropy сам вычисляет softmax для вас.

3. @KhalidSaifullah, он включает a nn.LogSoftmax , чтобы быть более правильным.

4. @Ivan Я использую nn.CrossEntropyLoss() для своих loss_fn . Удаление по softmax -прежнему дает тот же результат.

5. Не могли бы вы предоставить код, в который вы вызываете train model помощью ‘s и optimizer ‘s init)