用IDEA设计一个基于Java的手写数字识别程序,要求能识别0~9之间的数字。
对图片的信息进行处理,首先想到的就是卷积神经网络。
我选用了残差网络,由卷积层、池化层、全连接层组成,并用反向传播算法争取让损失函数降到最小,以此提高准确率。
我们知道,图像是由一个一个像素点组成的,要做图像处理,首先考虑的就是对像素点的处理。我们把像素点的颜色深度称为灰度值,把对像素点的处理转为对灰度值的处理。
如图3.1所示,一个常规的x是方方正正的,计算机很容易识别出来,但人们日常手写时,难免会有局部加粗,一定角度倾斜等情况,这时候就需要用到卷积神经网络。
图3.1
我们像图3.2那样提取出x的特征
图3.2
把这三个特征图像转为灰度值矩阵
图3.3
图3.3中这三个灰度值矩阵称为特征提取器(过滤器)filter。假设要提取某一个特征,只需要将该过滤器与原图像的灰度值矩阵相卷积(这里设步长为1),若得到了一个全1矩阵,则说明原图像匹配该特征。
图3.4
再将卷积后的矩阵平均后放入结果矩阵中
图3.5
挨个卷积,得到如图3.6所示结果
图3.6
易知,过滤器不同,即可提取出不同的特征
比如垂直边缘检测时,常用矩阵 1 0 -1 来提取特征,如图3.7
1 0 -1
1 0 -1
图3.7
可以明显地看到,原图像左边亮,右边暗,中间有一道明显的明暗分界线,在使用垂直检测后,得到了一个中间亮的结果图,中间灰度值不为0的那条线就是原图像的垂直的明暗分界线。
在实际处理时,图片的像素往往比6*6大得多,可能需要进行上亿次卷积运算,计算量庞大很不方便。于是有人提出了“池化层”这一概念。
它类似于采样,根据“采样”标准不同,分为最大值池化,均值池化,随机池化,中值池化,组合池化等。其中最大值池化应用最为广泛。
图3.8
它是将输入的图像划分为若干个矩形区域,对每个子区域输出最大值,这样会不断地减小数据的空间大小,因此参数的数量和计算量也会下降,这在一定程度上也控制了过拟合。通常来说,CNN的卷积层之间都会周期性地插入池化层。
线性整流函数(Rectified Linear Unit, ReLU),又称修正线性单元,是一种人工神经网络中常用的激活函数,通常指代以斜坡函数及其变种为代表的非线性函数。
ReLU函数其实是分段线性函数,把所有的负值都变为0,而正值不变,这种操作被成为单侧抑制。可别小看这个简单的操作,正因为有了这单侧抑制,才使得神经网络中的神经元也具有了稀疏激活性。尤其体现在深度神经网络模型(如CNN)中,当模型增加N层之后,理论上ReLU神经元的激活率将降低2的N次方倍。
那么问题来了:这种稀疏性有何作用?换句话说,我们为什么需要让神经元稀疏?不妨举例子来说明,当看名侦探柯南的时候,我们可以根据故事情节进行思考和推理,这时用到的是我们的大脑左半球;而当看蒙面唱将时,我们可以跟着歌手一起哼唱,这时用到的则是我们的右半球。左半球侧重理性思维,而右半球侧重感性思维。也就是说,当我们在进行运算或者欣赏时,都会有一部分神经元处于激活或是抑制状态,可以说是各司其职。再比如,生病了去医院看病,检查报告里面上百项指标,但跟病情相关的通常只有那么几个。与之类似,当训练一个深度分类模型的时候,和目标相关的特征往往也就那么几个,因此通过ReLU实现稀疏后的模型能够更好地挖掘相关特征,拟合训练数据。
此外,相比于其它激活函数来说,ReLU有以下优势:对于线性函数而言,ReLU的表达能力更强,尤其体现在深度网络中;而对于非线性函数而言,ReLU由于非负区间的梯度为常数,因此不存在梯度消失问题,使得模型的收敛速度维持在一个稳定状态。这里稍微描述一下什么是梯度消失问题:当梯度小于1时,预测值与真实值之间的误差每传播一层会衰减一次,如果在深层模型中使用sigmoid作为激活函数,这种现象尤为明显,将导致模型收敛停滞不前。
全连接层中的每个神经元与其前一层的所有神经元进行全连接,整合卷积层或者池化层中具有类别区分性的局部信息,在整个卷积神经网络中起到“分类器”的作用。
如果说卷积层、池化层和激活函数层等操作是将原始数据映射到隐层特征空间的话,全连接层则起到将学到的“分布式特征表示”映射到样本标记空间作用。
说人话,就是,全连接层的作用是分类,对全连接层之前的提取出来的特征进行分类从而识别出原图像是什么。
如图3.9,卷积层和池化层已经提取出这五个特征,但仍然看不出来这是什么,这时就需要全连接层来对这些特征进行整合,发现这是一只正在行走的猫,将其分类为“猫”,如图3.10。
图3.9
图3.10
那么全连接层是怎么实现的?
图3.11
给每个值赋予一个权重,所谓权重就是该特征值在原图像中所占的重要程度。比如要识别数字“1”,人们更关注相近灰度值的区域是否笔直且连续,该特征所占的权重显然比“灰度值相近区域是否呈现交叉特性(字母X)”要大。
将经过卷积层、池化层后得到的矩阵中的每个值,乘以对应的权重,累加后即得到原图像是某种东西的概率。
对于不同图像的不同特征,对每个神经元赋予不同的权重,这是一个繁琐且困难的工作,Henry J. Kelley提出了反向传播算法。
一个图片是猫,但识别的是狗,神经网络就判断错误了。损失函数用来评价模型的预测值和真实值不一样的程度。
比如对于普通网络,错误率与权重大致呈V曲线。损失函数越小,模型的性能越好,可以对损失函数求导,求出导数为零的点,进而得到最小值所在的权重区间。修改每一个神经元的权重来进行微调,使得损失函数最小
图3.12
在主函数中确定训练集的位置,我把训练集放在了C盘临时目录下
UI界面如图
有清除、学习、记忆、训练四个按钮,学习就是告诉神经网络这个数字是几,然后它会自动将该图像放到训练集中的“x”类(x = 0~9)
记忆就是将训练集中的图像加载到库中以供对比
可以在Network类中修改printCorrentRatio方法来显示或隐藏记忆的进度条
记忆完毕后,在画板上手写一个数字,点击“测试”即可
这次课程设计不仅加深了我对卷积神经网络的理解,跟让我体会到了项目开发的不易。
之前觉得卷积神经网络、机器学习很高大上,普通人接触不到,这次学习让我了解了如何使用这两种方法实现图像处理。卷积是一种运算方法,通过一定大小的卷积核作用于图像信息的局部区域获得图像的局部信息,设定步长后通过滑窗来获取图像的全部信息。
经过卷积层、激活函数、池化层后得到了图像的特征,在全连接层中根据权重将原图像分类从而识别出图像。在识别过程中存在识别错误情况,用损失函数来评价模型的预测值和真实值不一样的程度。为了减小损失函数,Henry J. Kelley提出了反向传播算法,主要是由激励传播、权重更新循环迭代构成,直到网络的输出满足一定条件才停止。
做项目的过程中碰到了各种奇奇怪怪的问题,依赖的导入、Jdk版本要保持一致,注意要用UTF-8编码,