bp神经网络数字识别matlab_BP神经网络实现手写数字识别

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第1张图片

网上已经有许多博客描述过这一算法,但我向你保证,这是你看过的最容易明白的一篇。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第2张图片

机器学习的终极目标是类脑,至于超越人脑,再等个1000年吧。

人们对于人工智能的一个误解是,人工智能应该像人一样思考。事实上人类的大脑可以说是宇宙中最复杂的事物之一了,人工智能不是模仿人脑,而是用机器的方式实现人脑能实现的价值。

神经元是大自然的计算机器,人工神经网络通过模仿生物的神经信号传输过程来实现一些复杂的计算。狗有5亿个神经元,猫只有两亿个,然而狗看起来并没有比猫聪明两倍。作为地球的霸主,人类的神经元大概有1000亿个,然而大象的神经元能达到2500亿个,看起来神经元并不是越多就越聪明的。

生命是世间最伟大的奇迹。千百万年的进化不是计算机几十年的历史就可以超越的,尽管如此,通过模仿的方式,我们仍可以做许多有趣的事情。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第3张图片

1. 计算机视觉

神经网络早在1943年就已经提出,直到1986年反向传播算法(Back-Propagation)的提出才有了飞速的发展,在正式进入算法环节之前,需要介绍一下简单的计算机视觉。如果你能用Python完成PS的动作,请跳过这里。

众所周知,计算机对信号的处理都是通过0 1完成的。要让计算机识别你的手写数字,首先要读入它。

50085e49b05c6ab5c521afad64ce481a.png

这是一个手写数字,希望我的书法不会让你觉得他是4。我们把它转换为2值图。

我希望你可以跟着我一起做,这样才不会觉得我的程序复杂晦涩,你可以自己手写一个数字,或者将你手机的里的自拍照做一下二值化。

import 

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第4张图片

二值图是由0和1构成的图,打开变量h0,我们能更直观地看到计算机绘图的原理:

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第5张图片

二值图太过简单,许多模型都喜欢使用灰度图。

即使你不经常用口红,你也应该知道那些颜色由三原色:红、黄、蓝构成。调整RGB色板的颜色,就可以得到大红、粉红、蓝红、黑红……0-255是颜色的编码,你电脑上的每一个像素点都是由一个在(0,255)的三维数组构成的,不要问我为什么是255不是360。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第6张图片

现在我们尝试以RGB模式读入一张图片,图片还是那个2。

import 

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第7张图片

p1是RGB格式读入的图片,是一个每个数组元素都是三维的数组。P2是灰度图,是一个普通数组。虽然看起来p1更符合灰度图的定义……而p2看起来像“黑度图”。

在手写数字的识别中,我们不关心图形的颜色,所以需要的是灰度图。为了更清楚地了解计算机成像的基本原理,现在自己画一幅灰度图。生成一个28*28的矩阵,矩阵的每个元素是0-255间的随机整数。然后就根据这个矩阵数字编码转换为图形。

你应该大致猜到了这幅图的样子。

dp

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第8张图片

懒得写了,明天再写


2. sigmoid函数和其他基础知识

sigmoid函数:

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第9张图片

为什么是它?

在实际生活中,我们有许多场景需要用到二分类,比如股票是涨还是跌?明天会不会下雨?我写的东西里懂还是不懂?

我们需要一个数学函数能够描述这样的二分类场景,直觉的函数是

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第10张图片

这个阶跃函数最大的问题就是不可导,因此我们需要找到一个这样的函数,他的范围是在(0,1),并且可导。simoid函数恰好具有这些美好的性质。同时,sigmoid函数也是逻辑回归的核心。

矩阵

不知是哪位伟人发明了矩阵,从此,我们不仅要被拉格朗日,还要被拉布拉斯。

许多同学在学矩阵时可能在为矩阵乘法怪异的运算方式而震惊,对于矩阵的具体作用并不清楚,在本文中,你会看到矩阵是如何帮助你简化运算的。

3. 神经网络算法原理

现在我们正式进入神经网络的算法环节,我们先来看一个神经元即感知机(perceptron)是什么样子。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第11张图片

x为原始的输入,他们沿着w传递到神经元中,首先神经元对传入的信号进行初步的加权处理,

,然后将结果传入到激活函数进行进一步处理,激活函数是一个形象的说法,如果信号到达一定的阀值,则激活产生信号,否则不激活。

你可以自己设定认为恰当的激活函数,不一定非要是sigmoid函数。

此时我们已经获得了初步的H(x),传入激活函数得到

,简写成
,这就是单个神经元的最终输出结果。

典型的神经网络分为三层,输入层,隐藏层,输出层。隐藏层可以有多层,如果隐藏层大于1层则称为“深度神经网络”(如下图),有论文指出一般三层神经网络(即只有一层隐藏层)就可以解决大部分问题了。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第12张图片
图片源于网络

现在我们来看一个完整的神经网络是什么样的,为了便于理解,我希望你拿起笔算一算。这里我们借用了https://blog.csdn.net/lyl771857509/article/details/78990215 的例子。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第13张图片

传入隐藏层的输入记为hinput,

,请注意,这只是初步的加权,并不是隐藏层的输出,记隐藏层的输出为houtput,s(x)表示sigmoid函数,那么

我必须再次强调一遍,信息每次经过一个神经元都会经过两次处理,加权和激活函数。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第14张图片

现在信息已经传递到了输出层了,记输出为y

0.464就是我们这个神经网络的最终输出,假设我们的样本真实值是0.1,显然这里有误差,误差的度量我们一般选择离差平方和的方式,设误差为

,则
,这里t表示真实值,算出
=0.132,还记得最开始的那些权重吗?神经网络正是通过迭代更新权重来实现最小化误差。这里选择梯度下降的方法,具体的梯度下降的原理请自行学习,现在我么进行一下权重更新的公式的推导。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第15张图片

你应该还记得

,而
表示sigmoid函数,它的导数恰好是
。我们要最小化
,那么每次权重v更新的梯度为:

,于是
,step是学习的步长,因为2的作用等同与step了,所以可以放心地去掉2。对于w权重的更新是一样的,但是对于隐藏层并没有一个真实值供我们参考,就是说我们更新v的时候有真实值t但是更新w的时候却只有一个h,我们就无法直接计算出隐藏层的误差,再直白一点,就是无法计算(y-t)。

Back-propagation就是指误差的反向传递,一个直觉的反应是根据权重将误差反向传递过去。回到我们的例子中。

,则传递到h1几点的误差是
,我们甚至能将误差反向传播到输出层上去,虽然这并没有实际意义。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第16张图片

在我们的权重更新公式中,error就相当于(y-t)的一种近似。

所以权重w的更新梯度是:

更新停止条件

有两种方式设定停止迭代条件,一是跌代一定的次数,二是误差小于所需要的精确度。

以只有一个输出节点情况为例,现在完整地展现权重更新的过程。首先我们有一个样本,输入为x1,x2。

  1. 首先wi,vi随机赋予权重
  2. 根据权重计算出隐藏层和输出层的值,计算出误差
  3. 利用前面证明的权重更新公式,直接更新一次v
  4. 误差反向传播到隐藏层
  5. 根据隐藏层的误差,利用前面证明的权重更新公式,更新一次w,到此,我们更新了一次所有的权重参数。
  6. 根据新的权重参数重新计算输出层的值,计算误差,直到误差小于一个指定的值时(例如0.0001)停止更新,否则重复2-5

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第17张图片

请注意,大部分网上教程没有指出的是,上述教程只是一个样本的更新过程(是的,一个样本就要计算这么多次!),如果有两个样本怎么办呢?请记住,我们的目标是要使的对于所有的样本,我们有小于指定值的误差,因此,我们权重应该对所有的样本都适用。在更新完一个样本后,我们获得了对于第一个样本来说最优的权重参数,此时,以迭代完成的第一个样本的参数为基础继续更新。什么意思呢,就是把第1步随机赋予权重换成第一个样本得到的最优权重,在此基础上,在新的样本输入上继续更新。

或许看到这里你不禁疑问,已经获得了第一个样本的最优权重参数了,那么继续更新权重参数不是会使得参数变得对第一个样本又不最优了吗,那这不是白算了吗?

事实上,在权重参数已经对第一个样本最优的情况下继续更新,的确会造成偏离第一个样本的最优权重,但是!我们的目标是使权重对所有的样本都最优。

你可能略有疑虑,没关系,看了第二种权重更新方法,你可能会更加疑虑重重。

到达一定的次数停止迭代:

听起来挺简单的,但事实上是怎么做呢?它并不是对于每一个样本都设定一个迭代的次数,而是对所有的样本。让我们来看一看。

  1. 随机赋权。
  2. 输入样本n1,计算出输出值和反向误差,然后迭代一次(只更新一次权重,无论更新后误差有没有达到指定的精度)
  3. 输入第二个样本n2,在第一个样本的基础上继续更新权重......然后对所有的样本都更新一次权重。
  4. 现在所有的样本都对权重进行了一次更新,现在重新输入样本n1,继续更新权重...
  5. 如此循环下去,直到满足指定的训练次数,注意,每一次训练都将所有的样本依次代入。

你可能更加疑惑,也可能豁然开朗。

举个栗子你就明白啦,这个可以说是宇宙最通俗形象的栗子了。

假定有10个人,10个人都有一个最优的站位,在这个最优的站位上,他们所有人的腿都看起来是最长的,所谓相得益彰。现在有两种方法让他们走到最优的位置上,第一种是每一个人都不断地尝试,直到站在最优的站位上,第二个人在第一个人选好了最优站位后,不断尝试选择自己的最优站位,因为第一个人已经选好了位置不能再移动了,第二个人一站过来可能就使得第一个人的站位不再最优了(例如第二个人是个大长腿,一站过来使得第一个人原先最优的站位显得腿不那么长了,即最优的偏离)...这就是第一种迭代方法,尽管它可能使得部分人的站位不最优,甚至所有人的站位都不是最优,但是它使得所有人的总体情况尽可能地好。

第二种是,第一个人先走一步,然后第二个人观察第一个人的位置往自己最优的方向走一步,第三个人观察前两个人的位置然后往自己最优的方向走一步...直到10个人都走了一步。现在又轮到第一个人,它看到其余9个人现在的位置,然后往自己最优的方向走第二步;第二个人观察第一个人的走位后也走了第二步...直到所有人走够了指定的步数停止。这就是第二种迭代方法。

我认为第二种迭代的方法更好一些,更容易理解,程序速度也更快一些,因为只有一次循环,众所周知,python的循环是很慢的。

请注意,我前面的的例子是一个只有一个输出节点的例子,对于有多个输出节点的例子,我们的目标函数就变成了

,尽管它变成了这个样子,但是每次更新权重和前面是一样一样的。

4. 实现手写数字识别

看到本文首页的那些个数字了吗,没错,那些都是我手写的数字,我希望你看完本教程后能够实现对那些数字的识别。

准备样本

那么我们需要什么样的样本数据呢?显然,我们需要一个目标值,需要手写数字的灰度值。本文希望成本最容易理解、最完整的神经网络入门教程,请放心,本文不会做任何跳跃。

import 

上述程序中列表data是一个包含目标值与像素值的列表,第一个数为7表明这个数字是7,后面有28*28=784个数字,你可以数一下。我们把data画出来看一下是什么样子。

bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第18张图片

这正是手写数字7。你可以在你的python中也试一下。这正是我们所需要的样本,我们告诉神经网络,这个数字是7,他的784个特征是这样的,你给我认清楚了。

我们需要的样本已经有人做好了:

训练集 测试集

上述都是很大的数据集,大概100M左右,我们有小一些的:

100条数据 10条数据

创建算法

本文中许多案例都参考了Tariq Rashid的书《Python神经网络编程》,他的Github主页是

makeyourownneuralnetwork/makeyourownneuralnetwork​github.com
bp神经网络数字识别matlab_BP神经网络实现手写数字识别_第19张图片

在这里你可以下载一个基本的神经网络代码。

还记得我向你提到过的矩阵吗,代码中采用许多矩阵运算,如果你看不明白的话,返回我们的算法部分,将我们的预算变成矩阵你就明白了。

这里我直接给出他的代码:

import 

不要问我为什么样本图片是28*28的,为什么隐藏层的节点有200个...我也不知道,大部分用神经网络算法都这么做。

需要注意的一个问题是,我们的输出节点有10个,但显然,我们的输出是不可能直接输出为0,1,2...9这样的数字的(还记得sigmoid函数的范围吗),因此在训练时,我们就要对目标值进行一些改变。一种做法是这样

output

加0.01是因为是因为sigmoid函数的取值范围是(0,1)。

还记得我们的一个样本的第一个数是7吗,这代表了数值7。

output

它返回了最大值的索引,恰好是我们的目标数字7,这样就解决了问题。

此外还有一点需要注意的是,许多机器学习模型都会要求输入数据归一化处理,这样不仅提高模型的收敛速度,还能提高准确率。一般的归一化处理公式是

,
,这样将使数据的范围缩小到(0,1)。

但是这里对数据的归一化处理这样的,

,这样使得数据的范围在(0.01,1]之间。

现在我们已经做了充足的准备,开始训练吧

training_data_file 

只训练10个样本准确率只有70%,增加训练样本数量和迭代次数可以提高准确率,最后的准确率能高达93%。

如果你希望用自己的手写数字来测试一下,你可以打开画图或者拿手机拍一张,但是需要要使得像素是28*28.更改图片的像素可以通过window自带的画图完成,也可以通过以下程序:

import 

我希望你此时此刻充满了欣喜。

你可能感兴趣的:(pytorch实现手写数字识别)