【深度学习】Pytorch:手写数字识别

在这篇教程中,我会介绍 PyTorch 这个强大的深度学习框架,并通过使用它实现一个基于 MNIST 数据集的多层神经网络,帮助读者了解神经网络的基本原理和实现过程。我们将使用 CPU 版本的 PyTorch ,清除较为复杂的 GPU 配置过程,以便初学者更容易上手。对于需要 GPU 加速的用户,可以参考每个关键部分中的 CUDA 声明,以实现 GPU 训练和推理。

环境配置

在使用此教程前,您需要配置基础环境:

  1. 安装 Python (建议使用 Python 3.8 或更高版本)

  2. 安装 PyTorch:

    bash 复制代码
    pip install torch torchvision

数据集获取

MNIST 数据集包含手写数字图片,是机器学习和深度学习中的经典数据集。我们通过 torchvision.datasets.MNIST 类下载并加载数据,同时应用标准化转换,以提升模型的收敛速度和性能。

为了确保数据只被下载一次,我们在文件存储时进行检查:

python 复制代码
import os
from torchvision import datasets, transforms
import torch

# 设定数据文件夹
DATA_DIR = './data'
if not os.path.exists(DATA_DIR):
    os.makedirs(DATA_DIR)

# 自定义数据处理模块,将图像标准化
transform = transforms.Compose([
    transforms.ToTensor(),  # 转换为线性描述
    transforms.Normalize((0.1307,), (0.3081,))  # 标准化
])

# 获取 MNIST 训练集和测试集
train_dataset = datasets.MNIST(root=DATA_DIR, train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root=DATA_DIR, train=False, transform=transform)

# 定义设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

CUDA 声明 : 使用 DataLoader 后在每个批次中将数据移动到 CUDA 设备,而不是直接移动整个数据集。

构建多层神经网络

神经网络由层组成,每一层由若干个神经元构成,连接权重和偏置项用于确定输入数据如何在网络中传播。我们使用 torch.nn.Module 定义网络结构,并通过 forward 函数描述数据的前向传播过程。

python 复制代码
import torch
import torch.nn as nn

class NeuralNetwork(nn.Module):
    """
    一个基本的多层神经网络:
    - 28x28 输入,128 个节点的一层全连
    - ReLU 激活
    - 64 节点的第二层全连
    - 10 个节点用于输出
    """
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.fc1 = nn.Linear(28*28, 128)  # 输入层到28*28图像转为向量
        self.fc2 = nn.Linear(128, 64)  # 第二层
        self.fc3 = nn.Linear(64, 10)  # 最终输出 10 个分类结果

    def forward(self, x):
        x = x.view(-1, 28*28)  # 将图像抽成一个长向量
        x = torch.relu(self.fc1(x))  # 使用 ReLU 激活函数
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

model = NeuralNetwork()
model = model.to(device)  # 将模型移动到设备上

定义损失函数和优化器

损失函数用于度量模型输出与真实标签之间的差距。优化器通过最小化损失函数调整网络参数。我们选用交叉熵损失(nn.CrossEntropyLoss)和随机梯度下降(optim.SGD)优化器。

python 复制代码
import torch.optim as optim

criterion = nn.CrossEntropyLoss()  # 用于分类任务
optimizer = optim.SGD(model.parameters(), lr=0.01)  # 使用 SGD 优化
criterion = criterion.to(device)  # 将损失函数移动到 CUDA 设备

深度学习

训练过程包括前向传播、计算损失、反向传播和参数更新。我们通过 DataLoader 将数据分批次输入模型,逐步优化模型性能。

python 复制代码
from torch.utils.data import DataLoader

train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)

# 训练过程
num_epochs = 10
for epoch in range(num_epochs):
    model.train()
    running_loss = 0.0
    for images, labels in train_loader:
        images, labels = images.to(device), labels.to(device)  # 将数据移动到 CUDA 设备

        optimizer.zero_grad()  # 清空梯度
        outputs = model(images)
        loss = criterion(outputs, labels)  # 计算损失
        loss.backward()  # 进行反向传播
        optimizer.step()  # 更新参数

        running_loss += loss.item()

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

测试结果

模型评估阶段不需要更新参数,因此使用 torch.no_grad() 禁止梯度计算。通过 torch.max() 找到模型预测的类别,计算准确率。

python 复制代码
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)

model.eval()
correct = 0
total = 0
with torch.no_grad():  # 禁止更新参数
    for images, labels in test_loader:
        images, labels = images.to(device), labels.to(device)  # 将数据移动到 CUDA 设备
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)  # 进行分类预测
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

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

原理认识

  • ReLU 激活函数: 应用于神经网络层之间,能够有效地减少静态分布问题,并能增强网络泛化能力。
  • CrossEntropyLoss: 适合于多分类任务,通过计算分类误差以寻求最优训练结果。
  • SGD 优化器: 简单但有效的参数更新方法,在大部分场景中表现良好。

手写数字识别小程序

我们将使用 tkinter 创建一个简洁现代的 GUI,用户可以在画板上手写数字,然后点击按钮让模型识别数字。程序会展示预测结果,方便用户了解模型的识别能力。

python 复制代码
import tkinter as tk
from PIL import Image, ImageDraw
import numpy as np
import torch

class DigitRecognizerApp:
    def __init__(self, model):
        self.model = model  # 加载预训练的数字识别模型
        self.window = tk.Tk()  # 创建主窗口
        self.window.title("手写数字识别")  # 设置窗口标题

        # 创建画板,用于手写数字
        self.canvas = tk.Canvas(self.window, width=280, height=280, bg='white')
        self.canvas.grid(row=0, column=0, columnspan=3)  # 将画板放置在第一行,横跨3列

        # 绑定鼠标左键拖动事件,用于绘制数字
        self.canvas.bind('<B1-Motion>', self.draw_digit)

        # 创建清除按钮
        self.clear_button = tk.Button(self.window, text='清除', command=self.clear_canvas)
        self.clear_button.grid(row=1, column=0)  # 将清除按钮放置在第二行第一列

        # 创建识别按钮
        self.recognize_button = tk.Button(self.window, text='识别', command=self.recognize_digit)
        self.recognize_button.grid(row=1, column=1)  # 将识别按钮放置在第二行第二列

        # 创建结果显示标签
        self.result_label = tk.Label(self.window, text="识别结果:", font=("Arial", 16))
        self.result_label.grid(row=1, column=2)  # 将结果显示标签放置在第二行第三列

        # 初始化图像和绘图工具
        self.image = Image.new('L', (28, 28), color=0)  # 创建一个28x28的黑色图像
        self.draw = ImageDraw.Draw(self.image)  # 创建绘图对象

    def draw_digit(self, event):
        """在画板上绘制数字,并在内存中的图像上同步绘制"""
        x, y = event.x, event.y  # 获取鼠标当前位置
        self.canvas.create_oval(x-5, y-5, x+5, y+5, fill='black')  # 在画板上绘制黑色圆点
        self.draw.ellipse([(x/10, y/10), (x/10+1, y/10+1)], fill=255)  # 在内存中的图像上绘制白色圆点

    def clear_canvas(self):
        """清除画板和内存中的图像"""
        self.canvas.delete('all')  # 清除画板上的所有内容
        self.image = Image.new('L', (28, 28), color=0)  # 重新初始化图像为黑色
        self.draw = ImageDraw.Draw(self.image)  # 重新创建绘图对象
        self.result_label.config(text="识别结果:")  # 清空识别结果

    def recognize_digit(self):
        """识别画板上的数字并显示结果"""
        image_array = np.array(self.image).reshape(1, 1, 28, 28) / 255.0  # 将图像转换为模型输入格式
        image_tensor = torch.tensor(image_array, dtype=torch.float32)  # 将图像转换为张量
        with torch.no_grad():  # 禁用梯度计算
            output = self.model(image_tensor)  # 使用模型进行预测
            predicted_digit = torch.argmax(output).item()  # 获取预测结果
            self.result_label.config(text=f"识别结果:{predicted_digit}")  # 更新结果显示标签

    def run(self):
        """运行应用程序"""
        self.window.mainloop()  # 进入主事件循环

app = DigitRecognizerApp(model)  # 传入模型
app.run()  # 运行应用程序

结论

通过这次学习,读者应对 PyTorch 实现多层神经网络有了基本了解,可以尝试增加更深的层数,使用更复杂的激活函数,以改善结果。

相关推荐
羑悻的小杀马特7 分钟前
【Artificial Intelligence篇】AI 入侵家庭:解锁智能生活的魔法密码,开启居家梦幻新体验
c++·人工智能·生活
青松@FasterAI1 小时前
【NLP高频面题 - 分布式训练篇】PS架构是如何进行梯度同步和更新的?
深度学习
JINGWHALE11 小时前
设计模式 行为型 访问者模式(Visitor Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·访问者模式
羊小猪~~1 小时前
错误修改系列---基于RNN模型的心脏病预测(pytorch实现)
人工智能·pytorch·rnn·深度学习·神经网络·机器学习·tensorflow
金智维科技官方2 小时前
财务自动化管理系统有哪些?
大数据·人工智能·自动化
猫头不能躺2 小时前
【pytorch】注意力机制-1
深度学习
郁大锤2 小时前
Windows 下安装 PyTorch 的常见问题及解决方法
人工智能·python
多用户商城系统2 小时前
AI在零售行业中的应用:提升顾客体验与运营效率
大数据·人工智能·线上线下新零售
沃恩智慧2 小时前
解锁图像处理新姿势!多尺度特征融合带来的视觉革新!
图像处理·人工智能·计算机视觉
MUTA️2 小时前
RT-DETR代码详解(官方pytorch版)——参数配置(1)
人工智能·pytorch·笔记·深度学习·机器学习·计算机视觉