欢迎光临
我们一直在努力

PyTorch实现MNIST手写数字识别

一、MNIST数据集与PyTorch基础

MNIST是深度学习中非常经典的入门数据集,主要用于手写数字识别。数据集一共有70000张图片,其中60000张用于训练,10000张用于测试。每张图片都是28×28大小的灰度图像,对应的标签是0~9,因此MNIST属于典型的十分类问题。

在PyTorch中,可以使用torchvision直接加载MNIST:

training_data = datasets.MNIST(
root="data",
train=True,
download=True,
transform=ToTensor()
)

test_data = datasets.MNIST(
root="data",
train=False,
download=True,
transform=ToTensor()
)

root表示数据集保存的位置,train=True表示加载训练集,train=False表示加载测试集。download=True表示本地没有数据时自动下载。

ToTensor()的作用是将图片转换成PyTorch中的Tensor,同时将像素值转换到0~1之间。原始图片大小为28×28,转换后形状为:

[1, 28, 28]

其中1表示灰度通道。

PyTorch中的Tensor可以理解为一种多维数组,是深度学习中最基本的数据结构。图片、标签以及模型参数等都可以使用Tensor表示,并且Tensor可以放到GPU上进行计算。

为了观察数据,可以使用Matplotlib显示9张图片:

for i in range(9):
img, label = training_data[i]
figure.add_subplot(3, 3, i + 1)
plt.title(label)
plt.axis("off")
plt.imshow(img.squeeze(), cmap="gray")

这里img.squeeze()用于去掉大小为1的通道维度,使图片更适合使用Matplotlib显示。

二、DataLoader、Batch与Epoch

数据集加载完成后,还需要使用DataLoader组织数据:

train_dataloader = DataLoader(
training_data,
batch_size=128,
shuffle=True
)

test_dataloader = DataLoader(
test_data,
batch_size=128
)

batch_size=128表示模型每次读取128张图片进行训练,而不是一次将60000张图片全部送入模型。

shuffle=True表示每轮训练开始时随机打乱训练数据,可以减少模型对数据排列顺序的依赖。

这里需要理解三个重要概念。

Batch表示一次送入模型的一批数据。本代码每个Batch最多包含128张图片。

Iteration表示模型完成一次Batch的训练,包括前向传播、计算损失、反向传播和参数更新。

Epoch表示模型完整训练一次训练集。

60000张训练图片按照128张一批进行划分:

60000 ÷ 128 ≈ 469

因此一个Epoch大约有469个Batch,也就是大约469次参数更新。代码设置训练15个Epoch,所以模型会多次完整学习整个训练集。

三、CNN卷积神经网络

代码使用卷积神经网络CNN进行手写数字识别:

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

继承nn.Module是PyTorch定义神经网络的标准方式。

这个模型主要由卷积层、BatchNorm、ReLU、池化层和全连接层组成。

整个网络的数据尺寸变化如下:

输入
1 × 28 × 28

第一层卷积
32 × 28 × 28

最大池化
32 × 14 × 14

第二层卷积
64 × 14 × 14

第二层卷积
64 × 14 × 14

最大池化
64 × 7 × 7

第三层卷积
128 × 7 × 7

展平
128 × 7 × 7 = 6272

全连接层
256

输出层
10

Conv2d是CNN的核心,通过卷积核在图片上移动提取特征。网络前面的卷积层通常学习比较简单的特征,例如边缘、线条等,随着网络加深,可以逐渐学习更加复杂的数字结构。

例如:

nn.Conv2d(1, 32, 5, 1, 2)

表示输入1个通道,输出32个特征图,卷积核大小为5×5,步长为1,padding为2。由于padding设置合理,因此卷积后图片的宽高仍然保持28×28。

ReLU是常用的激活函数:

nn.ReLU()

它能够增加神经网络的非线性表达能力,使模型可以学习更加复杂的关系。

MaxPool2d(2)是最大池化层,会将特征图的宽和高缩小一半。例如:

28 × 28 → 14 × 14
14 × 14 → 7 × 7

这样可以减少计算量,同时保留比较重要的特征。

BatchNorm2d用于对特征进行归一化处理,可以使训练过程更加稳定,并帮助模型更快地收敛。

经过卷积层后,特征图需要转换成一维数据,再交给全连接层:

x = x.view(x.size(0), -1)

这里view()可以改变Tensor的形状。-1表示让PyTorch自动计算这一维的大小。

最终:

128 × 7 × 7 = 6272

所以全连接层使用:

nn.Linear(128 * 7 * 7, 256)

最后通过:

nn.Linear(256, 10)

输出10个数字类别的结果。

模型中还使用了:

nn.Dropout(0.3)

Dropout会在训练过程中随机关闭一部分神经元,可以减少模型对训练数据的过度依赖,从而降低过拟合。

四、模型训练、损失函数与优化器

模型训练是整个深度学习过程的核心,基本流程为:

输入数据

前向传播

计算损失

反向传播

计算梯度

更新参数

训练代码:

pred = model(x)
loss = loss_fn(pred, y)

optimizer.zero_grad()
loss.backward()
optimizer.step()

首先将图片输入模型,通过前向传播得到预测结果pred。

然后使用交叉熵损失函数:

loss_fn = nn.CrossEntropyLoss()

计算预测结果与真实标签之间的差距。MNIST属于多分类问题,因此交叉熵损失非常适合这种任务。

接下来执行:

optimizer.zero_grad()

清除上一次计算得到的梯度。PyTorch默认会累积梯度,所以每次更新参数之前通常都需要清空梯度。

然后:

loss.backward()

执行反向传播,根据损失函数计算模型各个参数的梯度。

最后:

optimizer.step()

根据计算出的梯度更新模型参数,使模型下一次预测更加接近正确结果。

代码使用Adam优化器:

optimizer = torch.optim.Adam(
model.parameters(),
lr=0.001,
weight_decay=1e-4
)

model.parameters()表示需要训练的模型参数。

lr是学习率,用于控制参数每次更新的幅度。学习率过大可能导致模型训练不稳定,过小则可能导致训练速度很慢。

weight_decay属于正则化方法,可以限制模型参数过度增长,在一定程度上减少过拟合。

五、学习率调整、模型测试与设备

为了让模型训练后期更加稳定,代码使用StepLR调整学习率:

scheduler = torch.optim.lr_scheduler.StepLR(
optimizer,
step_size=5,
gamma=0.5
)

step_size=5表示每训练5个Epoch调整一次学习率,gamma=0.5表示学习率变为原来的0.5倍。

如果初始学习率为0.001,那么学习率大致变化为:

开始:0.001
第5轮后:0.0005
第10轮后:0.00025
第15轮后:0.000125

训练过程中使用:

scheduler.step()

更新学习率。

模型还可以根据电脑硬件选择运行设备:

device = 'cuda' if torch.cuda.is_available() \\
else 'mps' if torch.backends.mps.is_available() \\
else 'cpu'

如果存在NVIDIA GPU,可以使用CUDA;Apple芯片可以使用MPS;如果都不可用,则使用CPU。

模型和数据必须放在相同设备上:

model = CNN().to(device)
x, y = x.to(device), y.to(device)

训练完成后,需要使用测试集评价模型:

model.eval()

with torch.no_grad():

model.eval()表示切换到测试模式,尤其会影响BatchNorm和Dropout等层。

torch.no_grad()表示测试过程中不计算梯度,可以减少内存占用并提高运行效率。

最后通过:

pred.argmax(1)

找到模型输出结果中最大值所在的位置,这个位置就是模型预测的数字。

例如模型输出10个类别的结果:

[0.01, 0.02, 0.05, 0.90, …]

最大值位于第3个位置,则模型预测结果就是数字3。

准确率的计算方式为:

Accuracy = 正确预测数量 ÷ 测试数据总数量

通过这个MNIST项目,可以完整理解PyTorch深度学习的基本流程:从数据加载开始,经过DataLoader组织Batch,再利用CNN提取图像特征,通过损失函数衡量预测结果,利用反向传播计算梯度,最后使用优化器更新参数。经过多个Epoch训练后,再利用测试集计算准确率,从而判断模型的识别效果。

赞(0)
未经允许不得转载:171主机测评 » PyTorch实现MNIST手写数字识别
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址