1. 问题引入

今天在使用pytorch训练一个模型的,数据集的读取是使用pytorch自带的函数来进行读取和预处理的,网络使用的是自定义的CNN,然后在运行的时候出现了如标题所示的这种小错误。

2. 运行报错

如下所示:

RuntimeError: Expected 4-dimensional input for 4-dimensional weight [32, 1, 5, 5], but got 2-dimensional input of size [32, 784] instead

3. 代码

首先是我自己自定义的CNN网络如下所示:

class MNIST_Model(nn.Module):
    def __init__(self, n_in):
        super(MNIST_Model, self).__init__()

        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels=n_in,
                      out_channels=32,
                      kernel_size=(5, 5),
                      padding=2,
                      stride=1),
        )

        self.maxp1 = nn.MaxPool2d(
                       kernel_size=(2, 2))

        self.conv2 = nn.Sequential(
            nn.Conv2d(in_channels=32,
                      out_channels=64,
                      kernel_size=(5, 5),
                      padding=0,
                      stride=1),
        )

        self.maxp2 = nn.MaxPool2d(kernel_size=(2, 2))
        
        self.fc1 = nn.Sequential(
            nn.Linear(in_features=64 * 5 * 5, out_features=200)  # Mnist
        )

        self.fc2 = nn.Sequential(
            nn.Linear(in_features=200, out_features=10),
            nn.ReLU()
        )


    def forward(self, x):
        x = self.conv1(x)
        x = self.maxp1(x)
        x = self.conv2(x)
        x = self.maxp2(x)
        x = x.contiguous().view(x.size(0), -1)
        x = self.fc1(x)
        x = self.fc2(x)
        return x

然后是在训练模型的代码

#实例化网络,只考虑使用CPU
model = model.MNIST_Model(1)
net = model.to(device)
#定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
#momentum:动量因子有什么用处?
optimizer = optim.SGD(model.parameters(),lr=lr,momentum=momentum)


#开始训练 先定义存储损失函数和准确率的数组
losses = []
acces = []
#测试用
eval_losses = []
eval_acces = []

for epoch in range(nums_epoches):
    #每次训练先清零
    train_loss = 0
    train_acc = 0
    #将模型设置为训练模式
    model.train()
    #动态学习率
    if epoch%5 == 0:
        optimizer.param_groups[0]['lr'] *= 0.1
    for img,label in train_loader:
        #前向传播,将图片数据传入模型中
        # out输出10维,分别是各数字的概率,即每个类别的得分
        out = model(img)
        #这里注意参数out是64*10,label是一维的64
        loss = criterion(out,label)
        #反向传播
        #optimizer.zero_grad()意思是把梯度置零,也就是把loss关于weight的导数变成0
        optimizer.zero_grad()
        loss.backward()
        #这个方法会更新所有的参数,一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数
        optimizer.step()
        
        #记录误差 
        train_loss += loss.item()
        
        #计算分类的准确率,找到概率最大的下标
        _,pred = out.max(1)
        num_correct = (pred == label).sum().item()#记录标签正确的个数
        acc = num_correct/img.shape[0]
        train_acc += acc
    losses.append(train_loss/len(train_loader))
    acces.append(train_acc/len(train_loader))
    
    eval_loss = 0
    eval_acc = 0
    model.eval()
    for img,label in test_loader:
        img = img.view(img.size(0),-1)
        
        out = model(img)
        loss = criterion(out,label)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        eval_loss += loss.item()
        
        _,pred = out.max(1)
        num_correct = (pred == label).sum().item()
        acc = num_correct/img.shape[0]
        eval_acc += acc
    eval_losses.append(eval_loss/len(test_loader))
    eval_acces.append(eval_acc/len(test_loader))
    

    print('epoch:{},Train Loss:{:.4f},Train Acc:{:.4f},Test Loss:{:.4f},Test Acc:{:.4f}'
             .format(epoch,train_loss/len(train_loader),train_acc/len(train_loader),
                    eval_loss/len(test_loader),eval_acc/len(test_loader)))

4. 分析原因

定位出错位置

Traceback (most recent call last):
  File "train.py", line 73, in <module>
    out = model(img)
  File "/home/gzdx/anaconda3/envs/Torch/lib/python3.7/site-packages/torch/nn/modules/module.py", line 889, in _call_impl
    result = self.forward(*input, **kwargs)
  File "/home/gzdx/wyf/PARAD/model.py", line 48, in forward
    x = self.conv1(x)
  File "/home/gzdx/anaconda3/envs/Torch/lib/python3.7/site-packages/torch/nn/modules/module.py", line 889, in _call_impl
    result = self.forward(*input, **kwargs)
  File "/home/gzdx/anaconda3/envs/Torch/lib/python3.7/site-packages/torch/nn/modules/container.py", line 119, in forward
    input = module(input)
  File "/home/gzdx/anaconda3/envs/Torch/lib/python3.7/site-packages/torch/nn/modules/module.py", line 889, in _call_impl
    result = self.forward(*input, **kwargs)
  File "/home/gzdx/anaconda3/envs/Torch/lib/python3.7/site-packages/torch/nn/modules/conv.py", line 399, in forward
    return self._conv_forward(input, self.weight, self.bias)
  File "/home/gzdx/anaconda3/envs/Torch/lib/python3.7/site-packages/torch/nn/modules/conv.py", line 396, in _conv_forward
    self.padding, self.dilation, self.groups)
RuntimeError: Expected 4-dimensional input for 4-dimensional weight [32, 1, 5, 5], but got 2-dimensional input of size [32, 784] instead

可以看到这句提示,大致就是我们传入的数据输入到CNN网络,然后由于维度不同导致的。因为我们输入的是四维,但是得到的却是二维。

  File "train.py", line 73, in <module>
    out = model(img)

5.解决办法

对于这种问题网上给出了很多中不同的方案,这个哦个人也是参考我网上别人给出的一点想法然后自己修改了下,错误就解决了,如下所示:

for i,data in enumerate(train_loader):
        #前向传播,将图片数据传入模型中
        # out输出10维,分别是各数字的概率,即每个类别的得分
        inputs, labels = data
        inputs,labels = data[0].to(device), data[1].to(device)
        # inputs torch.Size([32, 1, 28, 28])
        out = model(inputs)

解决办法也是很简单,就是将上面训练开始阶段将数据按照这种读取方式来赋值,然后在传入到model里面就不会出现上面那种错误了。

6. 完整代码

import numpy as np
import model
import torch

#导入PyTorch内置的mnist数据
from torchvision.datasets import mnist

#导入预处理模块
from torchvision import transforms
from torch.utils.data import DataLoader

#导入神经网络工具
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

#定义后面要用到的超参数
train_batch_size = 32
test_batch_size = 32

#学习率与训练次数
learning_rate = 0.01
nums_epoches = 50

#优化器的时候使用的参数
lr = 0.1
momentum = 0.5

#用compose来定意预处理函数
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5],[0.5])])

#下载数据,在工程文件夹里新建一个data文件夹储存下载的数据
train_dataset = mnist.MNIST('./data', train=True, transform=transform, target_transform=None, download=False)
test_dataset = mnist.MNIST('./data', train=False, transform=transform, target_transform=None, download=False)

#数据加载器,组合数据集和采样器,并在数据集上提供单进程或多进程迭代器
train_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True, num_workers=0)
test_loader = DataLoader(test_dataset, batch_size=test_batch_size, shuffle=False)

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

#实例化网络,只考虑使用CPU
model = model.MNIST_Model(1)
net = model.to(device)
#定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
#momentum:动量因子有什么用处?
optimizer = optim.SGD(model.parameters(),lr=lr,momentum=momentum)




#开始训练 先定义存储损失函数和准确率的数组
losses = []
acces = []
#测试用
eval_losses = []
eval_acces = []

for epoch in range(nums_epoches):
    #每次训练先清零
    train_loss = 0
    train_acc = 0
    #将模型设置为训练模式
    model.train()

    #动态学习率
    if epoch%5 == 0:
        optimizer.param_groups[0]['lr'] *= 0.1
    for i,data in enumerate(train_loader):
        #前向传播,将图片数据传入模型中
        # out输出10维,分别是各数字的概率,即每个类别的得分
        inputs, labels = data
        inputs,labels = data[0].to(device), data[1].to(device)
        out = model(inputs)
        #这里注意参数out是64*10,label是一维的64
        loss = criterion(out,labels)
        #反向传播
        #optimizer.zero_grad()意思是把梯度置零,也就是把loss关于weight的导数变成0
        optimizer.zero_grad()
        loss.backward()
        #这个方法会更新所有的参数,一旦梯度被如backward()之类的函数计算好后,我们就可以调用这个函数
        optimizer.step()
        
        #记录误差 
        train_loss += loss.item()
        
        #计算分类的准确率,找到概率最大的下标
        _,pred = out.max(1)
        num_correct = (pred == labels).sum().item() #记录标签正确的个数
        acc = num_correct/inputs.shape[0]
        train_acc += acc
    losses.append(train_loss/len(train_loader))
    acces.append(train_acc/len(train_loader))
    print('Finished Training') 

    # 保存模型
    PATH = './model/mnist_net.pth'
    torch.save(net.state_dict(), PATH)
    
    eval_loss = 0
    eval_acc = 0
    model.eval()
    for i,data in enumerate(test_loader):
        inputs, labels = data
        inputs,labels = data[0].to(device), data[1].to(device)
        out = model(inputs)
        loss = criterion(out,labels)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        eval_loss += loss.item()
        
        _,pred = out.max(1)
        num_correct = (pred == labels).sum().item()
        acc = num_correct/inputs.shape[0]
        eval_acc += acc
    eval_losses.append(eval_loss/len(test_loader))
    eval_acces.append(eval_acc/len(test_loader))
    

    print('epoch:{},Train Loss:{:.4f},Train Acc:{:.4f},Test Loss:{:.4f},Test Acc:{:.4f}'
             .format(epoch,train_loss/len(train_loader),train_acc/len(train_loader),
                    eval_loss/len(test_loader),eval_acc/len(test_loader)))




7. 参考文献

1.pytorch学习笔记—搭建CNN识别MNIST

2.使用Pytorch框架的CNN网络实现手写数字(MNIST)识别

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐