pytorch 手写数字识别

本文详细介绍了如何使用PyTorch库对MNIST数据集进行手写数字识别,包括数据预处理、构建简单CNN模型、定义损失函数和优化器,以及模型的训练和测试过程。
摘要由CSDN通过智能技术生成

前言

pytorch 手写数字识别


1.mnist数据集介绍

MNIST(Modified National Institute of Standards and Technology)是一个常用的手写数字识别数据集,被广泛用于测试和验证机器学习和深度学习模型的性能。该数据集由70,000张28x28像素的灰度图像组成,包括60,000张用于训练和10,000张用于测试。

每张图像都包含一个手写数字(0到9之间的数字),并且是由不同的人手写的。MNIST数据集的目标是让模型学会识别这些手写数字。这使得它成为一个很好的起点,用于入门和学习机器学习和深度学习。

MNIST数据集通常被用于计算机视觉和模式识别的教育、研究和基准测试。由于其相对较小的规模,训练速度较快,因此它是学习深度学习的理想选择。然而,在实际应用中,由于其相对简单的特性,人们可能会更倾向于使用更具挑战性的数据集,以更好地测试和验证模型的性能。
这里,可以使用,torchvision下载使用mnist数据集

import torchvision
import torchvision.transforms as transforms

# 定义数据转换
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])

# 下载和加载训练集
train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)

# 下载和加载测试集
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)

使用以下代码查看部分数据
# 显示图像和标签
def imshow(img):
    img = img / 2 + 0.5  # 反标准化
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()

# 显示图像
imshow(torchvision.utils.make_grid(images))
# 打印标签
print(' '.join(f'{label.item()}' for label in labels))

请添加图片描述

训练过程

1.引入库,加载数据

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
# 加载MNIST数据集
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])

train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=False)

transform 定义了图像预处理的操作,将图像转换为张量并进行标准化。
train_dataset 加载训练集,如果数据不存在则会下载,然后应用上述定义的预处理操作。
test_dataset 加载测试集,如果数据不存在则会下载,然后应用上述定义的预处理操作。
train_loader 创建一个训练集的数据加载器,它可以按照指定的批次大小和是否打乱的方式提供数据。
test_loader 创建一个测试集的数据加载器,同样也可以按照指定的批次大小提供数据,但一般不打乱测试集。

transforms.ToTensor(): 这个转换操作将图像数据转换为PyTorch张量(Tensor)的格式。PyTorch的模型通常使用张量作为输入数据类型,因此在将图像输入到模型之前,需要将图像转换为张量。

例如,如果原始图像的像素值在0到255之间,transforms.ToTensor() 会将它们缩放到0到1之间,同时将数据类型转换为浮点型。如果你的图像是单通道的(灰度图像),结果的张量形状将是 (1, height, width);如果是多通道的(彩色图像),形状将是 (3, height, width),其中3表示通道数。

transforms.Normalize((0.5,), (0.5,)): 这个转换操作用于标准化张量,将其值缩放到均值为0,标准差为1的范围。这种标准化有助于提高模型的训练稳定性和收敛速度。

在这里,transforms.Normalize 接受两个参数:均值和标准差。对于单通道的灰度图像,均值和标准差都是一个值;对于多通道的彩色图像,均值和标准差是每个通道的值。在这个例子中,(0.5,) 表示灰度图像的均值和标准差都是0.5。这会将张量的值从[0, 1]缩放到[-1, 1]的范围,进一步帮助模型的训练。

2.建立模型

# 定义神经网络模型
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.relu = nn.ReLU()
        self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

nn.Conv2d(1, 32, kernel_size=3, padding=1): 第一个卷积层,输入通道为1(因为MNIST图像是单通道的灰度图像),输出通道为32,卷积核大小为3x3,填充为1。这个卷积层会提取输入图像的特征。

nn.ReLU(): 非线性激活函数,使用ReLU激活函数来引入非线性特性。在卷积层后应用,有助于网络学习更复杂的特征。

nn.MaxPool2d(kernel_size=2, stride=2): 最大池化层,用于下采样。它将特征图的大小减半,有助于减小模型的计算负担并提取更显著的特征。

nn.Flatten(): 将特征图展平成一维向量,以便传递给全连接层。

nn.Linear(64 * 7 * 7, 128): 第一个全连接层,输入大小为64 * 7 * 7(由于两次最大池化,图像大小缩小了一半两次),输出大小为128。这个层用于学习全局特征。

nn.Linear(128, 10): 第二个全连接层,输入大小为128,输出大小为10,对应于10个数字类别(0到9)。这个层用于最终的分类。

forward 方法: 这个方法定义了前向传播的过程。输入 x 经过卷积层、激活函数、池化层、再次卷积层、激活函数、池化层等一系列操作,最终通过全连接层输出分类结果。

定义模型和损失函数

# 初始化模型、损失函数和优化器
model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

这两行代码涉及到损失函数和优化器的定义,它们是训练深度学习模型时非常重要的组件。

criterion = nn.CrossEntropyLoss(): 这行代码定义了损失函数。在分类问题中,特别是多类别分类(例如手写数字识别,有10个类别),交叉熵损失是一个常用的损失函数。nn.CrossEntropyLoss() 结合了nn.LogSoftmax() 和 nn.NLLLoss() 操作,它不仅适用于二分类问题,还适用于多分类问题。在训练期间,模型的输出会通过 softmax 激活函数,并且交叉熵损失用于计算模型输出与实际标签之间的差异。目标是最小化损失函数。

optimizer = optim.Adam(model.parameters(), lr=0.001): 这行代码定义了优化器。在这里,使用了 Adam 优化器,它是一种基于梯度的优化算法,常用于训练深度学习模型。model.parameters() 传递了模型的所有可学习参数给优化器,lr=0.001 设置学习率为 0.001。学习率控制了每次参数更新的步长,是一个需要仔细调整的超参数。Adam 优化器通过自适应地调整每个参数的学习率,有助于加速训练过程。

训练模型

# 训练模型
num_epochs = 5
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

for epoch in range(num_epochs):
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)

        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

训练5次。

测试

# 在测试集上评估模型
model.eval()
correct = 0
total = 0

with torch.no_grad():
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

accuracy = correct / total
print(f'Test Accuracy: {accuracy:.2%}')

运行结果

训练
请添加图片描述
部分数据
请添加图片描述
predicted: [7, 2, 1, 0, 4, 1, 4, 9, 5, 9, 0, 6, 9, 0, 1, 5, 9, 7, 8, 4, 9, 6, 6, 5,
4, 0, 7, 4, 0, 1, 3, 1, 3, 4, 7, 2, 7, 1, 2, 1, 1, 7, 4, 2, 3, 5, 1, 2,
4, 4, 6, 3, 5, 5, 6, 0, 4, 1, 9, 5, 7, 8, 9, 3]
labels: [7, 2, 1, 0, 4, 1, 4, 9, 5, 9, 0, 6, 9, 0, 1, 5, 9, 7, 3, 4, 9, 6, 6, 5,
4, 0, 7, 4, 0, 1, 3, 1, 3, 4, 7, 2, 7, 1, 2, 1, 1, 7, 4, 2, 3, 5, 1, 2,
4, 4, 6, 3, 5, 5, 6, 0, 4, 1, 9, 5, 7, 8, 9, 3]

部分错误的案例
在这里插入图片描述
labels: tensor(4, device=‘cuda:0’)
predicted: tensor(2, device=‘cuda:0’)
在这里插入图片描述
predicted: tensor(2, device=‘cuda:0’)
labels: tensor(8, device=‘cuda:0’)

4.完整代码

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

# 定义神经网络模型
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.relu = nn.ReLU()
        self.maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.flatten = nn.Flatten()
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.maxpool(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

# 加载MNIST数据集
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])

train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)

train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=False)

# 初始化模型、损失函数和优化器
model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
num_epochs = 5
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

for epoch in range(num_epochs):
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)

        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# 在测试集上评估模型
model.eval()
correct = 0
total = 0
import matplotlib.pyplot as plt
import numpy as np
# 显示图像和标签
def imshow(img):
    img = img / 2 + 0.5  # 反标准化
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()
with torch.no_grad():
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)

        total += labels.size(0)
        correct += (predicted == labels).sum().item()
       # 找出预测错误的样本
        incorrect_samples = (predicted != labels).nonzero()

        # 显示部分预测错误的样本
        num_samples_to_show = min(4, len(incorrect_samples))
        for i in range(num_samples_to_show):
           
            index = incorrect_samples[i].item()
            imshow(torchvision.utils.make_grid(images[index].cpu()))
            print('predicted:',predicted[index])
            print('labels:',labels[index])
            break
accuracy = correct / total
print(f'Test Accuracy: {accuracy:.2%}')


  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值