Pytorch NN培训问题:NN的损失不会减少

时间:2020-06-01 16:50:11

标签: pytorch classification

我想将随机Instagram图片分类为“图片有狗”或“图片没有狗”。 为了训练我的NN对狗进行分类,我想使用Stanford Dogs数据集,因此我大约有20.000张训练图像,包括不同品种的不同狗。

但是在训练我的NN时,损失并没有减少,我检查了学习率不同,有无辍学的情况。

任何人都可以在以下代码中给出提示或任何人看到错误吗?:

import torch
import torchvision
from torchvision import transforms
from PIL import Image
from os import listdir
import os
import random
import torch.optim as optim
from torch.autograd import Variable
import torch.nn.functional as F
import torch.nn as nn

TRAINDATAPATH = 'C:/Users/.../Desktop/train/'
TESTDATAPATH = 'C:/Users/.../Desktop/#apfel/'

"""normalize = transforms.Normalize(
   mean=[0.485, 0.456, 0.406],
   std=[0.229, 0.224, 0.225]
)"""
normalize = transforms.Normalize(
   mean=[0.5, 0.5, 0.5],
   std=[0.5, 0.5, 0.5]
)
transforms = transforms.Compose([transforms.Resize(256),
                                 transforms.CenterCrop(256),
                                 transforms.ToTensor(),
                                 normalize])

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
train_data_list = []
target_list = []
train_data = []

batch_size = 1

files = listdir(TRAINDATAPATH)

for i in range(len(listdir(TRAINDATAPATH))):
    try:
        f = random.choice(files)
        files.remove(f)
        img = Image.open(TRAINDATAPATH + f)
        img_tensor = transforms(img) # (3,256,256)
        train_data_list.append(img_tensor)
        isObj = 1 if 'obj' in f else 0
        isNotObj = 0 if 'obj' in f else 1
        target = [isObj, isNotObj]

        target_list.append(target)
        if len(train_data_list) >= 1:
            train_data.append((torch.stack(train_data_list), target_list))
            train_data_list = []
            target_list = []
            print('Loaded batch ', int(len(train_data)/batch_size), 'of ', int(len(listdir(TRAINDATAPATH))/batch_size))
            print('Percentage Done: ', 100*int(len(train_data)/batch_size)/int(len(listdir(TRAINDATAPATH))/batch_size), '%')
    except Exception:
        print("Error occured but ignored")
        print(str(Exception))
        continue

class Netz(nn.Module):
    def __init__(self):
        super(Netz, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, kernel_size=5)
        self.conv2 = nn.Conv2d(6, 12, kernel_size=5)
        self.conv3 = nn.Conv2d(12, 18, kernel_size=5)
        self.conv4 = nn.Conv2d(18, 24, kernel_size=5)
        self.fc1 = nn.Linear(3456, 1000)
        self.fc2 = nn.Linear(1000, 2)

    def forward(self, x):
        x = self.conv1(x)
        x = F.max_pool2d(x,2)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.max_pool2d(x,2)
        x = F.relu(x)
        x = self.conv3(x)
        x = F.max_pool2d(x,2)
        x = F.relu(x)
        x = self.conv4(x)
        x = F.max_pool2d(x,2)
        x = F.relu(x)
        x = x.view(-1,3456)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)

        return torch.sigmoid(x)

model = Netz()
model.to(torch.device("cuda" if torch.cuda.is_available() else "cpu"))

optimizer = optim.Adadelta(model.parameters(), lr=10)
def train(epoch):
    global model

    model.train()
    batch_idx = 0
    for data, target in train_data:
        batch_idx += 1
        data = data.to(torch.device("cuda" if torch.cuda.is_available() else "cpu"))
        target = torch.Tensor(target).to(torch.device("cuda" if torch.cuda.is_available() else "cpu"))
        data = Variable(data)
        target = Variable(target)
        optimizer.zero_grad()

        output = model(data)

        criterion = F.binary_cross_entropy
        loss = criterion(output, target)
        loss.backward()

        optimizer.step()
        print('Train Epoch: '+ str(epoch) + '\tLoss: ' + str(loss.data.item()) )


def test():
    global model

    model.eval()
    files = listdir(TESTDATAPATH)
    f = random.choice(files)
    img = Image.open(TESTDATAPATH + f)
    img_eval_tensor = transforms(img)
    img_eval_tensor.unsqueeze_(0)
    data = Variable(img_eval_tensor.to(torch.device("cuda" if torch.cuda.is_available() else "cpu")) )
    out = model(data)
    string_prediction = str(out.data.max(0, keepdim=True)[1])
    print(string_prediction[9:10])

for epoch in range(1,4):
    train(epoch)
i = 100
while i > 0:
    test()
    i -= 1

在TRAINDATAPATH中,有成千上万个带有文件名“ obj_XXX.jpg”的Dog图像,而另一些没有其他文件名的狗的图像则不包含“ obj”。

在TESTDATAPATH中只是随机图像,有些带有狗,有些没有。 NN将它们全部分类为“不包括狗”或“ 0”,这是不正确的。

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

您正在执行二进制分类,但是使用的是两个类:

isObj = 1 if 'obj' in f else 0
isNotObj = 0 if 'obj' in f else 1
target = [isObj, isNotObj]

在二进制情况下,它应该是单个类,其中1表示它是狗,而0表示不是。您已经在做,但是两次。您可以完全删除isNotObj,而仅保留isObj

您需要相应地调整模型,以使其仅预测isObj,因此fc2应该仅具有1类作为输出:

self.fc2 = nn.Linear(1000, 1)

在测试阶段,您需要基于单个类别进行预测,这可以看作是成为狗的概率。然后设置一个阈值,您可以认为该模型足以确定它实际上是一只狗。为了使其平衡,阈值是0.5,因此高于该阈值的所有内容都是一只狗,低于该阈值的所有内容都不是。使用torch.round可以轻松实现:

# Size: [batch_size, 1]
out = model(data)
predictions = torch.round(out)
# Get rid of the singular dimension
# To get size: [batch_size]
predictions = predictions.squeeze(1)

此外,学习率10在天文数字上很高,而学习率大于1使其无法收敛。更合适的学习率大约为0.01或0.001。

此外,由于您是PyTorch的新手,请不要使用Variable,它已被2年前发布的PyTorch 0.4.0弃用,并且所有功能均已合并到张量中。