首页 > 其他分享 >Pytorch 实践手写数字识别深度学习网络 LeNet-5

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

时间:2024-07-07 12:02:07浏览次数:21  
标签:self torch images Pytorch train LeNet test 手写 model

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

文章目录

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

认识 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/ 获取

大家如果想要的话可以联系我邮箱[email protected],也可以直接发给你。

在这里插入图片描述

处理数据集

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

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)


标签:self,torch,images,Pytorch,train,LeNet,test,手写,model
From: https://blog.csdn.net/Go_ahead_forever/article/details/140244045

相关文章

  • PyTorch中的多进程并行处理
    PyTorch是一个流行的深度学习框架,一般情况下使用单个GPU进行计算时是十分方便的。但是当涉及到处理大规模数据和并行处理时,需要利用多个GPU。这时PyTorch就显得不那么方便,所以这篇文章我们将介绍如何利用torch.multiprocessing模块,在PyTorch中实现高效的多进程处理。多进程是一......
  • 主干网络篇 | YOLOv5/v7 更换主干网络之 ShuffleNetv2 | 高效CNN架构设计的实用指南(2)
    主干网络篇|YOLOv5/v7更换主干网络之ShuffleNetv2|高效CNN架构设计的实用指南概述YOLOv5和YOLOv7是目前主流的轻量级目标检测模型,在速度和精度方面取得了良好的平衡。然而,传统的YOLOv5/v7模型使用FPN和CSPNet等结构作为主干网络,在移动设备和嵌入式系统等资源受限的场景......
  • 动手学深度学习(Pytorch版)代码实践 -循环神经网络-54~55循环神经网络的从零开始实现和
    54循环神经网络的从零开始实现importmathimporttorchfromtorchimportnnfromtorch.nnimportfunctionalasFfromd2limporttorchasd2limportmatplotlib.pyplotaspltimportliliPytorchaslp#读取H.G.Wells的时光机器数据集batch_size,num_steps......
  • 实现胶囊神经网络,识别手写MNIST数据集,谈谈实现及理解。
    ......
  • 外挂级OCR神器:免费文档解析、表格识别、手写识别、古籍识别、PDF转Word
    TextInTools是一款免费的在线OCR工具,支持快速准确的文字和表格识别,手写、古籍识别,提供PDF转Markdown大模型辅助工具,同时支持PDF、WORD、EXCEL、JPG、PPT等各类格式文件的转化。TextInTools特点免费:所有产品提供每日200页免费额度,覆盖日常使用需求。方便:无需下载安装,PC端......
  • 手写数字识别-使用TensorFlow构建和训练一个简单的神经网络
    下面是一个具体的Python代码示例,展示如何使用TensorFlow实现一个简单的神经网络来解决手写数字识别问题(使用MNIST数据集)。以下是一个完整的Python代码示例,展示如何使用TensorFlow构建和训练一个简单的神经网络来进行手写数字识别。MNIST数据集的训练集有60000个样本:Python代码i......
  • PyTorch基本操作
    PyTorch基本操作torch.allclose是一个PyTorch函数,用于检查两个张量是否在某个容忍度范围内近似相等torch.allclose(input,other,rtol=1e-05,atol=1e-08,equal_nan=False)input(Tensor)–第一个输入张量other(Tensor)–第二个输入张量rtol(float)–相对容忍度a......
  • 昇思25天学习打卡营第16天|ShuffleNet图像分类
    ShuffleNet网络介绍        ShuffleNetV1是由旷视科技提出的一种高效计算的卷积神经网络(CNN)模型,主要用于移动设备。与MobileNet和SqueezeNet类似,ShuffleNetV1的设计目标是利用有限的计算资源达到最佳模型精度。其核心设计是引入了PointwiseGroupConvolution和Channe......
  • 基于CNN的蒙牛评论情感分析(用的Tensorflow,因为不会Pytorch)
    目录一、数据来源二、导相应入的库 三、数据预处理四、模型的构建五、预测函数六、总结一、数据来源 蒙牛评论数据集:共有2000多条数据,其中一列为label,一列为review,label这一列已经分好差评和好评,差评为0,好评为1,好评和差评占比为1比1.二、导相应入的库#导入数......
  • element 手写季度组件
    组件:<template><divclass="time_quarter"><markstyle="position:fixed;top:0;bottom:0;left:0;right:0;background:rgba(0,0,0,0);z-index:999;"v-show="showSeason"@click.stop="showSeason=false&q......