# 手写数字辨认:利用PyTorch构建MNIST分类器

[复制链接]
发表于 2025-9-23 03:59:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
手写数字辨认:利用PyTorch构建MNIST分类器

在这篇文章中,我将引导你通过利用PyTorch框架构建一个简朴的神经网络模子,用于辨认MNIST数据会合的手写数字。MNIST数据集是一个经典的呆板学习数据集,包罗了60,000张训练图像和10,000张测试图像,每张图像都是28x28像素的灰度手写数字。

数据加载与预处置惩罚

我们起首加载MNIST数据集,并将图像转换为PyTorch张量格式,以便模子可以处置惩罚。
  1. import torch
  2. from torch import nn
  3. from torch.utils.data import DataLoader
  4. from torchvision import datasets
  5. from torchvision.transforms import ToTensor
  6. '''下载训练数据集(包含训练图片+标签)'''
  7. training_data = datasets.MNIST( #跳转到函数的内部源代码,pycharm 按下ctrl+鼠标点击 training_data:Dataset
  8.     root="data",#表示下载的手写数字 到哪个路径。60000
  9.     train=True, #读取下载后的数据 中的 训练集
  10.     download=True,#如果你之前已经下载过了,就不用再下载
  11.     transform=ToTensor(), #张量,图片是不能直接传入神经网络模型
  12. )   #对于pytorch库能够识别的数据一般是tensor张量。
  13. '''下载测试数据集(包含训练图片+标签)'''
  14. test_data = datasets.MNIST(
  15.     root="data",
  16.     train=False,
  17.     download=True,
  18.     transform=ToTensor()
  19. )
  20. print(len(training_data))
复制代码
数据可视化

为了更好地明白数据,我们可以展示一些手写数字图像。
  1. ''展示手写字图片,把训练数据集中的前59000张图片展示一下'''
  2. from matplotlib import pyplot as plt
  3. figure = plt.figure()
  4. for i in range(9):
  5.     img, label = training_data[i+59000] #提取第59000张图片
  6.     figure.add_subplot(3, 3, i+1) #图像窗口中创建多个小窗口,小窗口用于显示图片
  7.     plt.title(label)   # 设置每个小窗口的标题为对应的标签
  8.     plt.axis("off") ##关闭坐标轴的显示
  9.     plt.imshow(img.squeeze(), cmap="gray")  # 显示图片,squeeze()函数用于去除图片张量中长度为1的维度,cmap="gray"表示以灰度模式显示
  10.     a = img.squeeze()# img.squeeze()从张量img中去掉维度为1的。如果该维度的大小不为1则张量不会改变。#cmap="gray
  11. plt.show()
复制代码
运行结果

创建DataLoader

为了高效地加载数据,我们利用DataLoader来批量加载数据。
  1. # '"创建数据DataLoader(数据加载器)开'
  2. #  'batch_size:将数据集分成多份,每一份为batch_size个数据'
  3. #  '优点:可以减少内存的使用,提高训练速度。
  4. train_dataloader = DataLoader(training_data, batch_size=64) #64张图片为一个包,train_dataloader:<torch
  5. test_dataloader = DataLoader(test_data, batch_size=64)
  6. '''判断当前设备是否支持GPU,其中mps是苹果m系列芯片的GPU。'''
  7. device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
  8. print(f"Using {device} device")
复制代码
模子界说

接下来,我们界说一个简朴的神经网络模子,包罗两个潜伏层和一个输出层。
  1. '''定义神经网络类的继承这种方式'''
  2. class NeuralNetwork(nn.Module):  # 定义一个名为 NeuralNetwork 的类,继承自 PyTorch 的 nn.Module 类
  3.     # 通过继承 nn.Module,NeuralNetwork 类可以使用 PyTorch 提供的模块化网络结构和功能
  4.     def __init__(self):  # 类的构造函数,当创建 NeuralNetwork 类的实例时调用
  5.         super().__init__()  # 调用父类(nn.Module)的构造函数,确保父类的初始化被正确执行
  6.         self.flatten = nn.Flatten()  # 创建一个 Flatten 层,用于将多维输入一维化
  7.         self.hidden1 = nn.Linear(28*28, 128)  # 创建第一个隐藏层,输入特征为 28*28(即 784),输出特征为 128
  8.         self.hidden2 = nn.Linear(128, 256)  # 创建第二个隐藏层,输入特征为 128,输出特征为 256
  9.         self.out = nn.Linear(256, 10)  # 创建输出层,输入特征为 256,输出特征为 10(假设有 10 个类别)
  10.     def forward(self, x):  # 定义前向传播函数,x 为输入数据
  11.         x = self.flatten(x)  # 将输入数据 x 通过 Flatten 层展开
  12.         x = self.hidden1(x)  # 将展开后的数据通过第一个隐藏层
  13.         x = torch.relu(x)  # 对第一个隐藏层的输出应用 ReLU 激活函数
  14.         x = self.hidden2(x)  # 将激活后的数据通过第二个隐藏层
  15.         x = torch.relu(x)  # 对第二个隐藏层的输出再次应用 ReLU 激活函数
  16.         x = self.out(x)  # 将数据通过输出层
  17.         return x  # 返回最终的输出结果
  18. model = NeuralNetwork().to(device)  # 创建 NeuralNetwork 类的实例,并将其移动到指定的设备(CPU 或 GPU)
  19. print(model)  # 打印模型的结构,以查看各层的参数和连接方式
复制代码
训练与测试

我们界说训练和测试函数,利用交叉熵丧失函数和随机梯度降落优化器。
  1. def train(dataloader, model, loss_fn, optimizer):
  2.     # 将模型设置为训练模式,这将影响某些层(如Dropout和BatchNorm)的行为
  3.     model.train()  
  4.    
  5.     batch_size_num = 1  # 初始化批次计数器
  6.     for X, y in dataloader:  # 遍历数据加载器,获取每个批次的数据和标签
  7.         X, y = X.to(device), y.to(device)  # 将数据和标签移动到指定的设备(CPU或GPU)
  8.         pred = model.forward(X)  # 执行模型的前向传播,计算预测结果
  9.         loss = loss_fn(pred, y)  # 计算预测结果和真实标签之间的损失
  10.         # 反向传播和优化步骤
  11.         optimizer.zero_grad()  # 清除旧的梯度
  12.         loss.backward()  # 计算当前梯度
  13.         optimizer.step()  # 根据梯度更新模型参数
  14.         # 提取损失值并打印
  15.         loss_value = loss.item()  # 从张量中提取损失值
  16.         if batch_size_num % 100 == 0:  # 每100个批次打印一次损失值和批次编号
  17.             print(f"loss: {loss_value:>7f} [number:{batch_size_num}]")
  18.         batch_size_num += 1  # 递增批次计数器
  19. def test(dataloader, model, loss_fn):
  20.     size = len(dataloader.dataset)  # 获取测试数据集的大小
  21.     num_batches = len(dataloader)  # 获取测试数据加载器中的批次数量
  22.     model.eval()  # 将模型设置为评估模式,这将影响某些层(如Dropout)的行为
  23.     test_loss, correct = 0, 0  # 初始化测试损失和正确预测计数器
  24.     with torch.no_grad():  # 关闭梯度计算
  25.         for X, y in dataloader:  # 遍历测试数据加载器
  26.             X, y = X.to(device), y.to(device)  # 将数据和标签移动到指定的设备(CPU或GPU)
  27.             pred = model.forward(X)  # 执行模型的前向传播,计算预测结果
  28.             test_loss += loss_fn(pred, y).item()  # 累加测试损失
  29.             correct += (pred.argmax(1) == y).type(torch.float).sum().item()  # 计算正确预测的数量
  30.     # 计算平均测试损失和准确率
  31.     test_loss /= num_batches  # 计算平均测试损失
  32.     correct /= size  # 计算平均准确率
  33.     # 打印测试结果
  34.     print(f"Test result: \n Accuracy: {(100*correct)}%, Avg loss: {test_loss}")
复制代码
训练模子

末了,我们训练模子并测试其性能。
  1. loss_fn = nn.CrossEntropyLoss() #创建交叉熵损失函数对象,因为手写字识别中一共有10个数字,输出会有10个结果
  2. optimizer = torch.optim.SGD(model.parameters(), lr=0.01) #创建一个优化器,SGD为随机梯度下降算法 #lr:learning_rate学习率,也就是步长
  3. # #params:要训练的参数,一般我们传入的都是model.parameters()# #lr:learning_rate学习率,也就是步长
  4. #loss表示模型训练后的输出结果与,样本标签的差距。如果差距越小,就表示模型训练越好,越逼近干真实的模型。
  5. # train(train_dataloader, model, loss_fn, optimizer)
  6. # test(test_dataloader, model, loss_fn)
  7. epochs = 30
  8. for t in range(epochs):
  9.     print(f"Epoch {t+1}\n")
  10.     train(train_dataloader, model, loss_fn, optimizer) #调用train函数开始一个训练周期,使用训练数据加载器、模型、损失函数和优化器。
  11. print("Done!") #在所有训练周期完成后,打印"Done!"表示训练结束。
  12. test(test_dataloader, model, loss_fn)
复制代码
运行结果


结论

通过这篇文章,我们成功构建了一个简朴的神经网络模子来辨认MNIST数据会合的手写数字。这个模子展示了怎样利用PyTorch举行数据处置惩罚、模子界说、训练和测试。盼望这能资助你开始本身的深度学习项目!
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表