实现mnist手写数字识别(第一周)

🏡 我的环境:

  • 语言环境:Python 3.6.13

  • 编译器:Pycharm 2020.2

  • 深度学习环境:Pytorch 1.10.0

  • 显卡及显存:  Tesla T4 16G(服务器)

🍺 要求:

  1. 了解Pytorch,并使用Pytorch构建一个深度学习程序(✔)
  2. 了解什么是深度学习(✔)

🍻拔高(可选)

  1. 学习文中提到的函数方法(✔)

目录

一、 前言

二、 准备工作

2.1 设置显卡GPU,没有用CPU

2.2 导入数据

2.2.1 torchvision.datasets.MNIST

2.2.2 torch.utils.data.DataLoader

 2.3 数据可视化

2.3.1 squeeze 函数

CNN网络

3.1 nn.Conv2d   

3.3 nn.ReLU

3.4 nn.Linear 

3.5 nn.Sequential

3.6 模型设计(改进CNN模型)

 四训练模型

4.1 设置超参数

4.2 编写训练函数

4.3 编写测试函数

4.4 正式训练

4.5 结果可视化

 五总结


一、 前言

        众所周知,MNIST是机器学习入门的hello word!它是一个大型的手写数字数据库,通常用于训练各种图像处理系统。MNIST 数据库包含 60,000 张训练图像和 10,000 张测试图像。传统机器学习方法已经用这个训练集和测试集进行了测试。结果如下表:

类型准确率(%)
线性分类器92.4
非线性分类器96.7
随机森林97.2

可以看出,传统机器学习方法的准确率还有待提升,因此本文采用深度学习的方法来实现mnist手写数字识别。统计结果来源于MNIST 数据库-维基百科。

二、 准备工作

2.1 设置显卡GPU,没有用CPU

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device(type='cuda')

2.2 导入数据

2.2.1 torchvision.datasets.MNIST

        本文使用的Pytorch自带的数据库torchvision.datasets,这里使用的是torchvision.datasets中的MNIST数据集。

函数原型:

torchvision.datasets.MNIST(root, train=True, transform=None, target_transform=None, download=False)

参数详解:

  • root ( string ) -- 数据集所在的根目录 。训练集文件目录/data/precessed/training.pt,测试集文件目录 /data/precessed/test.pt

  • train ( bool , optional ) – 如果为真,则创建训练集,否则创建测试集。

  • download ( bool , optional ) – 如果为 True,则从 Internet 下载数据集并将其放在根目录中。如果数据集已经下载,则不会再次下载。

  • transform ( callable optional ) -- 一个函数/转换,它接收 PIL 图像并返回转换后的版本。例如,transforms.RandomCrop

  • target_transform ( callable optional ) – 接收目标并对其进行转换的函数/转换。

train_ds = torchvision.datasets.MNIST('data', 
                                      train=True, 
                                      transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
                                      download=True)

test_ds  = torchvision.datasets.MNIST('data', 
                                      train=False, 
                                      transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
                                      download=True)

2.2.2 torch.utils.data.DataLoader

  • 数据加载器。结合数据集和采样器,并提供给定数据集的可迭代对象。
  • 支持具有单进程或多进程加载、自定义加载顺序和可选的自动批处理(整理)和内存固定的地图样式和可迭代样式数据集。
  • 函数原型:

   

    torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=None,...)

参数说明(常用有5个):

  • dataset: Dataset类, 决定数据从哪读取以及如何读取。
  • bathsize: 批大小。
  • num_works: 是否多进程读取机制。
  • shuffle: 每个epoch是否乱序。
  • drop_last: 当样本数不能被batchsize整除时, 是否舍弃最后一批数据。

batch_size = 32

train_dl = torch.utils.data.DataLoader(train_ds, 
                                       batch_size=batch_size, 
                                       shuffle=True)

test_dl  = torch.utils.data.DataLoader(test_ds, 
                                       batch_size=batch_size)
# 取一个批次查看数据格式
# 数据的shape为:[batch_size, channel, height, weight]
# 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
imgs, labels = next(iter(train_dl))
imgs.shape

测试结果

 2.3 数据可视化

2.3.1 squeeze 函数

参考PyTorch官方文档

函数原型:

torch.squeeze( input , dim = None , * , out = None ) 

返回一个张量,其中所有input大小为1的维度都已删除。

举例:

输入:AX1XBXCX1XD

输出:AXBXCXD

#%%
import numpy as np

 # 指定图片大小,图像大小为20宽、5高的绘图(单位为英寸inch)
plt.figure(figsize=(20, 5))
for i, imgs in enumerate(imgs[:20]):
    # 维度缩减
    npimg = np.squeeze(imgs.numpy())
    print(imgs.shape)
    print( npimg.shape)
    # 将整个figure分成2行10列,绘制第i+1个子图。
    plt.subplot(2, 10, i+1)
    plt.imshow(npimg, cmap=plt.cm.binary)
    plt.axis('off')
plt.show()
#%%

 

 

三.CNN网络

构成:特征提取网络和分类网络

其中特征提取网络用于提取图片的特征,分类网络用于将图片进行分类。

函数:

3.1 nn.Conv2d

参考PyTorch官方文档,再结合这篇卷积神经网络(CNN)的整体框架及细节(详细简单)来理解卷积。

为卷积层,用于提取图片的特征,传入参数 (输入通道,输出通道,卷积核大小)

输入:(N,Cin,w,w)

输出:(N,Cout,o,o)


3.2 nn.MaxPool2d   

为池化层,进行下采样。

3.3 nn.ReLU

激活函数,使模型可以拟合非线性数据

参数:

  • 输入:任意数量的维度。

  • 输出:与输入的形状相同。

1

3.4 nn.Linear 

对输入数据应用线性变换。

参数:

  • in_features – 每个输入样本的大小

  • out_features – 每个输出样本的大小

3.5 nn.Sequential

详细解析可以参考这篇博客.pytorch中的顺序容器——torch.nn.Sequential

总的来说,构建模块有以下两种方式:

  • 直接嵌套
  • 以 orderdict 有序字典的方式进行传入

3.6 模型设计(改进CNN模型)

        本文模型使用Sequential构建,第一层使用两个5*5的卷积核,第二层使用两个3*3的卷积核,层与层之间用dropout减少网络规模。更多提升方法参加这篇博客MNIST手写数字识别准确度提升最全、最实用的方法-基于tensorflow

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential,ReLU,Dropout

num_classes = 10 
 
class Model(nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        
        self.conv1=Sequential(
           Conv2d(1, 32, kernel_size=5,padding='same'),# 32*28*28
           ReLU(),
           Conv2d(32, 32, kernel_size=5,padding='same'), # 32*28*28
           ReLU(),
            MaxPool2d(2),   #高宽减半  #32*14*14
            Dropout(0.25)  
        )
            
        self.conv2=Sequential( 
           Conv2d(32, 64, kernel_size=3,padding='same'), # 64*14*14
           ReLU(),
           Conv2d(64, 64, kernel_size=3,padding='same'), # 64*14*14
           ReLU(),   
            MaxPool2d(2), #高宽减半  # 64*7*7
            Dropout(0.25)  
        )
       
        self.fc=Sequential(
            Linear(32*7*7,64),
            ReLU(), 
            Linear(64,num_classes)
            
        )          
            
    def forward(self, x):
       batch_size = x.size(0)
       x = self.conv1(x)  # 一层卷积层,一层激活层,一层卷积层,一层激活层,一层Dropout
       x = self.conv2(x)  # 再来一次
       x = x.view(batch_size, -1)  # flatten 变成全连接网络需要的输入 (batch, 64,7,7) ==> (batch,64*7*7), -1 此处自动算出的是3136
       x = self.fc(x)

       return x

from torchinfo import summary
# 将模型转移到GPU中(我们模型运行均在GPU中进行)
model = Model().to(device)

summary(model)

  

 四 训练模型

4.1 设置超参数

loss_fn    = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-3 # 学习率
opt        = torch.optim.Adam(model.parameters(),lr=learn_rate)

4.2 编写训练函数

 loss.backward()

  •  反向传播,通过autograd包来实现的,autograd自动计算其对应的梯度。
  •  如果没有进行tensor.backward()的话,梯度值将会是None,优先执行loss.backward()函数来计算梯度
#%%

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片
    num_batches = len(dataloader)   # 批次数目,1875(60000/32)

    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率

    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)

        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失

        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新

        # 记录acc与loss
        train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()

    train_acc  /= size
    train_loss /= num_batches

    return train_acc, train_loss

4.3 编写测试函数

def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小,一共10000张图片
    num_batches = len(dataloader)          # 批次数目,313(10000/32=312.5,向上取整)
    test_loss, correct = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            target_pred = model(imgs)
            loss        = loss_fn(target_pred, target)
            
            test_loss += loss.item()
            test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()

    test_acc  /= size
    test_loss /= num_batches

    return test_acc, test_loss

4.4 正式训练

epochs     = 20
train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []
 
for epoch in range(epochs):
    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
    
    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
    
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print('Done')

 

  可以看到模型准确率达到99.5%。

4.5 结果可视化

 五 总结

1.了解pytorch开发基础流程,学习一些函数的使用方法。模型使用Sequential构建,第一层使用两个5*5的卷积核,第二层使用两个3*3的卷积核,层与层之间使用dropout减少网络规模,同时修改优化器为Adam,学习率要修改为0.001.

2.改进后的CNN模型准确率达到了99.5%。

3.其他提升准确率的方法可以参考这篇博客。MNIST手写数字识别准确度提升最全、最实用的方法-tensorflow

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值