当前位置:   article > 正文

基于Python的卷积神经网络分类器识别0-9的MNIST手写数据集_mnist手写识别报告

mnist手写识别报告

CNN神经网络实验报告

问题描述

MNIST手写数据集划分训练集和验证集,将自己手写拍照上传的数字图片作为测试样本,训练卷积神经网络分类器识别0-9的手写数字。

数据集说明

MNIST手写数字数据集包含60000张用于训练的手写数字图片和10000张用于测试的手写数字图片。所有的图片具有相同的尺寸(28x28 pixels),数字均位于图片中央。

数据集链接地址如下:

原训练集各类别分布和示例样本如下图所示:
在这里插入图片描述
在这里插入图片描述

图 1 训练集图片示例 图 2 训练集样本分布

在本实验中,原数据集中训练集被划分为训练集和验证集,其中验证集占比0.2,训练集占比0.8。原数据集的测试集作为测试集使用,用于评估模型性能。

在上述的分布图中不难发现,原数据集中的训练集基本上是类别均衡的;为了不破坏原数据集的类别均衡性,在划分训练集和验证集时,采用分类别抽样(stratified sampling)划分的方法。

在这里插入图片描述

图 3 TensorBoard makegrid看到的输入图片

算法代码

算法概述:

模型架构:resnet18修改了第一个卷积层使其接受 BW 格式图片;修改了fc层使其输出10个类别的概率分布。未使用预先训练的权重。

数据处理:图片转换为 Tensor;按照 imagenet 图片第一个通道的均值和方差标准化处理。

权重更新:采用带动量的随机梯度下降法,初始学习率为 1e-3,动量参数为0.9;学习率每7个epoch缩小为0.1倍

损失计算:softmax + crossentropy loss

其他设置:batch_size=128, epoch_num = 24

使用说明: 训练模块设置了五个命令行参数,用户可自行决定数据文件夹位置,日志存放位置,模型保存位置,模型接受图片大小,训练集验证集的划分比例。

第三方包安装:

Pytorch 
Tensorboard 
Sk-learn 
Numpy 
Matplotlib 
  • 1
  • 2
  • 3
  • 4
  • 5

Seaborn(代码中没用上)

导入库

# import necessary packages
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import matplotlib.pyplot as plt
import numpy as np
from torch.utils.data import Dataset, DataLoader, TensorDataset, Subset
import torchvision
from torchvision import transforms,utils,models
import argparse
import seaborn as sns
from collections import Counter
import time
import copy
from torch.utils.tensorboard import SummaryWriter
from sklearn.metrics import confusion_matrix, accuracy_score
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

实验分析

实验结果:

在测试集上的准确率为:0.9863999485969543 Confusion matrix如下所示:

可以看出模型最易混淆的为数字4和数字9

在这里插入图片描述

在这里插入图片描述

从上述训练集的损失和准确率变化可以看出模型在训练集上存在一定的过拟合现象,后续实验可以通过加dropout或者其他正则化手段来避免太大程度的过拟合。

训练过程如下,在下述epoch中选择validset上准确率最高的epoch对应的模型权重作为最终模型。

Epoch 0/24 ---------- 
train Loss: 0.2946 Acc: 0.9147 val Loss: 0.0904 Acc: 0.9732 
Epoch 1/24 ---------- 
train Loss: 0.0623 Acc: 0.9824 val Loss: 0.0672 Acc: 0.9799 
Epoch 2/24 ---------- 
train Loss: 0.0322 Acc: 0.9916 val Loss: 0.0611 Acc: 0.9828 
Epoch 3/24 ---------- 
train Loss: 0.0165 Acc: 0.9967 val Loss: 0.0589 Acc: 0.9812 
Epoch 4/24 ---------- 
train Loss: 0.0092 Acc: 0.9990 val Loss: 0.0566 Acc: 0.9830 
Epoch 5/24 
---------- train Loss: 0.0063 Acc: 0.9995 val Loss: 0.0561 Acc: 0.9830 
Epoch 6/24 ---------- train Loss: 0.0043 Acc: 0.9997 val Loss: 0.0563 Acc: 0.9838 
Epoch 7/24 ---------- train Loss: 0.0033 Acc: 0.9999 val Loss: 0.0565 Acc: 0.9829 
Epoch 8/24 ---------- train Loss: 0.0032 Acc: 0.9999 val Loss: 0.0558 Acc: 0.9835 
Epoch 9/24 ---------- train Loss: 0.0029 Acc: 0.9999 val Loss: 0.0554 Acc: 0.9836 
Epoch 10/24 ---------- train Loss: 0.0028 Acc: 1.0000 val Loss: 0.0556 Acc: 0.9836 
Epoch 11/24 ---------- train Loss: 0.0028 Acc: 1.0000 val Loss: 0.0554 Acc: 0.9838 
Epoch 12/24 ---------- train Loss: 0.0026 Acc: 1.0000 val Loss: 0.0555 Acc: 0.9833 
Epoch 13/24 ---------- train Loss: 0.0026 Acc: 1.0000 val Loss: 0.0556 Acc: 0.9834 Epoch 14/24 ---------- train Loss: 0.0027 Acc: 1.0000 val Loss: 0.0554 Acc: 0.9835 
Epoch 15/24 ---------- train Loss: 0.0025 Acc: 1.0000 val Loss: 0.0553 Acc: 0.9838 
Epoch 16/24 ---------- train Loss: 0.0026 Acc: 1.0000 val Loss: 0.0554 Acc: 0.9840 
Epoch 17/24 ---------- train Loss: 0.0027 Acc: 1.0000 val Loss: 0.0560 Acc: 0.9835 
Epoch 18/24 ---------- train Loss: 0.0025 Acc: 1.0000 val Loss: 0.0550 Acc: 0.9842 
Epoch 19/24 ---------- train Loss: 0.0026 Acc: 0.9999 val Loss: 0.0556 Acc: 0.9831 
Epoch 20/24 ---------- train Loss: 0.0025 Acc: 1.0000 val Loss: 0.0556 Acc: 0.9841 
Epoch 21/24 ---------- train Loss: 0.0025 Acc: 1.0000 val Loss: 0.0559 Acc: 0.9837 
Epoch 22/24 ---------- train Loss: 0.0026 Acc: 1.0000 val Loss: 0.0554 Acc: 0.9834 Epoch 23/24 ---------- 
train Loss: 0.0025 Acc: 1.0000 val Loss: 0.0564 Acc: 0.9835 
Epoch 24/24 ---------- 
train Loss: 0.0024 Acc: 1.0000 val Loss: 0.0562 Acc: 0.9833 
Training complete in 7m 36s 
Best val Acc: 0.984250 
total test accuracy:0.9863999485969543 over 10000 test samples 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

上传10张自己拍摄的手写数字照片,转换为灰度格式,resize到28x28的像素,检测模型输出,代码如下:

def predict_for_customized_data(model,dataPath,device):
    model.eval()
    for i in range(10):
        img1 = Image.open(dataPath/(str(i)+'.JPG')).convert('L')
        sized_img1 = transforms.Resize((28,28))(img1)
        tensor1 = transforms.ToTensor()(sized_img1)
        tensor1 = torch.unsqueeze(tensor1,0)
        assert tensor1.shape == (1,1,28,28)
        inputs = tensor1.to(device)
        outputs = model(inputs)
        print(outputs)
        _, prediction = torch.max(outputs.data, 1)
        print(prediction)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

最终输出如下:

tensor([[ 0.0627, 3.3247, -1.5043, -2.4198, -0.0387, 0.9379, -0.0958, 1.3305,
 -0.1327, -2.4302]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[-0.0849, 3.3900, -1.5465, -2.3464, 0.1393, 0.9960, -0.1491, 1.2198,
 -0.0301, -2.4716]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[ 0.0470, 3.1663, -1.4129, -2.3464, 0.0404, 0.8320, -0.0912, 1.2537,
 0.0387, -2.3751]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[ 0.0604, 3.3914, -1.4023, -2.2675, 0.0237, 0.8554, -0.2231, 1.2724,
 -0.1125, -2.4638]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[ 1.4152e-01, 3.2076e+00, -1.4329e+00, -2.4117e+00, -1.3021e-01,
 8.3629e-01, 1.1261e-04, 1.3240e+00, -4.8039e-02, -2.4092e+00]],
 device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[ 0.0408, 3.3281, -1.4669, -2.3937, 0.0214, 0.9613, -0.1727, 1.3236,
 -0.1440, -2.4152]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[ 0.0352, 3.3936, -1.5284, -2.4327, -0.0555, 0.9108, -0.0615, 1.2695,
 -0.1061, -2.4949]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[ 1.6809e-02, 3.3713e+00, -1.5663e+00, -2.3387e+00, -1.7898e-03,
 9.6868e-01, -1.6990e-01, 1.3084e+00, -1.1342e-01, -2.4470e+00]],
 device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[-0.1321, 3.6067, -1.6129, -2.4619, -0.0661, 1.0501, -0.1779, 1.4918,
 -0.2713, -2.4176]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
tensor([[-0.1155, 3.4807, -1.5790, -2.3580, 0.0713, 1.0390, -0.1681, 1.3022,
 -0.1777, -2.4345]], device='cuda:0', grad_fn=<AddmmBackward>)
tensor([1], device='cuda:0')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

模型将手写数字全部预测为 1,检查输入数据后发现这是因为手写的数字为笔画的地方暗而背景亮。训练集MNIST中的数据却是笔画的地方亮而背景暗,且背景为一致的黑色。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QKkkLUTJ-1664194390831)(https://www.writebug.com/myres/static/uploads/2022/9/26/97dc60160dbcc418962c71cd4d76bdcf.writebug)]

图 9 转为灰度图放缩后的图片

因此如果希望模型能够预测手写数字图片,我们需要对手写数字图片做一些处理。

下图是用像素值最大值255减去整张图片翻转明度后,对于小于60的值置 0的结果,可以看到,由于拍摄照片时光线的因素,数字2的上半部分和下半部分对应的像素亮度不一致。

这样的处理方法得到模型的预测值为

tensor([1], device='cuda:0')
tensor([1], device='cuda:0')
tensor([6], device='cuda:0')
tensor([0], device='cuda:0')
tensor([6], device='cuda:0')
tensor([6], device='cuda:0')
tensor([6], device='cuda:0')
tensor([6], device='cuda:0')
tensor([1], device='cuda:0')
tensor([6], device='cuda:0')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

下图是将输入图片二值化,对于低于128的值置0,其余值置255的结果,可以看到,手写数字4的许多笔画都被遗漏掉了。这样处理之后模型的预测结果为

tensor([5], device='cuda:0')
tensor([5], device='cuda:0')
tensor([1], device='cuda:0')
tensor([2], device='cuda:0')
tensor([5], device='cuda:0')
tensor([5], device='cuda:0')
tensor([1], device='cuda:0')
tensor([1], device='cuda:0')
tensor([1], device='cuda:0')
tensor([1], device='cuda:0')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

解决的方法还可以调用 opencv 的库函数来提高原图对比度,锐化图片之后再进行二值化等,但这样就并不是专注在神经网络模型上了。

对于修改神经网络模型来获得更好的泛化性能使之能够适应自己拍照上传的手写数字这个问题,也许可以在数据集生成时加入一定的数据增强的手段,比如让原数据像素值上下浮动一定范围等。

本模型在最初训练的时候尝试了随机裁剪和水平翻转等数据增强手段,但手写数字数据是一个较为依赖像素顺序,图片方向的数据(比如6和9的上下翻转或者旋转就会破坏原数据携带的信息)。这样做的结果反而不好(模型测试集准确率在83%左右)。

综合来看,模型在同分布的MNIST测试集上表现良好。

s6aunC-1664194390832)]

下图是将输入图片二值化,对于低于128的值置0,其余值置255的结果,可以看到,手写数字4的许多笔画都被遗漏掉了。这样处理之后模型的预测结果为

tensor([5], device='cuda:0')
tensor([5], device='cuda:0')
tensor([1], device='cuda:0')
tensor([2], device='cuda:0')
tensor([5], device='cuda:0')
tensor([5], device='cuda:0')
tensor([1], device='cuda:0')
tensor([1], device='cuda:0')
tensor([1], device='cuda:0')
tensor([1], device='cuda:0')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

解决的方法还可以调用 opencv 的库函数来提高原图对比度,锐化图片之后再进行二值化等,但这样就并不是专注在神经网络模型上了。

对于修改神经网络模型来获得更好的泛化性能使之能够适应自己拍照上传的手写数字这个问题,也许可以在数据集生成时加入一定的数据增强的手段,比如让原数据像素值上下浮动一定范围等。

本模型在最初训练的时候尝试了随机裁剪和水平翻转等数据增强手段,但手写数字数据是一个较为依赖像素顺序,图片方向的数据(比如6和9的上下翻转或者旋转就会破坏原数据携带的信息)。这样做的结果反而不好(模型测试集准确率在83%左右)。

综合来看,模型在同分布的MNIST测试集上表现良好。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/790505
推荐阅读
相关标签
  

闽ICP备14008679号