Pytorch 实践手写数字识别深度学习网络 LeNet-5

Pytorch 实践手写数字识别深度学习网络 LeNet-5

文章目录

  • Pytorch 实践手写数字识别深度学习网络 LeNet-5
    • 认识 LeNet-5
    • 认识数据集
    • 处理数据集
      • 下载数据集
      • 读取数据
      • 定义Dataset的继承类
      • 把数据进行载入
      • 载入`dataloader`
    • 编写网络
    • 编写训练与测试代码
    • 实践结果展示
    • 完整代码

训练手写体识别任务是一个非常简单的学习任务,理论简单是简单,但是我相信很多人都和我一样,想体验一下一个学习任务的全流程,有原始数据,处理数据,编写网络,训练模型,测试模型,使用模型这个过程。今天我们就由我来带大家体验一下。

认识 LeNet-5

LeNet-5出自论文《Gradient-Based Learning Applied to Document Recognition》, 原本是一种用于手写体字符识别的非常高效的卷积神经网络,包含了深度学习的基本模块:卷 积层,池化层,全连接层。

在这里插入图片描述

  • INPUT(输入层) :输入28∗28的图片。
  • C1(卷积层):选取6个5∗5卷积核(不包含偏置),得到6个特征图,每个特征
  • 图的一个边为28−5+1=24。
  • S2(池化层):池化层是一个下采样层,输出12∗12∗6的特征图。
  • C3(卷积层):选取16个大小为5∗5卷积核,得到特征图大小为8∗8∗16。
  • S4(池化层):窗口大小为2∗2,输出4∗4∗16的特征图。
  • F5(全连接层):120个神经元。
  • F6(全连接层):84个神经元。
  • OUTPUT(输出层):10个神经元,10分类问题。

认识数据集

MNIST数据集来自美国国家标准与技术研究所,National Institute of Standards and Technology(NIST),数据集由来自250个不同人手写的数字构 成,其中50%是高中学生,50%来自人口普查局(the Census Bureau)的工 作人员。

训练集:60000,测试集:10000

MNIST数据集可在 http://yann.lecun.com/exdb/mnist/ 获取

大家如果想要的话可以联系我邮箱2837468248@qq.com,也可以直接发给你。

在这里插入图片描述

处理数据集

一般我们进行这个实践的时候,因为这个太经典了,很多的框架就直接集成了这个数据集,不用我们自己处理了,直接拿来用就好了。如下:

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(dataset=train_dataset, batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=64, shuffle=False)

其中的 torchvision 的包的 MNIST 数据集已经替我们解析好了一切。下载、解压、加载都做好了,效果如下:

在这里插入图片描述

虽然这样我们也能用,但是我们今天是来体验全流程的,我们要自己处理。

下载数据集

可以通过上面的数据集下载网址进行下载

在这里插入图片描述

下载好后是 gz 格式,你可以选择用程序对其进行解压,或者直接用解压软件解压。

解压后我是这样存放数据的

在这里插入图片描述

读取数据

一开始看到这个 ubyte 形式的数据我直接震惊,这啥数据呀,哪里有图片呢,真不清楚,后面了解到这是把数据进行压缩了。 具体解释请看文章

读取图片数据:

# 读取图像文件
def load_mnist_images(file_path):
    with open(file_path, 'rb') as f:
        magic, num_images, rows, cols = struct.unpack('>IIII', f.read(16))
        if magic != 2051:
            raise ValueError(f'Invalid magic number {magic} in file: {file_path}')
        images = np.frombuffer(f.read(), dtype=np.uint8).reshape(num_images, 1, rows, cols)
    return images

读取标签数据

# 读取标签文件
def load_mnist_labels(file_path):
    with open(file_path, 'rb') as f:
        magic, num_labels = struct.unpack('>II', f.read(8))
        if magic != 2049:
            raise ValueError(f'Invalid magic number {magic} in file: {file_path}')
        labels = np.frombuffer(f.read(), dtype=np.uint8)
    return labels

定义Dataset的继承类

原来直接用 torch vision 的数据的话,他会直接包装好,但是我们这里是要全部体验全流程,所以我们自己包装。

为什么要有 Dataset 类呢?

因为在进行深度学习训练的时候都是一批一批进行训练的,需要把数据载入到 Pytorch 提供的 dataloader 中去,方便 pytorch 后面对我们的数据方便进行操作。

我们自己定义一个 Dataset 类的话,一定要实现三个函数

__init__

__len__

__getitem__

这里我们的实现如下:

# 自定义 Dataset 类
class MNISTDataset(Dataset):
    def __init__(self, images, labels, transform=None):
        self.images = images
        self.labels = labels
        self.transform = transform

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        image = self.images[idx]
        label = self.labels[idx]

        if self.transform:
            image = self.transform(image)

        return image, label

把数据进行载入

这里载入的时候我们有个小地方需要注意一下,对数据进行一个简单的处理,就是把数据的维度进行放小,原来图片的维度如下:

在这里插入图片描述

我们需要的数据维度是 (60000,28,28)的数据,所以要进行降维

train_images = train_images.squeeze(axis=1) # 把第一维度进行减掉

在这里插入图片描述

后面测试集的数据的话就是一样的处理。

载入dataloader

transform = transforms.Compose([
    transforms.ToTensor(), #把numpy数据转化为tensor
    transforms.Normalize((0.5,), (0.5,)) #对数据进行归一化处理
])
from torch.utils.data import DataLoader
train_dataset = MNISTDataset(train_images, train_labels,transform=transform)
test_dataset = MNISTDataset(test_images,test_labels,transform=transform)

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

编写网络

这个网络是非常简单的,直接定义一个类继承 torch.nn.Module进行实现就好了,代码如下:

# 网络定义
import torch
import torch.nn as nn
from torch.utils.data import Dataset
import torch.optim as optim
import torchvision.transforms as transforms


class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, kernel_size=5, stride=1, padding=2)
        self.conv2 = nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = torch.relu(self.conv1(x)) #卷积以后进行激活 
        x = torch.max_pool2d(x, kernel_size=2, stride=2) #最大池化,提取特征
        x = torch.relu(self.conv2(x))
        x = torch.max_pool2d(x, kernel_size=2, stride=2)
        x = x.view(-1, 16 * 5 * 5) #把数据进行展平方便全连接层的输入
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x

编写训练与测试代码

训练的话就是一般深度学习的流程,直接调用 pytorch 的API进行解决了。

# 训练和测试函数
def train(model, device, train_loader, optimizer, criterion, epoch):
    model.train()
    best_model = model
    min_loss = 1
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        if min_loss > loss.item():
            best_model, best_loss = model, loss.item()
            print("update")
        if batch_idx % 100 == 0:
            print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')
    print("模型训练结束")
    print("保存最好 loss 模型,loss:",min_loss)
    torch.save(best_model.state_dict(),'best-lenet5.pth')

def test(model, device, test_loader, criterion):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += criterion(output, target).item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    print(f'\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({100. * correct / len(test_loader.dataset):.0f}%)\n')
# 训练和测试模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = LeNet5().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

num_epochs = 10
for epoch in range(1, num_epochs + 1):
    train(model, device, train_loader, optimizer, criterion, epoch)
    test(model, device, test_loader, criterion)

实践结果展示

载入保存的模型 pth 文件,然后手写一个 28 * 28 的数字图片进行处理识别效果如下

在这里插入图片描述

代码如下:

model = LeNet5()
model.load_state_dict(torch.load('best-lenet5.pth',map_location=torch.device('cpu')))
# 假设图像路径
image_path = '5.png'  # 替换为你的图像路径
from PIL import Image
# 使用 PIL 库打开图像
image = Image.open(image_path)

# 使用 torchvision.transforms 进行数据转换和归一化
transform = transforms.Compose([
    transforms.Resize((28, 28)),  # 调整大小到 28x28
    transforms.Grayscale(),
    transforms.ToTensor(),        # 转为 Tensor
    transforms.Normalize((0.5,), (0.5,))  # 归一化
])

# 应用转换
image_tensor = transform(image).unsqueeze(0)
predict_output = model(image_tensor)
pred_num = predict_output.argmax(dim=1,keepdim=True)
print(pred_num) # 数据要写满28*28的格子才能预测)

如果需要 pth 文件也可以联系我,不过这个训练很快,可以自己训练玩!

完整代码

# 网络定义
import torch
import torch.nn as nn
from torch.utils.data import Dataset
import torch.optim as optim
import torchvision.transforms as transforms


class LeNet5(nn.Module):
    def __init__(self):
        super(LeNet5, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, kernel_size=5, stride=1, padding=2)
        self.conv2 = nn.Conv2d(6, 16, kernel_size=5, stride=1, padding=0)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = torch.relu(self.conv1(x))
        x = torch.max_pool2d(x, kernel_size=2, stride=2)
        x = torch.relu(self.conv2(x))
        x = torch.max_pool2d(x, kernel_size=2, stride=2)
        x = x.view(-1, 16 * 5 * 5)
        x = torch.relu(self.fc1(x))
        x = torch.relu(self.fc2(x))
        x = self.fc3(x)
        return x
# 自定义 Dataset 类
class MNISTDataset(Dataset):
    def __init__(self, images, labels, transform=None):
        self.images = images
        self.labels = labels
        self.transform = transform

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        image = self.images[idx]
        label = self.labels[idx]

        if self.transform:
            image = self.transform(image)

        return image, label
import numpy as np
import struct


# 读取标签文件
def load_mnist_labels(file_path):
    with open(file_path, 'rb') as f:
        magic, num_labels = struct.unpack('>II', f.read(8))
        if magic != 2049:
            raise ValueError(f'Invalid magic number {magic} in file: {file_path}')
        labels = np.frombuffer(f.read(), dtype=np.uint8)
    return labels


# 读取图像文件
def load_mnist_images(file_path):
    with open(file_path, 'rb') as f:
        magic, num_images, rows, cols = struct.unpack('>IIII', f.read(16))
        if magic != 2051:
            raise ValueError(f'Invalid magic number {magic} in file: {file_path}')
        images = np.frombuffer(f.read(), dtype=np.uint8).reshape(num_images, 1, rows, cols)
    return images

# 获取到标签,图像数据
train_images = load_mnist_images('./data/train/train-images-idx3-ubyte')
train_labels = load_mnist_labels('./data/train/train-labels-idx1-ubyte')
print('train_images.shape', train_images.shape)
print('label.shape', train_labels.shape)
train_images = train_images.squeeze(axis=1)
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])
test_images = load_mnist_images('./data/test/t10k-images-idx3-ubyte')
test_labels = load_mnist_labels('./data/test/t10k-labels-idx1-ubyte')
test_images = test_images.squeeze(axis = 1)
print(test_images.shape)
print(test_labels.shape)
from torch.utils.data import DataLoader
train_dataset = MNISTDataset(train_images, train_labels,transform=transform)
test_dataset = MNISTDataset(test_images,test_labels,transform=transform)

train_loader = DataLoader(dataset=train_dataset, batch_size=64,shuffle=True)
test_loader = DataLoader(dataset=test_dataset,batch_size=64,shuffle=False)
# 训练和测试函数
def train(model, device, train_loader, optimizer, criterion, epoch):
    model.train()
    best_model = model
    min_loss = 100000.0
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        if min_loss > loss.item():
            best_model, best_loss = model, loss.item()
            print("update")
        if batch_idx % 100 == 0:
            print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')
    print("模型训练结束")
    print("保存最好 loss 模型,loss:",min_loss)
    torch.save(best_model.state_dict(),'best-lenet5.pth')

def test(model, device, test_loader, criterion):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += criterion(output, target).item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    print(f'\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({100. * correct / len(test_loader.dataset):.0f}%)\n')

# 训练和测试模型
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = LeNet5().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

num_epochs = 10
for epoch in range(1, num_epochs + 1):
    train(model, device, train_loader, optimizer, criterion, epoch)
    test(model, device, test_loader, criterion)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/780146.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

#数据结构 笔记一

数据结构是计算机存储、组织数据的方式。 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构是带有结构特性的数据元素的集合,它研究的是数据的逻辑结构和物理结构以及它们之间的相互关系,并对这种结构定义相适应的运算&#xff0…

微软正在放弃React

最近,微软Edge团队撰写了一篇文章,介绍了微软团队如何努力提升Edge浏览器的性能。但在文中,微软对React提出了批评,并宣布他们将不再在Edge浏览器的开发中使用React。 我将详细解析他们的整篇文章内容,探讨这一决定对…

Java对象通用比对工具

目录 背景 思路 实现 背景 前段时间的任务中,遇到了需要识别两个对象不同属性的场景,如果使用传统的一个个属性比对equals方法,会存在大量的重复工作,而且为对象新增了属性后,比对方法也需要同步修改,不方…

微软拼音输入法不显示选字框问题

问题展示:不显示选字框 解决方式 打开兼容性即可(估计是升级带来的bug)

STM32 - 内存分区与OTA

最近搞MCU,发现它与SOC之间存在诸多差异,不能沿用SOC上一些技术理论。本文以STM L4为例,总结了一些STM32 小白入门指南。 标题MCU没有DDR? 是的。MCU并没有DDR,而是让代码存储在nor flash上,临时变量和栈…

LeetCode题练习与总结:直线上最多的点数--149

一、题目描述 给你一个数组 points ,其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 示例 1: 输入:points [[1,1],[2,2],[3,3]] 输出:3示例 2: 输入:points [[1,…

水箱高低水位浮球液位开关

水箱高低水位浮球液位开关概述 水箱高低水位浮球液位开关是一种用于监测和控制水箱中液位的自动化设备,它能够在水箱液位达到预设的高低限制时,输出开关信号,以控制水泵或电磁阀的开闭,从而维持水箱液位在一个安全的范围内。这类设…

STM32快速复习(八)SPI通信

文章目录 前言一、SPI是什么?SPI的硬件电路?SPI发送的时序?二、库函数二、库函数示例代码总结 前言 SPI和IIC通信算是我在大学和面试中用的最多,问的最多的通信协议 IIC问到了,一般SPI也一定会问到。 SPI相对于IIC多了…

3.js - 模板渲染 - 简单

3.js 真tm枯燥啊,狗都不学 效果图 源码 // ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } from three/examples/jsm/libs/li…

【数据结构】09.树与二叉树

一、树的概念与结构 1.1 树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。 根结点:根…

webGL可用的14种3D文件格式,但要具体问题具体分析。

hello,我威斯数据,你在网上看到的各种炫酷的3d交互效果,背后都必须有三维文件支撑,就好比你网页的时候,得有设计稿源文件一样。WebGL是一种基于OpenGL ES 2.0标准的3D图形库,可以在网页上实现硬件加速的3D图…

阶段三:项目开发---大数据开发运行环境搭建:任务2:安装配置ZooKeeper

任务描述 知识点:安装配置ZooKeeper 重 点: 安装配置ZooKeeper 难 点:无 内 容: ZooKeeper是一个开源分布式协调服务,其独特的Leader-Follower集群结构,很好的解决了分布式单点问题。目前主要用于诸…

IT之家最新科技热点 | 小米 AI 研究院开创多模态通用模型

人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…

一.2.(3)放大电路的图解分析方法和微变等效电路分析方法;

放大电路的主要分析方法:图解法、微变等效电路法 这里以共射放大电路为例 (1) 图解法: 1.静态分析 首先确定静态工作点Q,然后根据电路的特点,做出直流负载线,进而画出交流负载线,最后,画出各极电流电压的波形。求出最大不失真输出电压。 估算IBQ,然后根据数据手册里…

二分查找2

1. 山脉数组的峰顶索引&#xff08;852&#xff09; 题目描述&#xff1a; 算法原理&#xff1a; 根据题意我们可以将数组分为两个部分&#xff0c;一个部分是arr[mid-1]<arr[mid]&#xff0c;另一个部分为arr[mid-1]>arr[mid]&#xff0c;此时不难发现我们可以将二分…

U.S.News发布全美最佳本科AI专业排名

10 加州大学圣迭戈分校 University of California, San Diego UCSD的人工智能项目从事广泛的理论和实验研究&#xff0c;学校的优势领域包括机器学习、不确定性下的推理和认知建模。除了理论学习&#xff0c;UCSD教授非常注重把计算机知识运用到自然语言处理、数据挖掘、计算…

从模拟到预测,从智能到智慧:数字孪生技术在水库管理中的应用演变,推动水利行业向更高层次的智慧化迈进

目录 引言 一、数字孪生技术概述 二、数字孪生技术在水库管理中的应用演变 1. 从模拟到预测&#xff1a;构建精准的数字孪生模型 2. 从智能到智慧&#xff1a;实现全面感知与精准控制 3. 推动公众参与与透明化管理 三、数字孪生技术推动水利行业向更高层次的智慧化迈进 …

密室逃脱——收集版修改测试

一、原版修改 1、导入资源 Unity Learn | 3D Beginner: Complete Project | URP 2、设置Scene 删除SampleScene&#xff0c;打开UnityTechnologies-3DBeginnerComplete下的MainScene 3、降低音量 (1) 打开Hierarchy面板上的Audio降低音量 (2) 打开Prefabs文件夹&#xf…

推荐3款【王炸级别】的效率软件,免费无广告,你一定要收藏

Temp Cleaner Temp Cleaner 是一款专为 Windows 操作系统设计的临时文件清理工具。它的主要功能是安全且快速地清理磁盘上的临时文件和系统缓存&#xff0c;从而释放磁盘空间。该软件体积小巧&#xff08;仅有826KB&#xff09;&#xff0c;并且是无广告的绿色软件&#xff0c;…

智能交通(3)——Learning Phase Competition for Traffic Signal Control

论文分享 https://dl.acm.org/doi/pdf/10.1145/3357384.3357900https://dl.acm.org/doi/pdf/10.1145/3357384.3357900 论文代码 https://github.com/gjzheng93/frap-pubhttps://github.com/gjzheng93/frap-pub 摘要 越来越多可用的城市数据和先进的学习技术使人们能够提…