PyTorch项目笔记(一):MINST手写体数据集的识别

MINST手写体数据集

MNIST数据集(Mixed National Institute of Standards and Technology database)是美国国家标准与技术研究院收集整理的大型手写数字数据集,包含了60,000个样本的训练集以及10,000个样本的测试集。Mnist中所有样本都会将原本28*28的灰度图转换为长度为784的一维向量作为输入,其中每个元素分别对应了灰度图中的灰度值。Mnist使用一个长度为10的向量作为该样本所对应的标签,其中向量索引值对应了该样本以该索引为结果的预测概率。

1、导入数据集

利用DataLoader导入数据集

PyTorch中数据读取的一个重要接口是torch.utils.data.DataLoader,该接口定义在dataloader.py脚本中,只要是用PyTorch来训练模型基本都会用到该接口,该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入。

torch.utils.data.DataLoader参数:

  1. dataset (Dataset) – 加载数据的数据集。
  2. batch_size (int, optional) – 每个batch加载多少个样本(默认: 1)。
  3. shuffle (bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: False).
  4. sampler (Sampler, optional) – 定义从数据集中提取样本的策略,即生成index的方式,可以顺序也可以乱序
  5. num_workers (int, optional) – 用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)
  6. collate_fn (callable, optional) –将一个batch的数据和标签进行合并操作。
  7. pin_memory (bool, optional) –设置pin_memory=True,则意味着生成的Tensor数据最开始是属于内存中的锁页内存,这样将内存的Tensor转义到GPU的显存就会更快一些。
  8. drop_last (bool, optional) – 如果数据集大小不能被batch size整除,则设置为True后可删除最后一个不完整的batch。如果设为False并且数据集的大小不能被batch
    size整除,则最后一个batch将更小。(默认: False)
  9. timeout,是用来设置数据读取的超时时间的,但超过这个时间还没读取到数据的话就会报错

datasets.MNIST类 CLASS torchvision.datasets.MNIST(root: str,
train: bool = True, transform: Optional[Callable] = None,
target_transform: Optional[Callable] = None, download: bool = False)

  • root (string): 表示数据集的根目录,其中根目录存在MNIST/processed/training.pt和MNIST/processed/test.pt的子目录
  • train (bool, optional): 如果为True,则从training.pt创建数据集,否则从test.pt创建数据集
  • download (bool, optional): 如果为True,则从internet下载数据集并将其放入根目录。如果数据集已下载,则不会再次下载
  • transform (callable, optional): 接收PIL图片并返回转换后版本图片的转换函数
  • target_transform (callable, optional): 接收PIL接收目标并对其进行变换的转换函数

导入数据集的过程

  • 使用DataLoader对数据进行封装,PyTorch会在root目录下检测数据是否存在,当数据不存在时,则自动将数据下载到data目录中。
  • 使用ToTensor()将0 ~ 255的像素值映射到0 ~ 1的范围内,并转化为Tensor格式。
  • 使用Normalize(mean, std)方法实现归一化。不同数据集中的图像通道对应的均值(mean)和标准差(std)是不同的。MNIST数据集的均值是0.1307,标准差是0.3081,这些系数是数据集提供方提供的,有利于加速神经网络的训练。
  • 随机取出一个batch下的数据进行观察。
import numpy as np
import torch
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import torchvision
#训练集
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST(root='./data', #root表示数据加载的相对目录
                   train=True, #train表示是否加载数据库的训练集,False时加载测试集
                   download=True,#download表示是否自动下载
                   transform=transforms.Compose([#transform表示对数据进行预处理的操作
                       transforms.ToTensor(),#图片转张量,同时归一化0-255 ---》 0-1
                       transforms.Normalize((0.1307,), (0.3081,))#标准化均值方差
                   ])),batch_size=64, shuffle=True)#batch_size表示该批次的数据量  shuffle表示是否洗牌
#测试集
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', train=False, transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),batch_size=64, shuffle=True)

def imshow(img):
    img = img / 2 + 0.5     # 逆归一化 在深度学习中,通常在将图像输入模型之前会对其进行归一化操作,
    #使其像素值落在特定的范围内(例如[0, 1]或[-1, 1])。逆归一化的目的是将其还原回原始的像素值范围。
    npimg = img.numpy()#显示图像时,通常需要将图像数据从 PyTorch 格式转换为 NumPy 格式。 
    plt.imshow(np.transpose(npimg, (1, 2, 0)))#使用np.transpose函数将第一个维度(通道维度)变为转置数组的第二个维度,第二个维度变为第三个维度,第三个维度变为第一个维度
    plt.show()

# 得到batch中的数据
dataiter = iter(train_loader)#iter 用于创建一个可迭代对象的迭代器。
#使用iter函数将train_loader转换为一个迭代器对象dataiter,从而可以逐个获取train_loader中的批次数据。
images, labels = dataiter.__next__()

# 展示图片
imshow(torchvision.utils.make_grid(images))

在计算机图像处理中,图像通常以通道优先顺序存储,即通道维度在最前面。然而,在Matplotlib中显示图像时,默认假设图像数据是以通道为最后一个维度的顺序存储的。因此,为了正确显示图像,需要对图像数据进行转置,将通道维度放置在最后一个维度。
转置操作可以通过改变数组的维度顺序来实现。在这个例子中,使用np.transpose函数将第一个维度(通道维度)变为转置数组的第二个维度,第二个维度变为第三个维度,第三个维度变为第一个维度。这样,就将图像数据从通道优先顺序转换为以通道为最后一个维度的顺序。

数据集下载成功:
数据集MINST
一个batch下的数据如图所示:
一个batch的数据

2、构建模型

神经网络训练步骤包括以下:

  • 定义神经网络
  • 前向传播
  • 计算损失
  • 反向传播
  • 更新参数

2.1定义神经网络

借鉴csdn上大神的一个图像识别网络:指路->http://t.csdnimg.cn/mYEzi

Convolutions是卷积操作;Subsampling是下采样操作,也就是池化;Full connection 表示全连接层。该网络将输入的图片,经过两层卷积和池化,再经过三层全连接层,最后输出图片对应每个阿拉伯数字的概率。

名词解释:
- 卷积操作(Convolutional Operation):卷积操作是神经网络中的一种基本操作,用于提取输入数据中的特征。卷积操作通过滑动一个卷积核(也称为滤波器)在输入数据上进行计算,将输入数据的局部区域与卷积核进行逐元素的乘积运算,并将乘积结果相加得到输出特征图。卷积操作在图像处理领域尤为常见,因为它能有效地提取图像中的空间局部特征,例如边缘、纹理等。在卷积神经网络(Convolutional Neural Network, CNN)中,卷积操作通常用于处理图像数据。
- 下采样操作(Pooling Operation):下采样操作是一种减小特征图尺寸的操作。常见的下采样操作有最大池化(Max Pooling)和平均池化(AveragePooling)。这些操作将特征图划分为不重叠的区域,并在每个区域内取最大值或平均值作为输出特征值。下采样操作有助于减少特征图的空间维度,同时保留重要的特征信息,提高模型的鲁棒性和计算效率。在池化操作中,常见的池化操作之一是在(2, 2)的窗口上进行池化。这种池化操作通常称为2x2最大池化或平均池化。
- 全连接层(Fully Connected Layer):全连接层也称为密集连接层(Dense Layer),是神经网络中的一种常见层类型。在全连接层中,每个神经元与上一层的所有神经元都有连接。这就意味着前一层的所有神经元的输出都被传递到全连接层中的每个神经元,且每个神经元都有自己的权重参数。全连接层在网络中起到整合和映射特征的作用,通常用于将前面的卷积层或其他特征提取层的输出映射为最终的输出类别或回归值。

神经网络的前向传播是指信号从输入层经过网络中的各层逐层传递到输出层的过程。在前向传播中,神经网络将输入数据通过一系列的线性和非线性变换,逐渐提取并组合特征,最终生成对应的输出结果。前向传播在神经网络中非常重要,原因如下:

  • 特征提取:通过前向传播,神经网络可以自动学习和提取输入数据中的有用特征。每个隐藏层都可以看作是对输入数据进行特征提取的一种方式。随着信号在网络中传播,特征表示逐渐变得更加抽象和高级。前向传播过程中的每个层都会对输入进行一定的变换,使得网络能够自动学习输入数据的复杂特征。
  • 参数传递:神经网络的每个层都包含一组可学习的参数,如权重和偏置。前向传播过程中,这些参数被用于计算每个神经元的输出。通过前向传播,神经网络中的参数得以有效地传递和利用。不同层之间的参数传递是通过矩阵乘法和非线性激活函数实现的。
  • 输出预测:神经网络的前向传播将输入数据转化为网络的输出结果,这通常是它在特定任务上的预测或分类。通过前向传播,网络通过将输入数据映射到输出空间来进行预测和推断。这使得神经网络成为一种功能强大的模型,可用于图像分类、语音识别、自然语言处理等各种任务。
import torch
import torch.nn as nn
import torch.nn.functional as F#可以调用一些常见的函数,例如非线性以及池化等

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        # 1 input image channel, 6 output channels, 5x5 square convolution
        # 输入图片是1 channel输出是6 channel 利用5x5的核大小
        self.conv1 = nn.Conv2d(1, 6, 5)#定义了第一个卷积层,输入图像通道数为1(单通道灰度图像),输出通道数为6,卷积核大小为5x5。
        self.conv2 = nn.Conv2d(6, 16, 5)#定义了第二个卷积层,输入通道数为6(第一个卷积层的输出通道数),输出通道数为16,卷积核大小为5x5。
        # 全连接 从16 * 4 * 4的维度转成120
        self.fc1 = nn.Linear(16 * 4 * 4, 120)#定义了第一个全连接层,将16x4x4的输入数据展平为一个大小为120的向量。全连接层的输入尺寸与卷积核的尺寸无关,而是与卷积层的输出通道数相对应
        self.fc2 = nn.Linear(120, 84)#定义了第二个全连接层,将120维的向量转换为84维。
        self.fc3 = nn.Linear(84, 10)#定义了最后一个全连接层,将84维的向量转换为10维,通常用于多类别分类任务。
    def forward(self, x):#神经网络前向传播
        # 在(2, 2)的窗口上进行池化
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))#应用ReLU激活函数后,对第一个卷积层的输出进行最大池化操作。
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)#(2,2)也可以直接写成数字2 
        对第二个卷积层的输出进行最大池化操作。
        x = x.view(-1, self.num_flat_features(x))#将维度转成以batch为第一维 剩余维数相乘为第二维
        #将卷积层的输出数据展平为一维向量,以便进行全连接层的计算 self.num_flat_features(x)函数用于计算展平后的特征数量
        x = F.relu(self.fc1(x)) #应用ReLU激活函数后,通过第一个全连接层。
        x = F.relu(self.fc2(x))#通过第二个全连接层。
        x = self.fc3(x)#通过最后一个全连接层,这一层的输出通常用于进行分类。
        return x
    def num_flat_features(self, x):#用于计算展平后的特征数量。
        size = x.size()[1:]  # 第一个维度batch不考虑
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = Net()
print(net)#显示每一层的参数和结构信息。

输出结果
结果显示

2.2前向传播

前向传播是指:定义了神经网络之后,将所有数据按照batch的方式进行输入,得到对应的网络输出。我们取前两张图片进行观察。我们选择了前两个图像和它们的标签,然后将这些图像传递给模型以获得预测。

image = images[:2]
label = labels[:2]
print(image.size())
print(label)
out = net(image)#前向传播
print(out)#输出包含网络的预测结果

运行结果:
在这里插入图片描述
随机取出的图片是数字4和9。最后输出的维度为10的tensor,每个位置上的数值代表成为该类别的概率值。以第二张图片为例,第二张图片为数字5的概率最大,与实际数字9不一致。输出结果与实际情况不符的原因是目前网络没有进行训练,只是随机初始化了结构中的权重,所以输出暂时没有参考价值。

2.3计算损失

损失函数需要一对输入:模型输出与目标,用于评估输出距离目标有多远。损失用loss来表示,损失函数的作用就是计算神经网络每次迭代的前向计算结果与真实值之间的差距,从而指导模型下一步训练往正确的方向进行。常见的损失函数有交叉熵损失函数和均方误差损失函数。
在PyTorch中,nn库模块提供了多种损失函数,常见的有以下几种:

  • 回归问题:nn.MSELoss()
  • 二分类问题:nn.BCELoss()
  • 多分类问题:nn.CrossEntropyLoss()
    由于MNIST数据集是十个分类,因此选择nn.CrossEntropyLoss().
image = images[:2]
label = labels[:2]
out = net(image)
criterion = nn.CrossEntropyLoss()#交叉熵损失
loss = criterion(out, label)#计算模型预测out和标签真实值的交叉熵损失
print(loss)

结果如下:

结果表明当前两个样本通过网络输出后与实际差距仍有2.3094,我们的训练目标是最小化loss值。

2.4反向传播与参数更新

当计算出一次前向传播loss值之后,可进行反向传播计算梯度,以此来更新参数。在Pytorch中,对loss调用backward()即可。
在深度学习过程中进行反向传播,计算输出变量关于输入变量的梯度。最后要做的就是更新神经网络的参数,最简单的规则就是随机梯度下降(SGD),为了更新神经网络的权重以最小化损失函数,公式如下:
weight=weight−learningRate×gradient
weight是模型的参数
learningRate是学习率,控制了参数更新的步长
gradient是梯度

#创建优化器
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)#lr代表学习率
criterion = nn.CrossEntropyLoss()
# 在训练过程中
image = images[:2]
label = labels[:2]
optimizer.zero_grad()   # 消除累积梯度,PyTorch会在每次反向传播时累积梯度,所以你需要在每个批次之前将其清除,以避免梯度混合
out = net(image)
loss = criterion(out, label)
loss.backward()
optimizer.step()    # 更新参数

3、模型训练

可将训练过程封装成一个函数,像该函数传入网络模型、损失函数、优化器等必要对象后,在MNIST数据集上进行训练并打印日志观察过程,代码如下:

def train(n):
    net.train()  # 设置为training模式
    for epoch in range(n):
        running_loss = 0.0
        for i, data in enumerate(train_loader):
            # 得到输入 和 标签
            inputs, labels = data
            # 消除梯度
            optimizer.zero_grad()
            # 前向传播 计算损失 后向传播 更新参数
            outputs = net(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            # 打印日志
            running_loss += loss.item()
            if i % 100 == 0:    # 每100个batch打印一次
                print('[%d, %5d] loss: %.3f' %
                      (epoch + 1, i + 1, running_loss / 100))
                running_loss = 0.0

train(2)#此处表示训练两轮

结果如下图所示,从结果中可以看出训练过程中loss值不断下降:
训练结果

4、模型评估

训练完成之后为了检验模型的训练结果,可以在测试集上进行验证,通过不同的评估方法进行评估。分类模型的常见评估方法是进求分类准确率,它能衡量所有类别中预测正确的个数占所有样本的比值,代码如下:

correct = 0
total = 0
with torch.no_grad():#或者model.eval()  表示关闭梯度跟踪
    for data in test_loader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

torch.max()这个函数返回的是两个值,第一个值是具体的value(我们用下划线_表示),第二个值是value所在的index(也就是predicted)。1也可以写作dim=1。表示输出所在行的最大值,若改写成dim=0则输出所在列的最大值。预测为对应类别的概率,而行代表样本、列代表类别,所以这里应该用dim=1。

结果显示:“在10000张测试图像上的网络准确率为95%”
结果显示
训练时用的是train loader数据集,测试时就得用另一部分的数据集 test_loader。

5、结果测试

我们随机取出8张数字图片进行测试:

image = images[:8]
label = labels[:8]
imshow(torchvision.utils.make_grid(image))
print("数字图片标签值:", end="")
print(label)
out = net(image)
out_label = []
for m in out:
    t = torch.argmax(m).item()
    out_label.append(t)
print("神经网络预测值:", end="")
print(out_label)

结果显示:
在这里插入图片描述
以上即为MINST数据集的识别,记录自己入门学习的一些琐碎知识,如有不足,欢迎补充。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PyTorch是一种深度学习框架,可以用来进行手写体识别。MNIST是一个手写数字的数据集,包含了60000个训练样本和10000个测试样本。使用PyTorch可以很方便地对这个数据集进行训练和测试,从而实现手写体识别的功能。具体的实现方法可以参考PyTorch官方文档或者相关的教程。 ### 回答2: Pytorch是一个Python的开源深度学习库,它不仅支持GPU加速,而且拥有众多的功能,比如自动求导、动态计算图等。而手写体识别(MINST)则是深度学习的一个重要应用领域之一,通过对手写数字的图像进行识别,可以应用到很多领域,比如数字化办公、足球运动员数据分析等等。 Pytorch在MNIST手写体识别中的应用主要分为以下几个步骤: 1. 数据处理: Pytorch中的torchvision包括一些常用的数据集和模型,如MNIST、CIFAR、ImageNet等等,因此可以很方便地从torchvision中导入MNIST数据集数据集可以通过DataLoader进行封装,进行随机化和批处理,以便于更好地训练模型。 2. 建立模型: Pytorch中的nn包包括了深度学习中常用的模型和组件,可以方便地构建MNIST手写体识别的模型。一个简单的模型可以包括一个卷积层( Conv2d) 、一个池化层( MaxPool2d) 、一个全连接层( Linear) 和一个激活函数(ReLU) 。 3. 训练模型: 在Pytorch中,训练模型的主要流程包括前向传播、计算损失函数、反向传播和参数优化等步骤。训练过程包括多次迭代,每一次迭代需要根据损失函数进行反向传播,并通过优化器(如SGD)进行参数的更新,最终得到最优的模型。 4. 测试模型:为了验证训练好的模型的泛化能力,需要用测试数据集来进行测试。测试数据集和训练数据集的处理方式一样,可以采用DataLoader来进行批处理。在测试过程中,模型会根据输入图像进行前向传播,得到输出概率分布,需要根据这个概率分布进行预测。 最后,在Pytorch中进行MNIST手写体识别,需要注意的是要避免过拟合现象,可以采用Dropout等技术防止过拟合。另外,还可以对模型进行调优,如修改网络参数、修改学习率等。通过不断细化模型的表达能力,可以得到更高的识别率。 ### 回答3: PyTorch MNIST 手写体识别是一项基于深度学习的计算机视觉任务。该任务旨在通过训练深度神经网络模型,让计算机能够准确地识别手写数字。MNIST 手写体数据集包括大约 6 万张训练图片和 1 万张测试图片,这些图片由许多不同人写的手写数字组成,是机器学习界很受欢迎的基准数据集之一。 为了进行手写体识别,我们需要使用 PyTorch 深度学习框架来构建神经网络。首先需要完成数据集的预处理,包括对数据集进行归一化处理和将图片转换为张量类型。通过 PyTorch 提供的 DataLoader 将数据划分为小批次,每个小批次都包含多个图片和它们对应的标签。 接下来就是构建深度神经网络模型。一个经典的 CNN 模型包括卷积层、池化层、全连接层等。通过 PyTorch 中的 nn 模块,可以使用预定义的神经网络层构建自己的深度神经网络。在每个训练批次中,向前传播通过神经网络,计算损失值,然后通过反向传播来更新神经网络中的权重值。 模型训练完成后,通过测试集对模型进行评估,计算模型的准确率。最终,我们可以使用训练好的模型对新的手写数字进行识别。 需要注意的是,在进行深度学习任务时,模型的超参数对于模型的性能有很大的影响。例如,学习率、优化器等都需要进行适当的调整,以获得更好的结果。 总的来说,PyTorch MNIST 手写体识别是一项非常实用的计算机视觉任务,基于深度学习技术构建模型可以让计算机准确地识别手写数字,为人们提供更加方便快捷的服务。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值