PYTORCH多层卷积神经网络实现MNIST手写数字识别(python3.7)

根据模式识别作业要求写的,总结了以下几点需要注意:
1.主程序段需要加上 # if name == ‘main’: # 否则当做模块调用时会直接将该文件重新跑一遍。
2.分类任务网络最后一个全连接层替换成1*1卷积层目前看来可以降低运算量,产生激活作用降低过拟合,训练效果改善明显。
3.添加Batch normalization效果不明显,可能是batch量过小,以后进一步学习和尝试group narmalization。

import time
import torch
import torch.nn as nn
import torch.nn.functional as F

import torchvision   # torchvision 包收录了若干重要的公开数据集、网络模型和计算机视觉中的常用图像变换
import torchvision.transforms as transforms
import cv2

time_start = time.time()


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

        self.layer1 = nn.Sequential(
            nn.Conv2d(1, 16, 3),
            nn.BatchNorm2d(16), #group narmalization
            nn.ReLU(inplace=True))
        self.layer2 = nn.Sequential(
            nn.Conv2d(16, 32, 3),
            nn.BatchNorm2d(32),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(2, 2))
        self.layer3 = nn.Sequential(
            nn.Conv2d(32, 64, 3),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True))
        self.layer4 = nn.Sequential(
            nn.Conv2d(64, 128, 3),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2))
        self.fc = nn.Sequential(
            nn.Conv2d(128, 1024, 4),
            nn.ReLU(inplace=True),
            nn.Conv2d(1024, 256, 1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 10, 1))

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = self.fc(x)
        x = x.view(x.size(0), -1)

        return x

# 计算预测正确率的函数,其中predictions是模型给出的一组预测结果,batch_size行num_classes列的矩阵,labels是数据之中的正确答案
    def retrieve_features(self, x):  # 该函数专门用于提取卷积神经网络的特征图的功能,返回feature_map1, feature_map2为前两层卷积层的特征图

        feature_map1 = F.relu(self.conv1(x))  # 完成第一层卷积
        x = self.pool(feature_map1)  # 完成第一层pooling
        print('type(feature_map1)=', feature_map1)
        feature_map2 = F.relu(self.conv2(x))  # 第二层卷积,两层特征图都存储到了feature_map1, feature_map2中
        return (feature_map1, feature_map2)




if __name__ == '__main__':
    import matplotlib.pyplot as plt
    import numpy as np
    path = 'C:/Users/BOOM/OneDrive/作业/模式识别/python神经网络'
    path2 = 'C:/Users/BOOM/OneDrive/作业/模式识别/python神经网络/resent.pth'

    # transforms.ToTensor():将图像转化为Tensor,在加载数据的时候,就可以对图像做预处理
    train_dataset = torchvision.datasets.MNIST(root=path, train=True, transform=transforms.ToTensor(), download=True)
    test_dataset = torchvision.datasets.MNIST(root=path, train=False, transform=transforms.ToTensor(), download=True)
    # 训练数据集的加载器,自动将数据分割成batch,顺序随机打乱
    train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=50, shuffle=True)


    #接下来把测试数据中的前5000个样本作为验证集,后5000个样本作为测试集
    indices = range(len(test_dataset))
    indices_val = indices[:5000-1]
    indices_test = indices[5000:]
    # 通过下标对验证集和测试集进行采样
    sampler_val = torch.utils.data.sampler.SubsetRandomSampler(indices_val)
    sampler_test = torch.utils.data.sampler.SubsetRandomSampler(indices_test)
    # 根据采样器来定义加载器,然后加载数据
    validation_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=50, sampler=sampler_val)
    test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=50, sampler=sampler_test)


    #预览一个批次
    images, labels = next(iter(train_loader))
    img = torchvision.utils.make_grid(images)#把图片排列成网格形状。
    print(img.shape)
    img = img.numpy().transpose(1, 2, 0)#具体的变换高,宽,通道数
    print(img.shape)
    #std = [0.5, 0.5, 0.5]
    #mean = [0.5, 0.5, 0.5]
    #img = img * std + mean#颜色反转
    print(labels)
    # print([labels[i] for i in range(64)])
    # 由于matplotlab中的展示图片无法显示,所以现在使用OpenCV中显示图片
    # plt.imshow(img)
    cv2.imshow(r'按任意键以继续', img)
    key_pressed = cv2.waitKey(0)


    def accuracy(predictions, labels):
        # torch.max的输出:out (tuple, optional维度) – the result tuple of two output tensors (max, max_indices)
        pred = torch.max(predictions.data, 1)[1]  # 对于任意一行(一个样本)的输出值的第1个维度,求最大,得到每一行的最大元素的下标
        right_num = pred.eq(labels.data.view_as(pred)).sum()  # 将下标与labels中包含的类别进行比较,并累计得到比较正确的数量
        return right_num, len(labels)  # 返回正确的数量和这一次一共比较了多少元素


    image_size = 28
    num_classes = 10
    num_epochs = 6
    batch_size = 50

    net = ConvNet()
    print(net)

    criterion = nn.CrossEntropyLoss()  # Loss函数的定义,交叉熵
    optimizer = torch.optim.Adam(net.parameters(), lr=0.001, eps=1e-08)  # 定义优化器,普通的随机梯度下降算法momentum=0.9

    record = []  # 记录准确率等数值的list
    weights = []  # 每若干步就记录一次卷积核

    for epoch in range(num_epochs):
        train_accuracy = []  # 记录训练数据集准确率的容器
        # 一次迭代一个batch的 data 和 target
        for batch_id, (data, target) in enumerate(train_loader):
            net.train()  # 给网络模型做标记,标志说模型正在训练集上训练,这种区分主要是为了打开关闭net的training标志,从而决定是否运行dropout
            output = net(data)  # forward
            loss = criterion(output, target)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            accuracies = accuracy(output, target)
            train_accuracy.append(accuracies)

            if batch_id % 100 == 0:  # 每间隔100个batch执行一次打印等操作
                net.eval()  # 给网络模型做标记,将模型转换为测试模式。
                val_accuracy = []  # 记录校验数据集准确率的容器
                for (data, target) in validation_loader:  # 计算校验集上面的准确度
                    output = net(data)  # 完成一次前馈计算过程,得到目前训练得到的模型net在校验数据集上的表现
                    accuracies = accuracy(output, target)  # 计算准确率所需数值,返回正确的数值为(正确样例数,总样本数)
                    val_accuracy.append(accuracies)
                # 分别计算在已经计算过的训练集,以及全部校验集上模型的分类准确率
                # train_r为一个二元组,分别记录目前已经经历过的所有训练集中分类正确的数量和该集合中总的样本数,
                train_r = (sum([tup[0] for tup in train_accuracy]), sum([tup[1] for tup in train_accuracy]))
                # val_r为一个二元组,分别记录校验集中分类正确的数量和该集合中总的样本数
                val_r = (sum([tup[0] for tup in val_accuracy]), sum([tup[1] for tup in val_accuracy]))
                # 打印准确率等数值,其中正确率为本训练周期Epoch开始后到目前batch的正确率的平均值
                print('Epoch [{}/{}] [{}/{} ({:.0f}%)]\tLoss: {:.6f}\t训练正确率: {:.2f}%\t校验正确率: {:.2f}%'.format(
                    epoch + 1, num_epochs, batch_id * batch_size, len(train_loader.dataset),
                    100. * batch_id / len(train_loader), loss.item(),
                    100. * train_r[0] / train_r[1],
                    100. * val_r[0] / val_r[1]))
    torch.save(net, path2)
    PATH = 'D:/resent.pth'
    torch.save({
                'epoch': epoch,
                'model_state_dict': net.state_dict(),
                'optimizer_state_dict': optimizer.state_dict(),
                'loss': loss,
                }, PATH)

    #torch.save(net.state_dict(), path2)


    # 在测试集上进行测试
    net.eval() #标志模型当前为测试阶段
    vals = [] #记录准确率所用列表

    with torch.no_grad():
        for data, target in test_loader:
            output = net(data)
            val = accuracy(output, target)
            #print(val[0].data)
            vals.append(val)
    #计算准确率
    rights = (sum([tup[0] for tup in vals]), sum([tup[1] for tup in vals]))
    right_rate = 1.0 * rights[0].data.numpy() / rights[1]
    print("accuracy:", right_rate)

time_end = time.time()
print('程序共运行时间:', time_end-time_start, '秒')

训练前需要导入将网络模型导入,模型状态参数(用字典保存比较好)导入,model.eval()固化模型;需要注意的是要正确使用已训练的模型就要保证加载的测试集数据格式必须与训练时batch格式一致,涉及到维数扩展和变换调整。

#coding = utf-8
#MNIST黑底白字
#50 1 28 28;;3 1 25 25
import time
from new import ConvNet#模型仍需要导入
import torch
import torch.nn as nn
import torch.nn.functional
import torchvision
import torchvision.transforms as transforms
import cv2
import os.path
import glob
from PIL import Image
import numpy as np


time_start = time.time()
PATH = 'D:/resent.pth'#如果加载模型状态要注意文件权限问题
path2 = 'C:/Users/BOOM/OneDrive/作业/模式识别/python神经网络/resent.pth'
model = torch.load(path2) #要将类的定义添加到加载模型的这个py文件中
model.eval()#不启用batchnormalize和dropout


n = 1
for img in glob.glob(r'test\1\*.bmp'):
    img = cv2.imread(img)#不使用绝对路径,不使用中文路径
    print(img.shape)#这里的图片25*25*3
    size = (int(28), int(28))
    img = cv2.resize(img, size, interpolation=cv2.INTER_AREA) #改大小
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_info = img.shape
    image_height = img_info[0]
    image_weight = img_info[1]
    dst=np.zeros((image_height,image_weight,1),np.uint8)
    for i in range(image_height):
        for j in range(image_weight):
            grayPixel=gray[i][j]
            dst[i][j]=255-grayPixel
    #cv2.imshow('gary',dst)
    #cv2.waitKey(0)
    img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)#单通道,图片28*28
    img = torchvision.transforms.functional.to_tensor(img)#图片1*28*28
    img = torch.unsqueeze(img, 0)#图片1*1*28*28
    #img = img/255
    print(img)
    print(img.shape)
#    b = A.data.numpy().shape[0]
    if n == 1:
        A = img
    else:
        A = torch.cat((A, img), 0)
    n = n+1

output = model(A)
print(output)
pred = torch.max(output.data, 1)[1].data.numpy()# 返回每一行中最大值的那个元素的索引,然后将tenor变为numpy输出
print('预测的数字为{}'.format(pred))


time_end = time.time()
print('程序共运行时间:', time_end-time_start, '秒')


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要用卷积神经网络实现mnist手写体识别,首先需要准备好mnist数据集。然后,可以使用Python中的深度学习框架,如TensorFlow或PyTorch,来构建卷积神经网络模型。在模型中,需要使用卷积层、池化层和全连接层等组件,以及激活函数和优化器等工具,来训练模型。最后,可以使用测试集来评估模型的准确率,并对模型进行调整和优化。 ### 回答2: MNIST手写体识别是计算机视觉领域中最具有代表性的数据集之一,它包含了大量手写体数字,提供了一个很好的实验平台来测试各种计算机视觉算法的性能。卷积神经网络(CNN)已经成为图像识别的主流算法之一,它能够有效地提取图像的特征,从而实现高准确率的分类。下面我们就如何使用CNN实现MNIST手写体识别进行简要介绍。 首先需要准备好MNIST数据集,它包含了6万张训练图片和1万张测试图片。每个图片的大小为28x28像素,并且每个像素点的灰度值都在0-255之间。在这里我们使用TensorFlow深度学习框架来实现手写体识别。 我们先定义输入层,输入层的大小应该是28x28。然后我们添加一层卷积层,卷积核的大小一般是3x3,4x4或者5x5。这一层用来提取图片的特征。接着添加池化层,通常使用最大池化,它的大小一般是2x2。最大池化可以在不损失信息的前提下减小图片的尺寸,从而降低网络的复杂度。接下来,可以再添加几层卷积池化层来进一步提取特征。最后,添加一个全连接层,用来连接所有的卷积池化层,使得网络能够输出一个确定的类别。最后输出层的节点数应该是10,对应10种数字分类。 在进行训练之前需要先对数据进行预处理。一般来说,我们需要将每个像素点的像素值除以255,然后将每张图片展开成一个向量。接下来,我们可以使用随机梯度下降(SGD)算法来进行训练,对于每一次训练迭代,我们需要从训练集中随机抽取一批数据来进行训练,这个批量大小一般是32或64,然后使用反向传播算法来计算误差并更新参数。 最后,在测试集上进行结果评估。分类准确率是衡量分类器优秀度的标准,正确率越高,说明CNN网络性能越好。如果最终结果仍无法满足需求,可以通过增加网络深度、增加卷积核数量等手段来提高准确率。 从以上步骤可以看出,卷积神经网络是一种非常有效的图像识别算法,通过合理的设计网络体系和训练方法,能够在视觉任务中达到很高的精度,并且在实用领域得到了广泛应用。 ### 回答3: MNIST手写数字识别深度学习中最常见的任务之一,可以训练一个卷积神经网络(CNN)来实现这个任务。 首先,需要安装并导入必要的库,如tensorflow和numpy。接着,加载MNIST数据集,数据集包括60000张训练图片和10000张测试图片,每张图片大小为28x28像素,通过如下代码进行加载: ``` import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True) ``` 然后,定义CNN的网络结构,输入图片是一个28x28的矩阵,把它们作为CNN的输入,具有卷积层、激活函数和池化层,最终输出一个10维向量,用来表示输入图片所表示的数字分类。CNN的结构如下: ``` # 定义CNN结构 input_image = tf.placeholder(tf.float32, [None, 784]) # 输入数据为28x28的张量,把它们拉成一维的向量 input_label = tf.placeholder(tf.float32, [None, 10]) # 标签为10-d向量 input_image_reshape = tf.reshape(input_image, [-1, 28, 28, 1]) # 将拉成的向量重塑为28x28的张量 # 第1个卷积层 conv_1 = tf.layers.conv2d(inputs=input_image_reshape, filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool_1 = tf.layers.max_pooling2d(inputs=conv_1, pool_size=[2, 2], strides=2) # 第2个卷积层 conv_2 = tf.layers.conv2d(inputs=pool_1, filters=64, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool_2 = tf.layers.max_pooling2d(inputs=conv_2, pool_size=[2, 2], strides=2) # 扁平化层 pool_flat = tf.reshape(pool_2, [-1, 7 * 7 * 64]) # 全连接层 dense = tf.layers.dense(inputs=pool_flat, units=1024, activation=tf.nn.relu) dropout = tf.layers.dropout(inputs=dense, rate=0.4) # 输出层 output = tf.layers.dense(inputs=dropout, units=10) ``` 接着,定义CNN的损失函数和优化器,使用交叉熵代价函数,通过梯度下降法来更新网络中的权重参数: ``` # 定义损失函数 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=input_label, logits=output)) # 定义优化器 train_step = tf.train.GradientDescentOptimizer(0.5).minimize(loss) ``` 最后,使用训练集对CNN进行训练,训练过程中进行多次迭代,每次迭代使用一个batch的样本进行训练: ``` # 模型训练 sess = tf.InteractiveSession() tf.global_variables_initializer().run() for i in range(100): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={input_image: batch_xs, input_label: batch_ys}) # 计算测试集分类准确率 correct_prediction = tf.equal(tf.argmax(output, 1), tf.argmax(input_label, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print(sess.run(accuracy, feed_dict={input_image: mnist.test.images, input_label: mnist.test.labels})) ``` 到这里,就完成了MNIST手写数字识别任务的实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值