手写数字识别

深度学习实战:手写数字识别

前言

接触深度学习不久,学习的东西比较杂,这段时间一直在帮老师干杂话,水平也没有得到实际性的提高,所以参加了CSDN,K同学啊的深度学习实战,感觉这个栏目质量还不错,一步一个脚印走吧。
第一周先热个身,运用leNet实现手写数字识别。


一、LeNet网络结构

LeNet网络结构

  • 输入层:LeNet的输入层接受28x28像素的灰度图像。
  • 卷积层1:这一层包含6个卷积核,每个卷积核的大小为5x5,卷积步长为1。该层使用Sigmoid激活函数。
  • 池化层1:这一层使用2x2的最大池化窗口,步长为2。这一层的作用是降低图像尺寸,并保留最重要的特征。
  • 卷积层2:这一层包含16个卷积核,每个卷积核的大小为5x5,卷积步长为1。该层使用Sigmoid激活函数。
  • 池化层2:这一层使用2x2的最大池化窗口,步长为2。这一层的作用与池化层1相同。
  • 全连接层:这一层包含120个节点,使用Sigmoid激活函数。
  • 全连接层:这一层包含84个节点,使用Sigmoid激活函数。
  • 输出层:这一层包含10个节点,对应0~9的十个数字。
    这里我将sigmiod替换成为了relu函数

二、网络搭建

1.常用函数介绍

导入库

import torch.optim as optim

from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from tqdm import *  # tqdm用于显示进度条并评估任务时间开销
import sys
import torch
import torch.nn as nn

1.1 函数数据加载详解

⭐ torchvision.datasets.MNIST详解
torchvision.datasets是Pytorch自带的一个数据库,我们可以通过代码在线下载数据,这里使用的是torchvision.datasets中的MNIST数据集。

torchvision.datasets.MNIST(root: str, train: bool = True, transform: Optional[Callable] = None, target_transform: Optional[Callable] = None, download: bool = False)

参数:

  • root (string) - 数据集的根目录。MNIST/raw/train-images-idx3-ubyteMNIST/raw/t10k-images-idx3-ubyte
  • train (bool, 可选) - 如果为 True,则从 .train-- -images-idx3-ubyte创建数据集 , 否则从 t10k-images-idx3-ubyte
  • download (bool, 可选) – 如果为 True,则从 Internet 下载数据集,然后 把它放在根目录中。如果数据集已下载,则不会 再次下载。
  • transform (callable, optional) - 接受 PIL 图像的函数/转换 并返回转换后的版本。例如,transforms.RandomCrop
  • target_transform (callable, optional) - 一个函数/转换,它接受 定位并转换它。
    ⭐ torch.utils.data.DataLoader详解
torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=<function default_collate>, pin_memory=False, drop_last=False)

数据加载器。组合数据集和采样器,并在数据集上提供单进程或多进程迭代器。
参数:

  • dataset (Dataset) – 加载数据的数据集。
  • batch_size (int, optional) – 每个batch加载多少个样本(默认: 1)。
  • shuffle (bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: False).
  • sampler (Sampler, optional) – 定义从数据集中提取样本的策略。如果指定,则忽略shuffle参数。
  • num_workers (int, optional) – 用多少个子进程加载数据。0表示数据将在主进程中加载(默认: 0)
  • collate_fn (callable, optional) –
  • pin_memory (bool, optional) –
  • drop_last (bool, optional) – 如果数据集大小不能被batch size整除,则设置为True后可删除最后一个不完整的batch。如果设为False并且数据集的大小不能被batch size整除,则最后一个batch将更小。(默认: False)
    注意:数据读取机制有
    数据读取机制

1.2 神经网络常用函数

⭐torch.nn.Conv2d

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros', device=None, dtype=None)

参数

  • in_channels (int) – 输入图像中的通道数
  • out_channels (int) – 卷积产生的通道数
  • kernel_size (int or tuple) - 卷积内核的大小
  • stride (int 或 tuple, 可选) - 卷积的步幅。默认值:1
  • padding (int, tuple or str, 可选) – 添加到 输入。默认值:0
  • padding_mode(str,可选)–、或。违约:‘zeros’‘reflect’‘replicate’‘circular’‘zeros’
  • dilation (int or tuple, 可选) - 内核元素之间的间距。默认值:1
  • groups (int, 可选) – 来自输入的阻塞连接数 通道到输出通道。默认值:1
  • bias (bool, 可选) – 如果 ,则向 输出。违约:TrueTrue

⭐torch.nn.MaxPool2d

orch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)

参数

  • kernel_size (Union[int, Tuple[int, int]]) - 要占用最大值的窗口的大小
  • stride (Union[int, Tuple[int, int]]) - 窗口的步幅。默认值为kernel_size
  • padding (Union[int, Tuple[int , int]]) - 在两边添加隐式负无穷大填充
  • dilation (Union[int, Tuple[int, int]]) – 控制窗口中元素步幅的参数
  • return_indices (bool) – if ,将返回最大索引和输出。 以后有用True
  • ceil_mode (bool) – 当为 True 时,将使用 ceil 而不是 floor 来计算输出形状
    ⭐torch.nn.Linear
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

参数

  • in_features (int) – 每个输入样本的大小
  • out_features (int) – 每个输出样本的大小
  • bias (bool) - 如果设置为 ,则层不会学习加性偏差。 违约:FalseTrue
    ⭐torch.nn.ReLU(inplace=False)

在这里插入图片描述
梯度下降算法涉及的函数

1. optimizer.zero_grad()#梯度置零
2. loss.backward()#反向传播
3. optimizer.step()#梯度下降

2.模型训练

lenet代码如下:

# 定义LeNet的网络结构
class LeNet(nn.Module):
    def __init__(self, num_classes=10):
        super(LeNet, self).__init__()
        # 卷积层1:输入1个通道,输出6个通道,卷积核大小为5x5
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
        # 卷积层2:输入6个通道,输出16个通道,卷积核大小为5x5
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
        # 全连接层1:输入16x4x4=256个节点,输出120个节点,由于输入数据略有差异,修改为16x4x4
        self.fc1 = nn.Linear(in_features=16 * 4 * 4, out_features=120)
        # 全连接层2:输入120个节点,输出84个节点
        self.fc2 = nn.Linear(in_features=120, out_features=84)
        # 输出层:输入84个节点,输出10个节点
        self.fc3 = nn.Linear(in_features=84, out_features=num_classes)

    def forward(self, x):
        # 使用ReLU激活函数,并进行最大池化
        x = torch.relu(self.conv1(x))
        x = nn.functional.max_pool2d(x, kernel_size=2)
        # 使用ReLU激活函数,并进行最大池化
        x = torch.relu(self.conv2(x))
        x = nn.functional.max_pool2d(x, kernel_size=2)
        # 将多维张量展平为一维张量
        x = x.view(-1, 16 * 4 * 4)
        # 全连接层
        x = torch.relu(self.fc1(x))
        # 全连接层
        x = torch.relu(self.fc2(x))
        # 全连接层
        x = self.fc3(x)
        return x

完整代码如下

# 设置随机种子
torch.manual_seed(0)

# 定义模型、优化器、损失函数
model = LeNet()
optimizer = optim.SGD(model.parameters(), lr=0.025)
criterion = nn.CrossEntropyLoss()

# 设置数据变换和数据加载器
transform = transforms.Compose([
    transforms.ToTensor(),  # 将数据转换为张量
])

# 加载训练数据
train_dataset = datasets.MNIST(root='../data/mnist/', train=True, download=True, transform=transform)
# 实例化训练数据加载器
train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True)
# 加载测试数据
test_dataset = datasets.MNIST(root='../data/mnist/', train=False, download=True, transform=transform)
# 实例化测试数据加载器
test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False)

# 设置epoch数并开始训练
num_epochs = 10  # 设置epoch数
train_loss_history = []
train_acc_history = []
test_loss_history = []
test_acc_history = []

# tqdm用于显示进度条并评估任务时间开销
for epoch in tqdm(range(num_epochs), file=sys.stdout):
    # 记录损失和预测正确数
    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    size = len(train_loader.dataset)  # 训练集的大小,一共60000张图片
    num_batches = len(train_loader)  # 批次数目,1875(60000/32)
    # 批量训练
    model.train()
    for inputs, labels in train_loader:
        # 预测、损失函数、反向传播
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # 记录训练集loss
        train_acc+=((outputs.argmax(1) == labels).type(torch.float).sum().item())
        train_loss += loss.item()
    train_acc /= size
    train_loss /= num_batches

    # 测试模型,不计算梯度

    test_loss, test_acc = 0, 0
    size = len(test_loader.dataset)  # 测试集的大小,一共10000张图片
    num_batches = len(test_loader)  # 批次数目,313(10000/32=312.5,向上取整)
    model.eval()
    with torch.no_grad():
        for inputs, labels in test_loader:
            # 预测
            outputs = model(inputs)
            # 记录测试集预测正确数
            test_acc += (outputs.argmax(1) == labels).sum().item()
            test_loss += loss.item()

    test_acc /= size
    test_loss /= num_batches
    train_acc_history.append(train_acc)
    train_loss_history.append(train_loss)
    test_acc_history.append(test_acc)
    test_loss_history.append(test_loss)
    # 打印中间值
    # if epoch % 2 == 0:
    #     # tqdm.write("Epoch: {0} Loss: {1} Acc: {2}".format(epoch, loss_history[-1], acc_history[-1]))
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
    tqdm.write(template.format(epoch + 1, train_acc * 100, train_loss, test_acc * 100, test_loss))

使用Matplotlib绘制损失和准确率的曲线图

import matplotlib.pyplot as plt

epochs_range = range(num_epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc_history, label='Training Accuracy')
plt.plot(epochs_range, test_acc_history, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss_history, label='Training Loss')
plt.plot(epochs_range, test_loss_history, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

训练结果

结果
在训练结果
损失和准确率的曲线图

损失和准确率的曲线图


总结

本周主要通过对手写数字识别复习了深度学习基本框架和函数,下周尝试使用非官方数据集进行学习。路漫漫其修远兮,吾将上下而求索!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值