CNN 分类器只猜测一件事 - PyTorch

问题描述

我试图让模型预测 75x75 图像的种族,但是每当我训练模型时,准确率始终完全保持在 53.2%。直到我在一些照片上实际运行它之前,我才意识到为什么。事实证明,无论照片是什么,它总会预测“其他”。我不完全确定为什么。

我从 the official PyTorch Quickstart tutorial 复制了代码,在该数据集或标准 M​​NIST 数据中,它运行良好。我将数据集更改为 UTKFace,然后它开始一直只预测一个标签

这是我的代码

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision.transforms import ToTensor
import torch.nn.functional as F

training_data = ImageFolder(
    root = "data_training/",transform = ToTensor(),)

testing_data = ImageFolder(
    root = "data_testing/",transform = ToTensor()
)

training_DataLoader = DataLoader(training_data,batch_size=64,shuffle=True)
test_DataLoader = DataLoader(testing_data,shuffle=True)

class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork,self).__init__()
        self.conv1 = nn.Conv2d(3,6,5)
        self.pool = nn.MaxPool2d(2,2)
        self.conv2 = nn.Conv2d(6,16,5)
        
        self.fc1 = nn.Linear(1296,1024)
        self.fc2 = nn.Linear(1024,1024)
        self.fc3 = nn.Linear(1024,512)
        self.fc4 = nn.Linear(512,84)
        self.fc5 = nn.Linear(84,5)
        
    def forward(self,x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        
        
        x = x.view(x.size(0),-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = F.relu(self.fc3(x))
        x = F.relu(self.fc4(x))
        
        x = self.fc5(x)
        return x
    
model = NeuralNetwork().to("cpu")
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(),lr=1e-3)

def train(DataLoader,model,loss_fn,optimizer):
    size = len(DataLoader.dataset)
    for batch,(X,y) in enumerate(DataLoader):
        X,y = X.to("cpu"),y.to("cpu")
        # Compute prediction error
        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"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")

def tests(DataLoader,model):
    size = len(DataLoader.dataset)
    model.eval()
    test_loss,correct = 0,0
    with torch.no_grad():
        for X,y in DataLoader:
            X,y.to("cpu")
            pred = model(X)
            test_loss += loss_fn(pred,y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    test_loss /= size
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%,Avg loss: {test_loss:>8f} \n")

epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train(training_DataLoader,optimizer)
    tests(test_DataLoader,model)

torch.save(model.state_dict(),"model.pth")

训练日志:

Epoch 1
-------------------------------
loss: 1.628994  [    0/23705]
loss: 1.620698  [ 6400/23705]
loss: 1.615423  [12800/23705]
loss: 1.596390  [19200/23705]
Test Error: 
 Accuracy: 53.2%,Avg loss: 0.024725 

Epoch 2
-------------------------------
loss: 1.593613  [    0/23705]
loss: 1.581375  [ 6400/23705]
loss: 1.583656  [12800/23705]
loss: 1.591942  [19200/23705]
Test Error: 
 Accuracy: 53.2%,Avg loss: 0.024165 

Epoch 3
-------------------------------
loss: 1.541260  [    0/23705]
loss: 1.592345  [ 6400/23705]
loss: 1.540908  [12800/23705]
loss: 1.540741  [19200/23705]
Test Error: 
 Accuracy: 53.2%,Avg loss: 0.023705 

Epoch 4
-------------------------------
loss: 1.566888  [    0/23705]
loss: 1.524875  [ 6400/23705]
loss: 1.540764  [12800/23705]
loss: 1.510044  [19200/23705]
Test Error: 
 Accuracy: 53.2%,Avg loss: 0.023323 

Epoch 5
-------------------------------
loss: 1.530084  [    0/23705]
loss: 1.498773  [ 6400/23705]
loss: 1.537755  [12800/23705]
loss: 1.508989  [19200/23705]
Test Error: 
 Accuracy: 53.2%,Avg loss: 0.022993 
....

无论我给它设置了多少个 epoch,或者我添加了多少层来试图让它过拟合,它似乎总是一遍又一遍地猜测同样的事情,没有任何改进的迹象。

>

我根据姓名的种族类别将 UTKFace 数据集分成多个文件夹。训练数据有23705张图片,测试数据有10134张。

我不确定为什么会发生这种情况。我的数据集不够大吗?层数不够?

解决方法

层数和数据集大小不能解释此示例的这种行为。你的 CNN 表现得像一个常数函数,到目前为止我不知道为什么,但这些可能是一些线索:

  1. 由于您已按标签将数据分隔到文件夹中,如果您仅使用这些文件夹中的一个来训练模型,您将获得一个常量函数。
  2. 你的神经网络的最后一层没有激活函数!也就是说,在方法 forward 中,您正在执行 x = self.fc5(x) 而不是 x = F.<function>(self.fc5(x))
  3. 加载训练数据时,您在哪里指示每个图像的标签?您确定 training_dataloader 正在加载带有正确标签的图像吗?
,

一些评论:

  • 您是否检查了测试数据中的ground truth(形状可能不同)
  • 您能否检查输出概率以查看预测是否一致? (顺便说一句,在这种情况下,你不一定需要最后的激活函数,因为 pytorch 交叉熵已经包含一个 logsoftmax)
  • 您是否尝试过使用更多过滤器(例如 16,32 或 64)的 conv2d
  • 错误的百分比看起来不错,就像您提供的链接一样,准确度约为 35% 不能过度拟合似乎有点奇怪。