基于BP神经网络的手写数字识别报告


  • 基于BP神经网络的手写数字识别报告
    • 一、任务描述
    • 二、数据集来源
    • 三、方法
      • 3.1 数据集处理方法
      • 3.2.模型结构设计
      • 3.3.模型算法
    • 四、实验
      • 4.1.实验环境描述
      • 4.2.数据集描述(样本数量描述、分布)
      • 4.3模型代码片段描述
      • 4.4.模型训练
      • 4.5.实验结果分析(结果可视化)
    • 五、结论与展望
      • 1.结论
      • 2.展望

一、任务描述

MNIST数据集是手写数字识别问题中最经典的数据集之一,很多模型在该数据集上取得了良好效果。但由于其来自美国国家标准与技术研究所,数字写法不够丰富,不符合中国人的书写习惯,在某些情况下难以识别出正确效果。
本次作业的主要任务是使用扩充后的手写数字数据集训练一个全连接神经网络模型(BP),使之适应中国人的书写习惯,提升识别效果。
利用深度学习实现手写数字识别,当输入一张手写图片后,能够准确的识别出该图片中数字是几。输出内容是0、1、2、3、4、5、6、7、8、9的其中一个。


二、数据集来源

数据集由两部分组成,一部分是原始MNIST数据集,另一部分是自制手写数字数据集。
原始MNIST数据集:
MNIST数据集是NIST(National Institute of Standards and Technology,美国国家标准与技术研究所)数据集的一个子集,MNIST 数据集可在 http://yann.lecun.com/exdb/mnist/ 获取,主要包括四个文件:

文件名 描述
train-images-idx3-ubyte.gz 55000张训练集,5000张验证集
train-labels-idx1-ubyte.gz 训练集图片对应的标签
t10k-images-idx3-ubyte.gz 10000张测试集
t10k-labels-idx1-ubyte.gz 测试集图片对应的标签

加入自制手写数字数据集后:

文件名 描述
train-images-idx3-ubyte.gz 63090张训练集,7010张验证集
train-labels-idx1-ubyte.gz 训练集图片对应的标签
t10k-images-idx3-ubyte.gz 10010张测试集
t10k-labels-idx1-ubyte.gz 测试集图片对应的标签

三、方法

3.1 数据集处理方法

  • 在A4纸上均匀划分为4x6个区域,分别写下0~9的数字,各若干张。

  • 将图片切割成4x6张小图片

  • 将图片resize到(28,28)

  • 最后转换为MNIST格式的数据集
    MNIST格式介绍:MNIST链接

  • TRAINING SET LABEL FILE (train-labels-idx1-ubyte):

[offset] [type] [value] [Description]
0000 32 bit integer 0x00000801(2049)
0004 32 bit integer 60000
0008 unsigned byte ?? label
0009 unsigned byte ?? label
xxxx unsigned byte ?? label

The labels values are 0 to 9.



  • TRAINING SET IMAGE FILE (train-images-idx3-ubyte):
[offset] [type] [value] [description]
0000 32 bit integer 0x00000803(2051) magic number
0004 32 bit integer 60000 number of images
0008 32 bit integer 28 number of rows
0012 32 bit integer 28 number of columns
0016 unsigned byte ?? pixel
0017 unsigned byte ?? pixel
xxx unsigned byte ?? pixel
  • 将数据集转为MNIST格式
    将原MNIST数据集由MNIST格式转为图片;新图片加入后,在从图片转为MNIST格式。
  # header for label arrayhexval = "{0:#0{1}x}".format(len(FileList), 10)  # number of files in HEXheader = array('B')header.extend([0, 0, 8, 1])header.append(int('0x' + hexval[2:][:2], 16))header.append(int('0x' + hexval[4:][:2], 16))header.append(int('0x' + hexval[6:][:2], 16))header.append(int('0x' + hexval[8:][:2], 16))print(hexval[2:][:2])print(hexval[2:][2:])print(header)data_label = header + data_labelhexval = "{0:#0{1}x}".format(width, 10)  # width in HEXheader.append(int('0x' + hexval[2:][:2], 16))header.append(int('0x' + hexval[4:][:2], 16))header.append(int('0x' + hexval[6:][:2], 16))header.append(int('0x' + hexval[8:][:2], 16))hexval = "{0:#0{1}x}".format(height, 10)  # height in HEXheader.append(int('0x' + hexval[2:][:2], 16))header.append(int('0x' + hexval[4:][:2], 16))header.append(int('0x' + hexval[6:][:2], 16))header.append(int('0x' + hexval[8:][:2], 16))header[3] = 3  # Changing MSB for image data (0x00000803)data_image = header + data_imageoutput_file = open(name[1] + '-images-idx3-ubyte', 'wb')data_image.tofile(output_file)output_file.close()output_file = open(name[1] + '-labels-idx1-ubyte', 'wb')data_label.tofile(output_file)output_file.close()# gzip resulting filesfor name in Names:os.system('gzip ' + name[1] + '-images-idx3-ubyte')os.system('gzip ' + name[1] + '-labels-idx1-ubyte')

3.2.模型结构设计

模型此次作业使用的是最简单的BP模型

class NeuralNetwork(nn.Module):# 类中的方法第一个参数一定是self,代表实例对象def __init__(self):# NeuralNetwork继承nn.Module,下面这段代码就是对继承自父类nn.Module的属性进行初始化super(NeuralNetwork, self).__init__()# 展平一个连续范围的维度,输出类型为Tensorself.flatten = nn.Flatten()self.linear_relu_stack = nn.Sequential(nn.Linear(28 * 28, 512),nn.ReLU(),nn.Linear(512, 512),nn.ReLU(),nn.Linear(512, 10),)def forward(self, x):x = self.flatten(x)# logins常用于表示最终的全连接层的输出logins = self.linear_relu_stack(x)return logins

3.3.模型算法

# 定义超参数
learning_rate = 1e-3
batch_size = 64
epochs = 200
flag="A"  #A means origial MNIST;B for mine
#选择模型,loss,optimizer
model = NeuralNetwork().to(device) #BP神经网络
loss_fn = nn.CrossEntropyLoss()
# 优化器,优化算法我们采用SGD随机梯度下降,模型内部的参数(w,b)已经被初始化好了
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)# 训练train_dataset
def train_loop(dataloader, model, loss_fn, optimizer):# training_data是MNIST对象,train_dataloader.dataset从train_dataloader取出该对象,len方法返回数据集的大小size = len(dataloader.dataset)# batch 代表从dataloader中抽取出的第几个batch_size,是通过枚举enumerate得到的序号。X是64个image的Tensor,y是对应的标签for batch, (X, y) in enumerate(dataloader):  #enmumerate:枚举,元素一个个列举出来# Compute prediction and losspred = model(X.to(device))  # pred包含了64个样本的输出,是一个64*10的Tensorloss = loss_fn(pred, y.to(device))# Back propagationoptimizer.zero_grad()  # 重置模型参数的梯度,默认情况下梯度会迭代相加loss.backward()  # 反向传播预测损失,计算梯度optimizer.step()  # 梯度下降,w = w - lr * 梯度。随机梯度下降是迭代的,通过随机噪声能避免鞍点的出现loss=loss.item()if batch % 100 == 0:  # 取余的数值可以自己设置loss, current = loss, batch * batch_size  # 我将len(X)替换为了batch_sizeprint(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")  train_loss_list.append(round(loss, 7))# 损失加入到列表中# 训练test_dataset
def test_loop(dataloader, model, loss_fn):

四、实验

4.1.实验环境描述

Ubuntu 18.04
Python3.7
torch 1.2.0
torchvision 0.4.0
numpy 1.21.5
matplotlib 3.5.2

4.2.数据集描述(样本数量描述、分布)

数字 0 1 2 3 4 5 6 7 8 9 总计
原MNIST频数 5923 6742 5958 6131 5842 5421 5918 6265 5851 5949 60000
新加的频数 1010 1010 1010 1010 1010 1010 1010 1010 1010 1010 10100
总计 6933 7752 6968 7141 6852 6431 6928 7275 6861 6959 70100

4.3模型代码片段描述

  • 数据集处理
#数据集处理
img = Image.open(filename)
size=img.size
print(size)
if size[0]>size[1]:{}
else: img = np.rot90(img)           #(168, 112)size = img.size
print(size)
# 准备将图片切割成4x6张小图片
rows=4
columns=6
weight = int(size[0] // columns)
height = int(size[1] // rows)
for j in range(rows):for i in range(columns):box = (weight * i, height * j, weight * (i + 1), height * (j + 1))pth=f'./02-dataset/test-01/{k}'if not os.path.exists(pth):os.makedirs(pth)#调用系统命令行来创建文件f=f'R{j}C{i}_tensor({k}).png'dir=os.path.join(pth,f)region = img.crop(box)  # 进行裁剪region=region.resize((28,28))   #(168, 112)ret,thresh = cv2.threshold(region,100,0,cv2.THRESH_BINARY_INV)region.save(dir)

Train Loop

# 训练train_dataset
def train_loop(dataloader, model, loss_fn, optimizer):# training_data是MNIST对象,train_dataloader.dataset从train_dataloader取出该对象,len方法返回数据集的大小size = len(dataloader.dataset)# batch 代表从dataloader中抽取出的第几个batch_size,是通过枚举enumerate得到的序号。X是64个image的Tensor,y是对应的标签for batch, (X, y) in enumerate(dataloader):  #enmumerate:枚举,元素一个个列举出来# Compute prediction and losspred = model(X.to(device))  # pred包含了64个样本的输出,是一个64*10的Tensorloss = loss_fn(pred, y.to(device))# Back propagationoptimizer.zero_grad()  # 重置模型参数的梯度,默认情况下梯度会迭代相加loss.backward()  # 反向传播预测损失,计算梯度optimizer.step()  # 梯度下降,w = w - lr * 梯度。随机梯度下降是迭代的,通过随机噪声能避免鞍点的出现loss=loss.item()if batch % 100 == 0:  # 取余的数值可以自己设置loss, current = loss, batch * batch_size  # 我将len(X)替换为了batch_sizeprint(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")  train_loss_list.append(round(loss, 7))# 损失加入到列表中

Test Loop

# 训练test_dataset
def test_loop(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)test_loss, correct = 0, 0# 被with torch.no_grad()包住的代码,不会被跟踪反向梯度计算,也就是grad_fn不会变with torch.no_grad():for X, y in dataloader:pred = model(X.to(device))test_loss += loss_fn(pred, y.to(device)).item()# 通过将tensor中的布尔值转换为0/1并求和,获得BP模型识别成功的样本图片correct += (pred.argmax(1) == y.to(device)).type(torch.float).sum().item()print(pred.argmax(1).cpu())y_true_list.append(y)y_pred_list.append(pred.argmax(1).cpu())test_loss /= num_batchescorrect /= sizeprint(f"Test Error: \n Accuracy: {(100 * correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")test_loss_list.append(round(test_loss, 7))   # 损失加入到列表中test_acc_list.append(round(correct,7))  # correct加入到列表中

4.4.模型训练

在模型训练过程中,学习率和batch_size暂时未作修改。
通过增加epochs的数量,从epochs=100到epochs=200(如果使用了五折交叉验证,对应epochs=20到epochs=40做了对比);
扩充数据集从60000张扩充到70100张,以及使用五折交叉验证的方法,不断提高训练的准确性。

  • 超参数的信息如下:

learning_rate = 1e-3
batch_size = 64
epochs = 200

  • 数据集扩充到70100,epochs=200时的loss及准确率,见下图:
Epoch 200
-------------------------------
loss: 0.055270  [    0/70100]
loss: 0.051059  [ 6400/70100]
loss: 0.031387  [12800/70100]
loss: 0.161061  [19200/70100]
loss: 0.152146  [25600/70100]
loss: 0.128452  [32000/70100]
loss: 0.060355  [38400/70100]
loss: 0.159482  [44800/70100]
loss: 0.097903  [51200/70100]
loss: 0.203770  [57600/70100]
loss: 0.063589  [64000/70100]
Test Error: Accuracy: 96.4%, Avg loss: 0.120329

4.5.实验结果分析(结果可视化)

  • loss曲线
train loss test loss

  • 混淆矩阵
原数据集 新数据集

  • 消融实验 ablation study
模型 epochs 是否使用五折交叉验证 数据集 准确率
BP 50 60000 91.7%
BP 100 60000 94.1%
BP 20 60000 94.6%
BP 200 60000 96.1%
BP 200 70100 96.4%
BP 40 70100 96.8%

五、结论与展望

1.结论

  • 通过增加epochs能够提高准确率
  • 通过使用五折交叉验证能够提高准确率
  • 通过增加数据集样本数量能够提高准确率

2.展望

  • 从提高模型准确率方面
    – 针对误判率较高的数字,增加更多的训练样本,提高准确率
    – 调整超参数,对比不同超参数的调整对训练结果的影响
    – 后面可以使用CNN模型来训练
  • 从应用场景方面
    – 拓展到中文数字的识别
    – 拓展到任意大小图片内数字的识别

【第一个深度学习模型应用-手写数字识别】相关推荐

  1. 深度学习数字仪表盘识别_【深度学习系列】手写数字识别实战

    上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下.不过呢,这块内容太复杂了,所以就简单的介绍一下padd ...

  2. 深度学习项目实战——手写数字识别项目

    摘要 本文将介绍的有关于的paddle的实战的相关的问题,并分析相关的代码的阅读和解释.并扩展有关于的python的有关的语言.介绍了深度学习步骤: 1. 数据处理:读取数据 和 预处理操作 2. 模 ...

  3. 深度学习入门实践学习——手写数字识别(百度飞桨平台)——上篇

    一.项目平台 百度飞桨 二.项目框架 1.数据处理: 2.模型设计:网络结构,损失函数: 3.训练配置:优化器,资源配置: 4.训练过程: 5.保存加载. 三.手写数字识别任务 1.构建神经网络流程: ...

  4. [深度学习-1]神经网络-手写数字识别

    Date:2018年9月29记 数据及彭亮给出的数据集和源码在下面网址: https://github.com/mnielsen/neural-networks-and-deep-learning 在 ...

  5. 【学习日记】手写数字识别及神经网络基本模型

    2021.10.7 [学习日记]手写数字识别及神经网络基本模型 1 概述 张量(tensor)是数字的容器,是矩阵向任意维度的推广,其维度称为轴(axis).深度学习的本质是对张量做各种运算处理,其分 ...

  6. AI Studio 飞桨 零基础入门深度学习笔记6.3-手写数字识别之数据处理

    AI Studio 飞桨 零基础入门深度学习笔记6.3-手写数字识别之数据处理) 概述 前提条件 读入数据并划分数据集 扩展阅读:为什么学术界的模型总在不断精进呢? 训练样本乱序.生成批次数据 校验数 ...

  7. TensorFlow基础12-(keras.Sequential模型以及使用Sequential模型 实现手写数字识别)

    记录TensorFlow听课笔记 文章目录 记录TensorFlow听课笔记 一,Sequential模型 二,实现手写数字识别 一,Sequential模型 二,实现手写数字识别 #使用Sequen ...

  8. 深度学习,实现手写字体识别(大数据人工智能公司)

    手写字体识别是指给定一系列的手写字体图片以及对应的标签,构建模型进行学习,目标是对于一张新的手写字体图片能够自动识别出对应的文字或数字.通过深度学习构建普通神经网络和卷积神经网络,处理手写字体数据.通 ...

  9. 菜菜学paddle第一篇:单层网络构建手写数字识别

    前言: 1.数字识别是计算机从纸质文档.照片或其他来源接收.理解并识别可读的数字的能力,目前比较受关注的是手写数字识别.手写数字识别是一个典型的图像分类问题,已经被广泛应用于汇款单号识别.手写邮政编码 ...

最新文章

  1. CSS position财产
  2. java 字符串xml,解析java中的xml字符串?
  3. win10之dlib安装 c++调用
  4. Linux centos openshift安装教程整理
  5. Visual Studio与C#编程十个实用技巧
  6. 罗宾斯管理学13版pdf_全球著名管理学家,曾任五大高校教授,罗宾斯教你如何做好管理者...
  7. Unity加载本地图片的2种方式
  8. 全局光照模型与Rendering Equation(全局光照的最为基础的核心理论) .
  9. android中所有颜色大全
  10. MATLAB利用Copula函数进行频率分析
  11. Spring Cloud 基础教程 - 程序猿DD
  12. Delphi 安卓11 中文语音合成(中文朗读)注意内容
  13. linux查找文件夹命令
  14. ubuntu18.04安装微信——简单操作
  15. Tair存储引擎简单介绍以及常见API操作
  16. PAT/PTA甲级2020春季题目【满分】弃坑贴
  17. SAP idoc功能够强大: 采购订单下达自动触发销售订单创建
  18. 【reversing.kr逆向之旅】Ransomware的writeup
  19. 包装设计模式-Decorator
  20. Win Server 2012发布!回顾Server发展史

热门文章

  1. 区块链下一个入口之证券型代币STO交易所
  2. iOS创建浮动按钮,点击按钮“置顶”
  3. Redis学习一:Redis两种持久化机制
  4. 网站对接支付宝,微信支付接口史上最详细教程
  5. OpenCV实践:低对比度图像检测圆形轮廓
  6. 苏宁不蹭618,造了一个“宝宝节”
  7. 用Javascript实现的嵌入式软件定制界面
  8. GOOGLE 人机验证(RECAPTCHA)无法显示解决方案(转)
  9. matlab 多相滤波,数字多相网络的滤波原理
  10. CSS常用属性-3.1字体文字-1.字体font-family-2.尺寸font-size-3.样式font-style-4.粗细font-weight-5.简写属性font