【问题标题】:Pytorch NN Training issue: Loss of NN does not decrasePytorch NN Training 问题:NN 的损失没有减少
【发布时间】:2020-09-20 00:50:06
【问题描述】:

我想将随机 Instagram 图片分类为“图片有狗”或“图片没有狗”。 为了训练我的神经网络对狗进行分类,我想使用斯坦福狗数据集,因此我有大约 20.000 张不同品种狗的训练图像。

但是在训练我的 NN 时,损失并没有减少,我用不同的学习率和有或没有 dropout 层进行了检查。

任何人都可以提供提示或是否有人看到以下代码中的错误?:

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”的狗图片,还有一些没有狗的图片,其他文件名不包括“obj”。

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

感谢您的每一个帮助!

【问题讨论】:

  • 你说你尝试了不同的学习率,在什么范围内?学习率 10 不仅太高,而且学习率大于 1 会导致无法收敛。学习率通常应该在 0.01 或 0.001 左右。
  • 我尝试了 0.001 到 100 之间的学习率。
  • 好的,那么也许可以尝试不同的优化器,例如 optim.SGDoptim.AdamW,学习率为 0.001。
  • 您没有发现任何重大错误?这是我的第一个 pytorch 项目之一......
  • 使用 AdamW 优化器,损失值仍然在 0.7 和 0.6 之间反弹!使用 SGD 优化器,损失在 1.1 和 0.08 之间反弹,总体上仍然没有下降。 :( 所有测试都使用 lr=0.001

标签: pytorch classification


【解决方案1】:

你正在做一个二元分类,但你正在使用两个类:

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 弃用,其所有功能已合并到张量。

【讨论】:

  • 真的非常感谢您。我会尝试调整您的提示。
猜你喜欢
  • 2018-01-31
  • 2020-02-08
  • 2020-02-28
  • 2019-02-01
  • 2021-05-29
  • 2021-12-31
  • 2021-07-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多