# 基于神经网络的 Minst 手写数字识别
### 前言
这篇文章中,将具体阐述如何通过计算机用简单的神经网络算法来实现手写数字的识别,并简单阐述背后的基本原理。文章的后面可以看到, 经过训练后的神经网络对手写数字的识别可以达到 94%~95 的准确率。
文章的代码部分是用 MATLAB 来实现的。文章结构是由“论述 + 代码”组成的,每一个小标题的文字论述后面都会有具体的代码实现。
## 1.目标
首先需要明确一下我们的目标:识别手写数字。这里我们采用的是来自 MINST 的手写数据集,由 60000 个数字图片以及对应的人工识别数字组成。我们拿一些出来看一下:
![](https://www.writebug.com/myres/static/uploads/2021/12/30/baca0275b473ed2d3a1ea5828b595c36.writebug)
MINST 数据集(截取部分)
我们很容易知道这些数字图片的规格为 28x28 像素。
**如何下载并在 MATLAB 中导入这些数据呢?**
这里为了方便,我直接给出下载地址:[识别标签](https://link.jianshu.com/?t=http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz) [图片数据](https://link.jianshu.com/?t=http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz)
每一张图片都是灰度图片,即每一个像素点都是由 0-255 来描述它的黑色浓度的(姑且这么叫吧)。我们将他们下载好了之后需要将导入 MATLAB,并做一些简单的处理。包括将 28x28 的矩阵转换为 784x1 的矩阵,归一化处理将每一个像素的值转换到 0~1 之间(很容易想到除以 255 就可以了),将数据文件中的一些描述性文字去除。
这里就不再赘述具体实现,直接调用下面的代码就可以将数据导入。
这是导入标签数据集的 function 代码:
```matlab
function labels = loadMNISTLabels(filename)
%loadMNISTLabels returns a [number of MNIST images]x1 matrix containing the labels for the MNIST images
fp = fopen(filename, 'rb');
assert(fp ~= -1, ['Could not open ', filename, '']);
magic = fread(fp, 1, 'int32', 0, 'ieee-be');
assert(magic == 2049, ['Bad magic number in ', filename, '']);
numLabels = fread(fp, 1, 'int32', 0, 'ieee-be');
labels = fread(fp, inf, 'unsigned char');
assert(size(labels,1) == numLabels, 'Mismatch in label count');
fclose(fp);
end
```
导入图片数据集的 function 代码:
```matlab
function images = loadMNISTImages(filename)
%loadMNISTImages returns a 28x28x[number of MNIST images] matrix containing the raw MNIST images
fp = fopen(filename, 'rb');
assert(fp ~= -1, ['Could not open ', filename, '']);
magic = fread(fp, 1, 'int32', 0, 'ieee-be');
assert(magic == 2051, ['Bad magic number in ', filename, '']);
numImages = fread(fp, 1, 'int32', 0, 'ieee-be');
numRows = fread(fp, 1, 'int32', 0, 'ieee-be');
numCols = fread(fp, 1, 'int32', 0, 'ieee-be');
images = fread(fp, inf, 'unsigned char');
images = reshape(images, numCols, numRows, numImages);
images = permute(images,[2 1 3]);
fclose(fp);
% Reshape to #pixels x #examples
images = reshape(images, size(images, 1) * size(images, 2), size(images, 3));
% Convert to double and rescale to [0,1]
images = double(images) / 255;
end
```
## 2.如何进行识别
我们已经知道对于,识别这样的数字对人脑来说十分简单。但怎么让计算机也可以对这样的手写数字进行识别呢?
正如标题所说这里我们用到的是神经网络的算法,具体而言是一个单隐层前馈网络。
![](https://www.writebug.com/myres/static/uploads/2021/12/30/fb49942d404a66a0e6d7f5a4da62ed36.writebug)
接下来我们具体地来论述这个所谓的“单隐层前馈网络”是什么意思,它代表了什么。以及对这个网络结构为什么能给出一张手写数字的识别结果建立一些直觉上的认识。
在生物神经网络中,每个神经元与其他的神经元相连接,当一个神经元的电位大于一定的阈值时,它就会被激活从而“兴奋”,并且向其他神经元发送化学物质,引起其他神经元电位状态的改变。
现在我们先抛开上面那张乱七八糟的图片不管,先来看一个简单的东西,它的名字叫做“感知器”(Perceptron),是一种人造神经元。
![](https://www.writebug.com/myres/static/uploads/2021/12/30/bec70864395c70597e46215f75d1b3e4.writebug)
感知器
一个感知器可以有多个输入,一个输出。上图是一个拥有 3 个输入一个输出的感知器。它的工作原理是这样的:先把我们的每一个输入乘以一个权重,将他们累加起来,当累加的值超过了阈值(threshold)时,感知器的输出为 1,否则为 0。用数学语言描述如下:
![](https://www.writebug.com/myres/static/uploads/2021/12/30/062152cc7edc94c3a33f5689fdd880ed.writebug)
如何直观地来感受这个感知器呢?举个例子吧,假设我们需要用一个感知器来判断一部手机是不是你想要的?我们为这个感知器设计 3 个输入参数:X1 是屏幕大小,X2 是屏占比,X3 是手机重量,输出 1 时为你想要的,0 时则不是。
我们可以通过调整权重和阈值来进行决策,也就是判断一部手机是否是你想要的。比如你比较在乎的是屏幕和重量,而对屏占比不怎么看重。那么这就意味着屏幕和和重量的权重会比较大,而屏占比的权重比较小。也就是说,当我们给定了这一个感知器的所有权重和阈值时,拿一部手机到你面前,你是否想要这一部手机的决策可以由这个感知器给出。
现在比如我们想要用单个感知器识别一张图片是否为 9。
![](https://www.writebug.com/myres/static/uploads/2021/12/30/431cbdfab8cb453de822321aad01dc4d.writebug)
通过观察上面这张图片,我们人工地给这样一个感知器大概的描述:
我们给图片中的黑色部分的位置一个较高的权值,说明这些位置的黑色浓度对一张图片是不是 9 的决策影响很大;给白色部分的权值赋一个很小的值,说明当这些位置的黑色浓度对一张图片是不是 9 的决策几乎没有影响。这就是感知器做出决策的过程,而我们上面这个“人工观察”的过程可以由计算机学习多张图片来完成,具体过程不再赘述。
需要注意:感知器只有输出层神经元进行激活函数处理,即只拥有一层功能神经元,其学习能力非常有限。显然单个感知器的是无法完全地解决我们的目标:“识别手写数字”的。这里我们采用一种称为“多层前馈神经网络”的结构,如下所示:
![](https://www.writebug.com/myres/static/uploads/2021/12/30/167e598253490e80916da7da69216125.writebug)
多层前馈神经网络
每层神经元与下一层神经元全互连,神经元之间不存在同层连接,也不存在跨层连接,每一层的输出作为下一层的输入,这样的神经网络结构称之为“多层前馈神经网络”。第一层为输入层,最后一层为输出层,中间的层称之为隐层。显然,对比我们之前的单感知器结构,只是多了中间的隐层而已。而我们的功能神经元便是由隐层和输出层的神经元来组成的。
需要注意的是:输入层是不对数据进行处理的,它只是把所有的数据原封不动地引入而已。
现在回到我们的手写识别问题,我们的每一张图片是由 784 个像素组成的,所以我们的一张图片应该对应了 784 输入层神经元。而我们的识别范围是 0-9,因此需要完全表征识别的数字至少需要 10 个输出层神经元。现在还剩下隐层的神经元没有确定。实际上隐层的层数和神经元的数目是不固定的,且隐层层数越多,神经元数目越大识别效果越好,但也就意味着学习时间会加长。
这里我们采用的是单隐层,15 个隐层神经元的网络结
- 1
- 2
前往页