当前位置: 首页 > news >正文

手写体识别

手写数字识别:基于PyTorch的卷积神经网络实现

一、项目概述

使用PyTorch实现一个基于卷积神经网络(CNN)的手写手写数字识别模型,通过MNIST数据集训练,实现对手写数字(0-9)的分类识别。

二、环境依赖

  • Python 3.x
  • PyTorch
  • torchvision
  • matplotlib

三、代码实现与解析

1. 导入必要库

# 导入PyTorch核心库
import torch
# 导入PyTorch神经网络模块
import torch.nn as nn
# 导入PyTorch优化器模块
import torch.optim as optim
# 导入数据加载工具
from torch.utils.data import DataLoader
# 导入计算机视觉相关的数据集和数据转换工具
from torchvision import datasets, transforms
# 导入matplotlib用于可视化
import matplotlib.pyplot as plt

2. 数据准备与预处理

# 定义数据转换管道:将图像转为Tensor并进行标准化
transform = transforms.Compose([transforms.ToTensor(),  # 将PIL图像转为Tensor格式,并将像素值从[0,255]归一化到[0,1]transforms.Normalize((0.1307,), (0.3081,))  # 用MNIST数据集的均值(0.1307)和标准差(0.3081)标准化数据
])# 加载MNIST训练数据集(手写数字0-9)
train_dataset = datasets.MNIST(root='./data',  # 数据集存储路径train=True,     # 加载训练集download=True,  # 如果本地没有数据集则自动下载transform=transform  # 应用定义好的数据转换
)
# 加载MNIST测试数据集
test_dataset = datasets.MNIST(root='./data',  # 数据集存储路径train=False,    # 加载测试集download=True,  # 如果本地没有数据集则自动下载transform=transform  # 应用定义好的数据转换
)# 创建训练数据加载器:按批次加载数据,支持打乱顺序
train_loader = DataLoader(train_dataset,  # 要加载的数据集batch_size=64,  # 每个批次包含64个样本shuffle=True    # 训练时打乱数据顺序,增加随机性
)
# 创建测试数据加载器:批次更大,不需要打乱
test_loader = DataLoader(test_dataset,   # 要加载的数据集batch_size=1000,# 每个批次包含1000个样本(测试时可更大)shuffle=False   # 测试时不需要打乱顺序
)

3. 定义神经网络模型

class HandwritingRecognizer(nn.Module):def __init__(self):# 调用父类构造函数super(HandwritingRecognizer, self).__init__()# 定义卷积层序列:用于提取图像特征self.conv_layers = nn.Sequential(# 第一个卷积层:输入1通道(灰度图),输出32通道,卷积核3x3,步长1,填充1nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1),nn.ReLU(),  # 激活函数:引入非线性,增强模型表达能力# 最大池化层:2x2窗口,步长2,输出尺寸变为14x14(原28x28)nn.MaxPool2d(kernel_size=2, stride=2),# 第二个卷积层:输入32通道,输出64通道,卷积核3x3,步长1,填充1nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),nn.ReLU(),  # 激活函数# 最大池化层:2x2窗口,步长2,输出尺寸变为7x7(原14x14)nn.MaxPool2d(kernel_size=2, stride=2))# 定义全连接层序列:用于分类决策self.fc_layers = nn.Sequential(# 第一个全连接层:输入为64通道×7×7特征图展平后的向量,输出128维nn.Linear(64 * 7 * 7, 128),nn.ReLU(),  # 激活函数nn.Dropout(0.5),  # 随机丢弃50%神经元,防止过拟合nn.Linear(128, 10)  # 输出层:10个类别(对应数字0-9))# 定义前向传播过程def forward(self, x):x = self.conv_layers(x)  # 输入经过卷积层提取特征# 将特征图展平为一维向量:-1表示自动计算批次维度,64*7*7为特征维度x = x.view(-1, 64 * 7 * 7)x = self.fc_layers(x)    # 展平后的特征经过全连接层得到分类结果return x

4. 初始化模型、损失函数和优化器

model = HandwritingRecognizer()  # 创建手写数字识别模型实例
criterion = nn.CrossEntropyLoss()  # 定义损失函数:多分类交叉熵损失(适用于分类任务)
# 定义优化器:Adam优化器,学习率0.001(控制参数更新速度)
optimizer = optim.Adam(model.parameters(), lr=0.001)

5. 模型训练函数

def train(model, train_loader, criterion, optimizer, epochs=1):model.train()  # 设置模型为训练模式(启用dropout等训练特定层)# 遍历训练轮次for epoch in range(epochs):running_loss = 0.0  # 累计当前轮次的损失# 遍历训练数据的每个批次for batch_idx, (data, target) in enumerate(train_loader):optimizer.zero_grad()  # 清零优化器的梯度(防止梯度累积)output = model(data)   # 前向传播:输入数据经过模型得到预测结果loss = criterion(output, target)  # 计算预测结果与真实标签的损失loss.backward()        # 反向传播:计算损失对各参数的梯度optimizer.step()       # 更新模型参数(基于梯度和优化器规则)running_loss += loss.item()  # 累加当前批次的损失值# 每300个批次打印一次平均损失(便于监控训练过程)if batch_idx % 300 == 299:print(f'Epoch {epoch+1}, Batch {batch_idx+1}, Loss: {running_loss/300:.4f}')running_loss = 0.0  # 重置累计损失

6. 模型测试函数

def test(model, test_loader):model.eval()  # 设置模型为评估模式(关闭dropout等训练特定层)correct = 0   # 记录正确预测的样本数total = 0     # 记录总样本数# 关闭梯度计算(测试时不需要反向传播,节省计算资源)with torch.no_grad():# 遍历测试数据的每个批次for data, target in test_loader:output = model(data)  # 前向传播得到预测结果# 取预测概率最大的类别作为最终预测(dim=1表示按行取最大值)_, predicted = torch.max(output.data, 1)total += target.size(0)  # 累加总样本数# 累加预测正确的样本数(预测类别与真实标签相等)correct += (predicted == target).sum().item()# 计算并打印测试集准确率print(f'Test Accuracy: {100 * correct / total:.2f}%')

7. 执行训练和测试

# 执行训练和测试:训练1轮,然后在测试集上评估
train(model, train_loader, criterion, optimizer, epochs=1)
test(model, test_loader)

8. 可视化预测结果

def visualize_prediction(model, test_dataset, idx=0):model.eval()  # 设置模型为评估模式image, label = test_dataset[idx]  # 获取测试集中指定索引的图像和真实标签# 关闭梯度计算with torch.no_grad():# 为图像增加批次维度(模型输入需要[批次, 通道, 高, 宽]格式)output = model(image.unsqueeze(0))predicted = torch.argmax(output).item()  # 取预测概率最大的类别# 显示图像:squeeze()去除多余维度,cmap='gray'设置为灰度图plt.imshow(image.squeeze().numpy(), cmap='gray')# 设置标题:显示真实标签和预测结果plt.title(f'True: {label}, Predicted: {predicted}')plt.show()  # 显示图像# 可视化测试集中索引为42的样本的预测结果
visualize_prediction(model, test_dataset, idx=42)

四、模型结构说明

  1. 卷积层部分

    • 第一层卷积:32个3×3卷积核,提取基础边缘和纹理特征
    • 最大池化:将特征图尺寸从28×28降为14×14
    • 第二层卷积:64个3×3卷积核,提取更复杂的组合特征
    • 最大池化:将特征图尺寸从14×14降为7×7
  2. 全连接层部分

    • 第一个全连接层:将64×7×7的特征展平后映射到128维
    • Dropout层:随机丢弃50%神经元,防止过拟合
    • 输出层:10个神经元,对应0-9十个数字的分类结果

五、训练与评估流程

  1. 训练过程:

    • 前向传播:输入数据通过网络得到预测结果
    • 计算损失:使用交叉熵损失衡量预测与真实标签的差距
    • 反向传播:计算损失对各参数的梯度
    • 参数更新:使用Adam优化器根据梯度更新网络参数
  2. 评估过程:

    • 关闭梯度计算,节省计算资源
    • 计算模型在测试集上的准确率
    • 通过可视化查看具体样本的预测结果

六、扩展方向

  1. 增加训练轮次,观察模型性能变化
  2. 调整网络结构(如增加卷积层、调整通道数)
  3. 尝试不同的优化器和学习率
  4. 增加数据增强操作,提高模型泛化能力
  5. 在GPU上运行以加速训练过程
http://www.hskmm.com/?act=detail&tid=36053

相关文章:

  • 你好,我是肆闲:C语言的学习,成长与分享旅程
  • AGC 合集 1.0
  • 20231302邱之钊密码系统设计实验一第二
  • 深入BERT内核:用数学解密掩码语言模型的工作原理
  • ZR 2025 NOIP 二十连测 Day 6
  • 20251021
  • [论文笔记] Precision-Guided Context Sensitivity for Pointer Analysis
  • 英语_备忘_疑难
  • 数学题刷题记录(数学、数论、组合数学)
  • 朋友圈文案不会写?这个AI指令可能帮得上忙
  • 「JOISC2020-掃除」题解
  • 结对作业
  • CF简单构造小计
  • 深入认识ClassLoader - 一次投产失败的复盘
  • python 包来源镜像
  • CSharp基础复习-1
  • Linux7种文件类型
  • 米理 课程描述/学习计划/Study program
  • 2025年线路调压器厂家推荐榜:10kv线路调压器/单相线路调压器/三相线路调压器/助力电网稳定运行,优选品牌指南
  • Day15
  • 2025 智能/商超照明/灯具/灯光/源头厂家推荐榜:上海富明阳凭分区域光效领跑,生鲜 / 百货场景适配优选
  • 2025 艺考文化课推荐榜:济南震华学校 5 星领跑,全阶段体系适配基础补弱到高分冲刺
  • 2025 广州人力资源/派遣/劳务外包/人事代理/推荐榜:精典人才凭派遣合规 + 全场景适配领跑,企业用工优选
  • 2025 变电站厂家推荐榜最新资讯:撬装变电站/移动车载变电站/预制舱式变电站/移动变电站/预装式变电站/聚焦智能适配与可靠服务,这家企业成优选​
  • 题解:P12525 [Aboi Round 1] 私は雨
  • 完整教程:罗技G102有线鼠标自己维修教程
  • 挖矿-学校挖矿排查
  • 杂谈
  • 读书日记2
  • 鸿蒙hdc命令【杭州多测师】