计算机系统应用教程网站

网站首页 > 技术文章 正文

pytorch利用CNN卷积神经网络来识别手写数字

btikc 2024-10-12 11:03:55 技术文章 11 ℃ 0 评论

pytorch——人工智能的开源深度学习框架

pytorch深度学习框架之tensor张量

计算机视觉的基石——读懂 CNN卷积神经网络

使用MNIST数据集训练第一个pytorch CNN手写数字识别神经网络

上期文章我们分享了使用MINIST数据集训练第一个CNN卷积神经网络,并保存了预训练模型,本期我们基于上期的模型,进行神经网络的识别

MINIST数据

MINIST的数据分为2个部分:55000份训练数据(mnist.train)和10000份测试数据(mnist.test)。这个划分有重要的象征意义,他展示了在机器学习中如何使用数据。在训练的过程中,我们必须单独保留一份没有用于机器训练的数据作为验证的数据,它能确保训练的结果的可行性。

前面已经提到,每一份MINIST数据都由图片以及标签组成。我们将图片命名为“x”,将标记数字的标签命名为“y”。训练数据集和测试数据集都是同样的结构,例如:训练的图片名为 mnist.train.images 而训练的标签名为 mnist.train.labels。

每一个图片均为28×28像素,我们可以将其理解为一个二维数组的结构,这里重点强调一下MINIST数据集是黑底白字,这里在识别数字的时候需要转换一下自己的数据图片,一般我们的照片时白底黑字,这里在调试代码时也遇到了类似的问题,神经网络总是识别错误

CNN卷积神经网络搭建

首先按照上期的代码搭建一下我们的CNN 卷积神经网络

import torch
import torch.nn as nn
from PIL import Image  # 导入图片处理工具
import PIL.ImageOps
import numpy as np
from torchvision import transforms
import cv2
import matplotlib.pyplot as plt
# 定义神经网络
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(  # input shape (1, 28, 28)
            nn.Conv2d(
                in_channels=1,  # 输入通道数
                out_channels=16,  # 输出通道数
                kernel_size=5,  # 卷积核大小
                stride=1,  #卷积步数
                padding=2,  # 如果想要 con2d 出来的图片长宽没有变化, 
                            # padding=(kernel_size-1)/2 当 stride=1
            ),  # output shape (16, 28, 28)
            nn.ReLU(),  # activation
            nn.MaxPool2d(kernel_size=2),  # 在 2x2 空间里向下采样, output shape (16, 14, 14) )
        self.conv2 = nn.Sequential(  # input shape (16, 14, 14)
            nn.Conv2d(16, 32, 5, 1, 2),  # output shape (32, 14, 14)
            nn.ReLU(),  # activation
            nn.MaxPool2d(2),  # output shape (32, 7, 7) )
        self.out = nn.Linear(32 * 7 * 7, 10)  # 全连接层,0-9一共10个类
# 前向反馈
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size(0), -1)  # 展平多维的卷积图成 (batch_size, 32 * 7 * 7)
        output = self.out(x)
        return output

第一层,我们输入minist的数据集,minist的数据图片是一维 28*28的图片,所以第一层的输入(1,28,28),高度为1,设置输出16通道,使用5*5的卷积核对图片进行卷积运算,每步移动一格,为了避免图片尺寸变化,设置pading为2,则经过第一层卷积就输出(16,28,28)数据格式

再经过relu与maxpooling (使用2*2卷积核)数据输出(16,14,14)

第二层卷积层是简化写法nn.Conv2d(16, 32, 5, 1, 2)的第一个参数为输入通道数in_channels=16,其第二个参数是输出通道数out_channels=32, # n_filters(输出通道数),第三个参数为卷积核大小,第四个参数为卷积步数,最后一个为pading,此参数为保证输入输出图片的尺寸大小一致

        self.conv2 = nn.Sequential(  # input shape (16, 14, 14)
            nn.Conv2d(16, 32, 5, 1, 2),  # output shape (32, 14, 14)
            nn.ReLU(),  # activation
            nn.MaxPool2d(2),  # output shape (32, 7, 7)
        )

全连接层,最后使用nn.linear()全连接层进行数据的全连接数据结构(32*7*7,10)以上便是整个卷积神经网络的结构,

大致为:input-卷积-Relu-pooling-卷积-Relu-pooling-linear-output

卷积神经网络建完后,使用forward()前向传播神经网络进行输入图片的训练

初始化图片数据

file_name = '2.png'  # 导入自己的图片
img = Image.open(file_name)
plt.imshow(img)
plt.show()
img = img.convert('L')
plt.imshow(img)
plt.show()
img = PIL.ImageOps.invert(img)
plt.imshow(img)
plt.show()
train_transform = transforms.Compose([
       transforms.Grayscale(),
         transforms.Resize((28, 28)),
         transforms.ToTensor(),
 ])
img = train_transform(img)
img = torch.unsqueeze(img, dim=0)

首先我们需要导入我们需要识别的图片,在数字图像处理中,针对不同的图像格式有其特定的处理算法。所以,在做图像处理之前,我们需要考虑清楚自己要基于哪种格式的图像进行算法设计及其实现。本文基于这个需求,使用python中的图像处理库PIL来实现不同图像格式的转换。




img = img.convert('L'):模式“L”

此转换为PIL灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。在PIL中,从模式“RGB”转换为“L”模式是按照下面的公式转换的:

L = R * 299/1000 + G * 587/1000+ B * 114/1000

转换到灰度空间后,这里便是最重要的一步img = PIL.ImageOps.invert(img)

把照片的像素值翻转,把0的地方换成1,1的地方换成0,这就成为了MINIST数据的要求

这里我们可以通过plt.show()函数查看一下当前图片的效果

这是我们要识别的图片,可以看到是白底黑字的效果,若不经过图片的处理,神经网络无法进行正确的识别,通过2值化图片转换后的效果如下图,变成了MINIST数据要求的图片格式,黑底白字

转换图片到torch

train_transform = transforms.Compose([
       transforms.Grayscale(),
         transforms.Resize((28, 28)),
         transforms.ToTensor(),
 ])

img = train_transform(img)
img = torch.unsqueeze(img, dim=0)

我们转换图片到白字黑底后,我们需要把图片转换到pytorch能够识别的数据模式,这里采用的是transforms.Compose函数统一所有的变换

变换是常见的图像处理操作。可以使用 将它们链接在一起Compose。大多数转换类都有一个等价的函数:函数式转换对转换进行细粒度控制。大多数转换都接受PIL 图像和张量图像,尽管有些转换是PIL-only,有些是tensor-only。该转换变换可以被用于转换和从PIL图像。transforms函数里面有很多图片转换的函数,包含

  • Grayscale:将图像转换为灰度。如果图像是 Torch Tensor,则它应该具有 [..., 3, H, W] 形状,其中 ... 表示任意数量的前导维度
  • Resize:将输入图像调整为给定的大小。如果图像是 Torch Tensor,则它应该具有 [..., H, W] 形状,其中 ... 表示任意数量的前导维度
  • ToTensor:转换为张量。此转换不支持 torchscript。如果 PIL 图像,则将 [0, 255] 范围内的 PIL 图像或 numpy.ndarray (H x W x C) 转换为 [0.0, 1.0] 范围内形状 (C x H x W) 的 torch.FloatTensor到其中一种模式(L、LA、P、I、F、RGB、YCbCr、RGBA、CMYK、1)或者 numpy.ndarray dtype = np.uint8
  • ToPILImage:将张量或 ndarray 转换为 PIL Image。此转换不支持 torchscript。将 Torch.*Tensor 形状 C x H x W 或形状 H x W x C 的 numpy ndarray 转换为 PIL 图像,同时保留值范围。
  • CenterCrop:在中心裁剪给定的图像。如果图像是 Torch Tensor,则它应该具有 [..., H, W] 形状,其中 ... 表示任意数量的前导维度。如果图像尺寸沿任何边缘小于输出尺寸,图像用 0 填充,然后中心裁剪。
  • ColorJitter:随机改变图像的亮度、对比度、饱和度和色调。如果图像是 Torch Tensor,则它应该具有 [..., 3, H, W] 形状,其中 ... 表示任意数量的前导维度。如果 img 是 PIL Image,则不支持模式“1”、“L”、“I”、“F”和具有透明度(alpha 通道)的模式。
  • Normalize:使用均值和标准差对张量图像进行归一化。此转换不支持 PIL 图像。给定 mean:(mean[1],...,mean[n])和 std:(std[1],..,std[n])对于n 通道,此变换将标准化输入的每个通道, torch.*Tensor即, output[channel] = (input[channel] - mean[channel]) / std[channel]

pytorch通过一系列的转换,便可以把输入的图片转换成功神经网络需要的格式

神经网络的识别

model = CNN()
model.load_state_dict(torch.load('./model/CNN_NO1.pk',map_location='cpu'))
model.eval()

index_to_class = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

with torch.no_grad():
    y = model(img)
    output = torch.squeeze(y)
    predict = torch.softmax(output, dim=0)
    print(predict)
    predict_cla = torch.argmax(predict).numpy()
    print(predict_cla)
print(index_to_class[predict_cla], predict[predict_cla].numpy())

图片输出预处理完成后,我们便可以使用神经网络模型进行预测

首先我们要初始化神经网络,并使用torch.load函数加载我们上期文章训练完成的神经网络,map_location我们指定为CPU,当然若有GPU ,可以使用GUP运行代码

model.load_state_dict(torch.load('./model/CNN_NO1.pk',map_location='cpu'))

由于MNIST数据集只有0-9个数字,我们设置一个list存放这10个值

Python 有多种机制可以在本地禁用梯度计算:要在整个代码块中禁用渐变,有上下文管理器,如 no-grad 模式和推理模式。为了从梯度计算中更细粒度地排除子图,可以设置requires_grad张量。这里主要的好处是可以 节省大量的内存,我们只是为了测试我们的神经网络,可以采用类似做法。

然后我们把图片传入神经网络进行预测,我们可以计算出神经网络的预测结果与置信度

tensor([2.3575e-05, 2.2012e-03, 2.9602e-02, 1.7745e-02, 1.5333e-01, 3.1148e-03,
        1.2766e-03, 2.0447e-02, 4.6207e-02, 7.2605e-01])
9
9 0.7260508

运行以上代码,神经网络会输出每个值的预测置信度,我们从中挑选出置信度最大的。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表