2009-09-03 16:20:00 Mycro 阅读数 3489

 

数字图像中手写阿拉伯数字的识别技术概览

撰文 周翔   

摘要:
图像中手写阿拉伯数字的识别和其他模式的识别所采用的方法是多种多样的。本文论述了图像中手写阿拉伯数字的识别过程,并对手写数字识别的三种方法(基于规则的方法、基于统计的方法和基于神经网络的方法)进行了简要介绍和分析,并通过实例重点对基于规则的方法进行了描述。最后是对这些方法的简要评价。

1. 手写数字图像识别简介

手写阿拉伯数字识别是图像内容识别中较为简单的一个应用领域,原因有被识别的模式数较少(只有0到9,10个阿拉伯数字)、阿拉伯数字笔画少并且简单等。手写阿拉伯数字的识别采用的方法相对于人脸识别、汉字识别等应用领域来说可以采用更为灵活的方法,例如基于规则的方法、基于有限状态自动机的方法、基于统计的方法和基于神经网络的方法等。目前比较流行的方法是基于神经网络的方法和基于统计的方法,但无论使用哪种方法,也需要通过基本的图像处理技术来对图像进行预处理,才能获得这些方法的输入信息。所以,本文的开始部分先对手写阿拉伯数字识别的整个处理流程进行论述,而这个流程也可以用于图像中其他模式的识别。当然这个处理流程也不是唯一的,可以根据不同的模式识别应用场景进行与之不同的预处理流程。

2. 手写数字图像识别的主要流程

在本文中,笔者在对图像进行分析前,先对图像进行了以下处理操作。

第一步:对源图像进行黑白二值化处理,如图1。

       
图1:源图像(左)和处理后的图像(右)

第二步:对第一步处理后的图像进行形态学图像处理中的开运算,然后再进行闭运算(如图2),先进行开运算的目的是去除图像中的离散黑色像素点,再进行闭运算可以填补手写数字中的裂缝。为了突出变换前后的区别,图2中将源图像中的一部分放大显示,可以看出,经过处理,数字7上面横线中的空虚白色像素被黑色像素填实。


          
图2:进行闭操作前的二值图像(左)和经过闭操作后的二值图像(右)

第三步:为了提取出图像中的每一个数字,可用泛洪(FloodFill)算法,从数字上的某个像素开始对数字进行填充,也可以按从左到右、从上到下的顺序扫描图像,找到一个黑色的像素开始填充,当填充结束时,所得到的填充区域就是图中某个手写阿拉伯数字的图像区域,这时,得到这个阿拉伯数字图像区域中像素坐标点对的集合,如图3所示。

   
图3:对第二步处理后的图像中的某个像素进行泛洪填充提取出的数字5的图像

第四步:通过第三步得到的某个数字的像素坐标点对的集合,通过骨架提取算法(见参考文献3)提取出手写数字的骨架,再将其骨架图像映射到某个8×8的0/1矩阵,矩阵中值为1的点表示手写数字骨架经过的矩阵中的点,0表示数字的背景,要求数字图像的每一行或每一列都至少有一个值为1,如图4所示。

        
图4:数字5的骨架(左)和映射到8×8矩阵中的情况(图中灰色表示1,白色表示0)

可以想象出,矩阵的行列数越大,表示的骨架越精确,但对于手写阿拉伯数字的识别来说,取8×8的矩阵就足够了。如果要进行汉字手写体的识别,一般需要用64×64的矩阵或者128×128的矩阵。

第五步:对8×8的0/1矩阵进行内容分析。其中分析的方法主要有基于规则的方法、基于统计的方法和基于神经网络的方法,这三种方法将在本文的第3、4、5部分进行介绍。通过这些方法可以识别8×8的0/1矩阵中的数字,然后输出结果,转向第三步对图像中的其他数字进行识别。

3. 基于规则的方法

基于规则的方法主要是根据0/1矩阵的特征对矩阵所描述的图形表示的内容进行描述。在这里,我采用的是对矩阵的一行为一步进行特征判断。可以事先规定阿拉伯数字在矩阵中靠左上角显示,则有这样的规则:如果矩阵第一行中有两个值为1,而且这两个1之间有大于1个的0,则这个矩阵所表示的数字为4。用规则的方法进行识别时,可以采用规则树的结构进行判断,树的每一层对应矩阵某一行的规则,比如,对于n行矩阵,树的第k层对应矩阵的第k行的规则(1<=k<=n),如图5所示,树中的节点是“规则{满足规则的数字的集合}”的形式。
 


图5:规则树

在运行该算法时,程序会从根节点(开始节点)开始根据规则沿着某条分支到达叶节点,这时候算法结束,输出节点集合中的元素。往往这样的集合中的元素个数为1个,也有时候是多个,比如手写体的数字1和7很像,当算法结束时,有可能会输出两个结果。

这个算法的时间复杂度正比于矩阵的行数,在本例中,因为矩阵有8行,则最多需要进行8步判断可以得出识别结果。可见,算法的复杂度与规则树的分叉数无关。而规则分的越细,分叉数越多,对象的区分度越好,搜索过程中对树的遍历深度越少,识别的正确率就越高。可见,定义一系列精密的规则是采用本方法进行模式识别的关键。

当然,也可以每一步按每列的规则进行判断,方法与上面的叙述类似。

4. 基于统计的方法

基于统计的模式识别方法是根据系统已有的统计信息,在当前的实例情况下,取概率最大的一个模式。这里的模式是阿拉伯数字。如果设当前的实例为E,阿拉伯数字为N,则我们要求的是对所有的E,条件概率值P(N|E)最大的一个N。即:

 


对上式的右端进行分母归一化处理并假设所有阿拉伯数字出现的概率是相等的,则上式可简化为:

 

也就是要求对于数字N,它的图像是E的概率为最大的那个数字N。当然,对整个图像求概率得到的结果是非常小的,而且求解过程比较困难,我们可以对整个图像进行区域划分,进行粒度计算得出在每个区域中对应的数字出现的概率,并将这些概率值进行平滑处理或放大处理,然后把这些概率值相乘,最后取条件概率最大的一个数字,就是阿拉伯数字的识别结果。

同时,还可以采用隐马尔可夫模型(HMM)的思想(见参考文献2),如果按照在本文第2节中的预处理流程得到的结果,设观察序列是8×8矩阵中的0/1值,状态是阿拉伯数字,可以通过样本进行参数训练,得出HMM的参数,然后通过Viterbi算法得出在已知状态序列(8×8矩阵中的0/1值)的情况下,求出概率最大的状态(即阿拉伯数字,也就是识别结果)。

5. 基于神经网络的方法

神经网络的方法是采用人类大脑神经中学习反馈的思想,通过用户训练得出正确的识别结果。您可以通过网页http://www.vckbase.net/document/viewdoc/?id=1187来查看这种方法的具体实现过程和源代码。

6. 分析与总结

通过上面的分析,我们可以看出,基于规则的方法,相对较为简单,比如手写阿拉伯数字识别等识别对象较少的情况,有比较少的时间复杂度和比较高的识别正确率,这种方法的关键在于规则的定义对模式是否有较高的区分度;这种方法对于比较复杂的情况,则需要一个规则库来保存这些规则,但这时,规则的设计也会变的复杂而且困难。

基于统计的方法需要维护一个具有一定规模的样本库,而且在使用HMM进行数字识别时计算量较大。样本库的规模越大,样本的分布越接近于实际情况,数字识别的正确率越高。在使用基于统计的方法的时候还需要数据平滑的技术来扩大小概率的值。

基于神经网络的方法是当今应用的最广泛的方法,其特点在于样本数可以比较少,神经节点的激励函数的运算与HMM中的概率计算相比较为简单,因此有比较好的运行效率,实现比较简单。但识别的过程需要人的参与(训练),识别的正确率受用户主观因素的影响。

基于有限状态自动机的方法也可以看做是基于规则的方法,单独将这种方法作为数字识别的系统比较少,因为对于复杂的应用,形成的有限状态自动机的拓扑结构往往比较复杂。在比较简单的情况下,如7段码数字识别,将会有较其他基于规则的方法更快的识别效率和更高的准确率。

参考文献:
[1]  MICHAEL SIPSER著,张立昂等译,《计算理论导引》,机械工业出版社,2000。
[2] 王晓龙,关毅等编,《计算机自然语言处理》,清华大学出版社,2005。
[3] R.C.Gonzales等著,阮秋崎等译,《数字图像处理》,电子工业出版社,2002。
[4] 王文杰等编,《人工智能原理》,人民邮电出版社,2003。

 

2017-05-29 17:53:32 GregoryHanson 阅读数 4975

 

                       数字图像处理课程论文

                         题目:数字图像识别

 

摘要

        模式识别(PatternRecognition)是一项借助计算机,就人类对外部世界某一特定环境中的客体、过程和现象的识别功能(包括视觉、听觉、触觉、判断等)进行自动模拟的科学技术,也是指对表征事物或现象的各种形式的(数值的、文字的和逻辑关系的)信息进行处理和分析,以对事物或现象进行描述、辨认、分类和解释的过程,是信息科学和人工智能的重要组成部分。

        本文针对手写数字图像识别问题,采用了图像预处理与k近邻算法进行识别。本文的图像训练集约有2000张数字0~9的手写数字图片,测试集共有约1000张数字0~9的手写数字图片。

        在预处理阶段,使用matlab软件进行操作。首先通过大小变换将训练集、测试集所有图片统一转化为32像素*32像素大小的图像,然后对图像进行中值滤波,滤出噪声。对去噪后的图像进行二值化处理,将所有图像转化为只包含0或1的矩阵。并用文本文件存储,为文本文件进行科学的命名。

        在k近邻算法中,采用欧氏距离度量图像间距离大小。采用多数表决法进行分类判别。通过比较不同的k值影响,发现当k=3时对本文采用的数据集具有最好的识别结果,946张测试集图片中仅识别错误11张图片,正确率高达98.84%。在运算速度方面,平均识别出一个数字只需0.037秒。对此结果进行科学的推算,预计从一个汽车车牌号中识别出所有数字与字母需约0.79秒。具有速度较快、精确率高、易于实现的特点。

 

关键词:K近邻法 中值滤波 二值化图像 手写数字图像识别

 

一、问题重述

        随着当今社会的日新月异及信息化进程的快速发展,我们如今正被数字化时代笼罩着,数字正朝着庖代我们对话语和文字的语言表达、记忆的方向进展。对于生活中的数字,人脑可以快速地识别出是哪个数字,但是计算机不能直接直观地识别出数字。因此,需利用数字图像处理与机器学习相关知识让计算机识别出图像里的字符。

 

二、问题分析   

        从现实中获取的图像大小不一、包含大量与分类器无关的多余信息,且存在一定噪声等,故需对图像进行预处理。在进行识别之前,需将图像统一为大小相同、适合分类器操作的形式。

        机器学习分为有监督学习与无监督学习两种,其可以完成分类、回归等功能。对于识别手写数字图像,我们应采用有监督学习来完成分类判决。有监督学习方法在图像识别中主要有基于决策理论方法的识别,如k近邻法、决策树等,基于概率理论方法的识别,如贝叶斯分类器等。本文采用基于决策理论方法的k近邻法对图像进行识别。

 

三、模型建立

3.1 图像预处理

        数据预处理包括统一大小、进行二值化处理、数字滤波进行平滑去噪处理及规范化处理等。

3.1.1 图像大小变换

        输入图像大小不一,需将每张图片统一为32像素*32像素大小的图像。

3.2.2 中值滤波

        图像中往往因现实因素含有一定噪声,故采用中值滤波器对图像进行滤波,以使图像可以更加正确地被二值化处理。

        中值滤波法是一种非线性平滑技术,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。

        中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。方法是用某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升(或下降)的为二维数据序列。二维中值滤波输出为g(x,y)=med{f(x-k,y-l),(k,l∈W)},其中,f(x,y),g(x,y)分别为原始图像和处理后图像。W为二维模板,通常为3*3,5*5区域,也可以是不同的的形状,如线状,圆形,十字形,圆环形等。

3.2.3 二值化处理

        对于数字的识别数据获取可以有很多方法,输入的彩色图像包含大量颜色信息,会占用较多的存储空间,且处理时也会降低系统的执行速度,因此对图像进行识别等处理时,常将彩色图像转换为灰度图像,以加快处理速度。输入图像为黑底白字的手写数字图像。为了更加便利的开展图像处理操作,需要对图像做一个二值化处理。设置一个阈值threshold,将灰度图中值大于threshold的像素点置为1,小于等于threshold的像素点置为0,完成二值化处理。

 

3.2 k近邻法

        k近邻法使用的模型实际上对应于特征空间的划分。模型由三个基本要素——距离度量、k值选择和分类决策规则决定。

3.2.1 模型简介

        k近邻法中,当训练集、距离度量(如欧氏距离)、k值及分类决策规则(如多数表决)确定后,对于任何一个新的输入实例,它所属的类唯一地确定。这相当于根据上述要素将特征空间划分为一些子空间,确定子空间里的每个点所属的类。这一事实要从最近邻算法中可以看得很清楚。

        特征空间中,对每个训练实例点xi,距离该点比其他点更近的所有点组成一个区域,叫做单元。每个训练实例点拥有一个单元,所有训练实例点的单元构成对特征空间的一个划分。最近邻法将实例xi的类yi作为其单元中所有点的类标记。这样,每个单元的实例点的类别是确定的。

3.2.2 距离度量

        特征空间中两个实例点的距离是两个实例点相似程度的反映。k近邻模型的特征空间一般是n维实数向量空间Rn。

        设特征空间X是n维实数向量空间Rn,xi,xj∈X,xi=(xi(1),xi(2),...,xi(n))T,xj=(xj(1),xj(2),...,xj(n))T,xi,xj的Lp距离定义为

                                                 

        这里p≥1.当p=2时,称为欧氏距离,即

                                                 

        当p=1时,称为曼哈顿距离,即

                                                

        当p=∞时,它是各个坐标距离的最大值,即

                                         ​​        

        在本文中,选取欧氏距离作为距离度量。

3.2.3 分类决策规则   

        k近邻法中的分类决策规则往往是多数表决,即由输入实例的k个临近的训练实例中的多数表决决定输入实例的类。

        多数表决规则有如下解释:如果分类的损失函数为0-1损失函数,分类函数为

                                                  f:Rn→{c1,c2,...,ck}

        那么误分类的概率为

                                                P(Y≠f(X))=1−P(Y=f(X))

        对于给定的实例x∈χ, 其最近邻的k个训练实例点构成集合Nk(x). 如果涵盖Nk(x) 的区域的类别cj, 那么误分类率是

                                      1k∑xi∈Nk(x)I(yi≠cj)=1−1k∑xi∈Nk(x)I(yi=cj)

        要使误分类率最小即经验风险最小,就要使∑xi∈Nk(x)I(yi=cj) 最大。所以多数表决制对应于经验风险最小化。

 

四、模型求解

4.1 图像预处理

        matlab可以快速、有效的对图像进行预处理,其自带的imresize、rgb2gray等函数可以有效的完成大小变换、灰度图转换等功能,故图像预处理部分采用matlab软件进行处理。

4.1.1 收集数据

        下载约3000张符合要求的不重复的手写数字图片。其中0~9十个数字各约300张。每个数字的300张图片中,随机选择约200张作为训练集,100张作为测试集。

        以数字‘0’为例,图一至图三展示数字0训练集188张图片中的前3张。

                                 ​​​​​     

                                        图一数字‘0’在训练集中的第一张图
                                ​​​​​     

                                        图二数字‘0’在训练集中的第二张图
                                 ​​​​​     

                                        图三数字‘0’在训练集中的第三张图

4.1.2 准备数据

(1)中值滤波

        用matlab批量读取图像,对图像进行中值滤波。以数字0在训练集中的第一幅图为例,结果如图四所示。

              

                                 图四含有椒盐噪声的数字图像中值滤波结果

        由结果可见,中值滤波在手写数字图像中可以有效的消除椒盐噪声,方便图像进行进一步处理,其中3*3模板下的中值滤波效果最好,故本文采用3*3模板的中值滤波进行图像滤波的预处理。

(2)二值化处理

        将滤波后的图像转化为灰度图。经实验验证,将阈值threshold设为128的时候,可以完成正确的图像二值化处理。将处理后的图像以二进制文本文件存储。

        以图一展示的手写数字图片为例,图五为存储图一的二进制文本文件。

                                   ​​​​​        

                                  图五 存储图一的手写数字图片的二进制文本文件

4.1.3 分析数据

        读取在准备数据阶段存储的二进制文本并进行图像显示,确保符合要求。

 

4.2 k近邻法   

        Python语言非常适用于数据处理、机器学习等方面的操作。NumPy是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表结构要高效的多。其运算速度与matlab不相上下的同时,具有更强的可移植性。故在此采用Python语言与它的科学计算包NumPy完成程序。

        测试集中共有946个测试手写数字的数字图像处理后的文本文件。取k值为3时,程序返回部分结果如图六:

                                                       图六 k=3时测试集测试部分结果

        由图可见,对于大多数手写数字的图像,k近邻法分类器均能返回正确的结果。在946个测试集中,判断错误的图像只有11个,错误率约为1.16%。由此可见,k近邻法对于进行预处理后的手写数字图像的识别具有较高的识别正确率。

 

五、结果分析

5.1 图像预处理部分分析

        中值滤波法对消除椒盐噪声非常有效,在光学测量条纹图象的相位分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大。本文中3*3模板下的中值滤波可以较好地完成去噪声功能。

        中值滤波在图像处理中,常用于保护边缘信息,是经典的平滑噪声的方法。

 

5.2 k近邻算法部分分析

        现研究k值,即多数表决规则中训练集里得分最高的参与表决的训练样本数量,对测试结果的影响。现分别取k为1到10的整数,进行观察分析比较,结果如表一:

                                                 表一 相同训练集测试集,不同k值下的识别结果比较

K值

1

2

3

4

5

测试集错误数量

13

13

11

14

17

错误率

1.37%

1.37%

1.16%

1.48%

1.80%

K值

6

7

8

9

10

测试集错误数量

19

21

21

22

20

错误率

2.01%

2.22%

2.22%

2.33%

2.11%

        为了更直观的分析,将测试结果绘制成图七:

 

                  

                                                  图七 不同k值下的测试判断错误关系

        由图表所示的实验结果可见,k值在k近邻法分类器中对结果存在着一定的影响,且并非k值越大越好或越小越好。当k值较小时,如k=1时,k近邻又叫最近邻法,此时更易受训练集中某一个意外的巧合下发生错误的训练数据影响。当k值较大时,结果易受一些符合度相对较小的训练集中的结果影响。因此,应取k值为一个偏小的、且又不是非常小的数。如本例中,k取3时结果最佳。

        本例中,k近邻法通过约2000组训练集判断约1000组数据,共需约37秒。平均判断出一个数字需要0.037秒。即一秒钟约能识别出27个手写数字图像中的数字。若训练集中不止包含手写数字,还包含字母,按照此训练集规模来计算,则需要一个约7200组数据文件的训练集。若将此方法应用在识别汽车牌照上的数字与字母(共6位),理论计算识别出一个车牌上的所有英文与字母需约6*(26+10)/10*0.037=0.79秒。此速度对应约98.7%的识别正确率尚能接受。但仍然在速度上可以改进。在实现算法时,可采用构造kd树等方法,可以加快模型判决速度,在此不赘述。

 

六、评价与改进

        k近邻算法是分类数据最简单最有效的算法。它是基于实例的学习,使用算法时必须有接近实际数据的训练样本数据。k近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。此外,必须对数据集中的每个数据计算距离值,实际使用时可能会非常耗时。通过上文实验结果与计算,识别出一个车牌上的所有字母、数字需要约0.79秒。在算法实现上,可通过构建kd树来加快k近邻法的判决速度。

        k近邻算法的另一个缺点是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具有什么特征。对此,我们可以尝试用概率测量方法处理分类问题,如朴素贝叶斯等。然而,在实际应用过程中,繁琐的系统分部密度求取经常给人们带来很多的不方便,且很多时候,参数或概率密度函数未知,因此贝叶斯分类器经常无法广泛使用,相比之下,非参数模式识别分类方法如k近邻算法的适用范围更广。

 

七、参考文献

[1].   刘玲丽,基于PCA变换和k近邻法的印刷体数字图像识别.计算机光盘软件与应用,2013(08):第137+139页.

[2].   韦根原与沈桐,基于最小距离法和近邻法的数字图像识别.仪器仪表用户,2012(05):第53-55页.

[3].   张旭等,基于朴素贝叶斯K近邻的快速图像分类算法.北京航空航天大学学报,2015(02):第302-310页.

[4].   王楠,一种基于K近邻的图像去噪方法.软件导刊,2015(10): 第155-157页.

[5]  李航,《统计学习方法》,清华大学出版社,2012。

[6]  Peter Harrington,《机器学习实战》,人民邮电出版社,2013。

[7]  (美)冈萨雷斯(Gonzalez.R.C.) . (美)伍兹 (Woods.R.E.),《数字图像处理(第三版)》,电子工业出版社,2011。

[8]  姚敏,《数字图像处理(第二版)》,机械工业出版社,2012。

 

八、附录

8.1 图像预处理代码(matlab)

clear;clc;          %清空变量
I=imread('0_0.txt');
% 中值滤波
J= imnoise(I,'salt & pepper',0.02);%添加椒盐噪声
k1=medfilt2(J);             %进行3*3模板中值滤波
k2=medfilt2(J,[5 5]);       %进行5*5模板中值滤波
k3=medfilt2(J,[7 7]);       %进行7*7模板中值滤波
figure,subplot(231),imshow(I);title('原图像');       %以下均为显示图像
subplot(2,3,2),imshow(J);title('添加椒盐噪声图像');
subplot(2,3,3),imshow(k1);title('3*3模板中值滤波');
subplot(2,3,4),imshow(k2);title('5*5模板中值滤波');
subplot(2,3,5),imshow(k3);title('7*7模板中值滤波');

% 二值化处理
picSize=size(k1);           %读取图片大小
throeshold=128;             %设置阈值大小
for i=1:picSize(1)         %二值化处理
    for j=1:picSize(2)
        if k1(i,j)>128
            k1(i,j)=1;
        else
            k1(i,j)=0;
        end
    end
end

8.2 k近邻法判决器代码(python)

# 导入numpy库为矩阵运算等做准备,listdir库为读取文件等做准备
from numpyimport *
importoperator
from osimport listdir

# 判决器函数,在手写数字识别函数中被调用,输入分别为测试数据、训练数据、标签、k近邻算法的k值。输出返回值为判决器判决的数字结果。
defclassify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize,1)) -dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)                     # 欧式距离
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()            # 对训练结果中的欧式距

# 离大小排序
    classCount={}         
    for i in range(k):       # 由距离最小的k个点通过多数表决法判别出结果
        voteIlabel =labels[sortedDistIndicies[i]]
        classCount[voteIlabel] =classCount.get(voteIlabel,0) + 1
    sortedClassCount =sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]
   
# 该函数将图像转化成程序中的矩阵变量
defimg2vector(filename):
    returnVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):                                 # 经过预处理,读取图像大小均为
        lineStr = fr.readline()                    # 32像素*32像素,依次读取这1024
        for j in range(32):                         # 像素点
            returnVect[0,32*i+j] =int(lineStr[j])
    return returnVect

# 手写数字识别函数(运行时运行此函数即可得出结果)

# 训练集与测试集中文件命名方法为a_b.txt,a为该文件存储的数字,b为

# 该数字下的文件编号,如:2_4.txt代表存储数字‘2’的第四个文件
defhandwritingClassTest(k):
    hwLabels = []
    trainingFileList = listdir('trainingDigits')           # 加载训练集中的文件
    m = len(trainingFileList)
    trainingMat = zeros((m,1024))
    
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]           #去掉文件名中的.txt
        classNumStr =int(fileStr.split('_')[0])        # 从文件名中提取该文件
        hwLabels.append(classNumStr)                # 代表数字的正确答案
        trainingMat[i,:] =img2vector('trainingDigits/%s' % fileNameStr)
    testFileList = listdir('testDigits')                 # 测试集的迭代器准备
    errorCount = 0.0                                         # 记录错误个数
    mTest = len(testFileList)
    
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]     # 去掉文件名中的.txt,
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest =img2vector('testDigits/%s' % fileNameStr)
        classifierResult =classify0(vectorUnderTest, trainingMat, hwLabels, k)                # 调用k近邻法的分类器函数,进行判决
        print ("the classifier came back with:%d, the real answer is: %d" % (classifierResult, classNumStr))                            # 输出结果
        if (classifierResult != classNumStr):errorCount += 1.0

    print ("\nthe total number of errorsis: %d" % errorCount)
    print ("\nthe total error rate is:%f" % (errorCount/float(mTest)))

 

2018-11-29 01:20:48 ercong_kang 阅读数 817

关于图像处理——运用CNN实现数字手写体识别的调研

电子科技大学 格拉斯哥学院 2017级 余尔聪

前言

关于图像处理

当下的21世纪是一个充满信息的时代,图像作为人类感知世界的视觉基础,是人类获取信息、表达信息和传递信息的重要手段。数字图像处理技术,即用计算机对图像进行处理的技术,在当下被广泛地运用到医学,教育,军事等多个领域。受学校以图像处理为主题的新生研讨课的启发,在参加完深度学习课程后,希望通过此例对图像识别有更深入的了解。

关于CNN

卷积神经网络(Convolutional Neural Networks,CNN)是一类包含卷积或相关计算且具有深度结构的前馈神经网络(Feedforward Neural Networks),是深度学习(deep learning)的代表算法之一 。由于卷积神经网络能够进行平移不变分类(shift-invariant classification),因此在文献中也被称为“平移不变人工神经网络(Shift-Invariant Artificial Neural Networks, SIANN)” 。
对卷积神经网络的研究始于二十世纪80至90年代,时间延迟网络和LeNet-5是最早被证实有效的卷积神经网络算法;在二十一世纪后,随着数值计算设备的改进,卷积神经网络得到了快速发展,并被大量应用于计算机视觉、自然语言处理等领域 。
FROM 百度百科-卷积神经网络

本例将以CNN的学习规则为基础结合BP算法,参考数据库中的原始MNIST数据,实现简单的数字手写体的识别。

正文

程序架构
大程序框架
以CNN为基础的程序框架主要分为特征提取(Feature Extractor)和分类器(Multi-classifier)两个部分。通过训练数据的前向(Forward Algorithm)输入运算和误差的后向传播(BP Algorithm)运算,多轮训练获得合适的权值,使得输出逐步靠近真实输出。

数据处理

  • 训练数据用0-1编码,以便进行矩阵运算。本例中单个输入样本为28×28矩阵。
  • 输出结果采用单热编码(e.g. ‘1’=[1 0 0 0 0 0 0 0 0 0],仅用于分类区分),以便于网络设计与统计。

特征提取(Feature Extractor)

这一部分分为卷积层和池化层两部分。通过设置滤波器,我们用卷积层提取图像中的特征。(值得注意的是,用大量数据训练提取出的图像特征大部分十分抽象而与人类肉眼判断的相差很大。)随后通过池化层池化降维以便于之后的运算(本例运用取平均值的方法降维)。
特征提取

  • 卷积层设计

    卷积层采用20个9×9的滤波器,滤波结果选取valid部分。其后运用ReLU作为激活函数,传入池化层。
  • 池化层设计

    池化层输入为20个20×20的矩阵。本例池化层采用2×2平均池化,即每四个输出为一个单元,取这四个数的均值为新的输出。最后的池化结果为20个10×10的矩阵。

分类器(Multi-classifier)

这一部分的程序设计采用包含一个有100个节点的单隐层神经网络结构(输入层+隐层+输出层)。基于梯度下降算法和BP算法,用每一个输入调整层与层之间的权重矩阵达到训练网络的目的。
在这里插入图片描述

  • 输入层设计

  1. 从特征提取网络传入的单组样本为20个10×10的矩阵(因为有20个滤波器,故而每一个输入样本有20组输出),为方便之后的分类训练,我们将其转化为一个2000×1的向量;
  2. 设置第一层权重矩阵大小为100×2000;
  3. 运用ReLU作为第一层的激活函数。
  • 隐层设计

  1. 设置隐层权重矩阵大小为10×100;
  2. 设置激活函数为ReLU。
  • 输出层设计

  1. 设置输出层激活函数为Softmax(用于多分类问题的激活函数);
  2. 最终输出结果为10×1的向量,最大值对应表征为1,其余值表征为0。

后向传播的实现(Backward)

本例中,采用后向传播算法不断更新权值,使得最终输出逐步逼近真实值。而后后向传播的关键在于梯度下降的概念,通过从后往前传导误差,达到训练目的,在此不做赘述。
可参照公式:
梯度下降算法原理
(说明:J为代价函数;w为权重矩阵;e为误差;x为前层输出)

算法实现

主程序部分

% this is my main program for MNIST
% CNN and BP are used to build whole structure
% this is a basic one
% name: 余尔聪
% date: 2018-11-28

load('MNISTData.mat'); 
% import TRAINING and TESTING Data

% initiation of variables
X = X_Train; % training data-input
D = D_Train; % training data-output
W1 = randn(9,9,20); % 9×9 Convolution Matrix
W3 = (2*rand(100,2000)-1)/20; % 1st layer Weight Matrix 
W4 = (2*rand(10,100)-1)/10; % 2nd layer Weight Matrix

% Training for 1 time
    [W1,W3,W4] = CNN(X,D,W1,W3,W4);

% Show Accuracy
acc = ACC(D_Test,X_Test,W1,W3,W4);
fprintf('Accuracy is %f\n', acc);

本例只进行了一轮训练作为示例(也因为样本量足够大-60000个,但实际情况下多数需要多轮训练)。

CNN-训练部分

function [W1,W3,W4] = CNN(X,D,W1,W3,W4)
% this is the training function
% comprised of Feature Extractor and Multi-classifier
% inputs include Training input and output, W1, W2, W3
% aims to find proper W1, W2, W3 as output

    dW1 = zeros(9,9,20); % preallocating for being faster
    alpha = 0.01; % step length
    for k = 1:60000 % one epoch for one sample
        x = X(:,:,k); % initiate one epoch input
        d = D(:,k); % initiate one epoch output

前向传播(Forward)

  • 特征提取(Feature Extractor)
        % Forward Algorithm
        
        % Feature Extractor 
        % comprised of Convolution and Pooling
        for m = 1:20
            V1(:,:,m) = conv2(x,rot90(W1(:,:,m),2),'valid'); % Convolution Algorithm
        end
        Y1 = max(0,V1); % Activation Function-ReLU
        Y2 = (Y1(1:2:end,1:2:end,:)+Y1(2:2:end,1:2:end,:)+Y1(1:2:end,2:2:end,:)+Y1(2:2:end,2:2:end,:))/4; 
        % Pooling by 2×2 Mean
  • 分类器(Multi-classifier)
        % Multi-classifier
        y2 = reshape(Y2,[],1); % reshape Y2 into 2000×1 vector
        v3 = W3*y2; % 1st layer calculation
        y3 = max(0,v3); % ReLU
        v = W4*y3; % 2nd layer calculation
        y = Softmax(v); % Softmax-multiclassification

后向传播(Backward)

  • 分类器(Multi-classifier)
        % BP Algorithm
        % Multi-classifier
        e = d-y; % error of output
        delta = e; % cross_entropy+Softmax
        e3 = W4'*delta; % error of 2nd layer
        delta3 = (v3>0).*e3; % ReLU
        e2 = W3'*delta3; % error of 1st layer

        dW4 = alpha*delta*y3'; % change in W4
        dW3 = alpha*delta3*y2'; % change in W3

        W3 = dW3+W3; % update W3
        W4 = dW4+W4; % update W4
  • 特征提取(Feature Extractor)
        % Feature Extractor
        E2 = reshape(e2,size(Y2)); % reshape e2 into 10×10×20 matrix
        E1 = zeros(size(Y1));E2_4 = E2/4; % backward pooling
        E1(1:2:end,1:2:end,:) = E2_4;
        E1(1:2:end,2:2:end,:) = E2_4;
        E1(2:2:end,1:2:end,:) = E2_4;
        E1(2:2:end,2:2:end,:) = E2_4;
        delta1 = (V1>0).*E1; % ReLU
        for m = 1:20
            dW1(:,:,m) = alpha*conv2(x,rot90(delta1(:,:,m),2),'valid');
            % change in W1
        end
        W1 = W1+dW1; % update W1

结果检测

运用测试集(除训练集以外的样本)检测训练结果(三个权重矩阵)。

function acc = ACC(D_Test,X_Test,W1,W3,W4)
% This is Used to Show Accuracy
    N = length(D_Test);
    d_comp = zeros(1,N);
    for k = 1:N
        x = X_Test(:,:,k); % initiate one epoch input
        % Feature Extractor 
        % comprised of Convolution and Pooling
        for m = 1:20
            V1(:,:,m) = conv2(x,rot90(W1(:,:,m),2),'valid'); % Convolution Algorithm
        end
        Y1 = max(0,V1); % Activation Function-ReLU
        Y2 = (Y1(1:2:end,1:2:end,:)+Y1(2:2:end,1:2:end,:)+Y1(1:2:end,2:2:end,:)+Y1(2:2:end,2:2:end,:))/4; 
        % Pooling by 2×2 Average
        
        % Multi-classifier
        y2 = reshape(Y2,[],1); % reshape Y2 into 2000×1 vector
        v3 = W3*y2; % 1st layer calculation
        y3 = max(0,v3); % ReLU
        v = W4*y3; % 2nd layer calculation
        y = Softmax(v); % Softmax-multiclassification
        
        % Statistics Accuracy
        [~, i] = max(y);
        d_comp(k) = i;
    end
[~, d_true] = max(D_Test); % real output
correctMsk = (d_comp == d_true); % counting
acc = sum(correctMsk)/N; % rate calculation
end


结语

结果分析

运行时间

正确率

MAINPro
Accuracy is 0.975800

注:正确率会因为权重矩阵初始化的不同而有所差异。

网络优化

本例中的程序框架使用了CNN算法,其耗时283.148s(当然取决于自己的电脑了),正确率为97.58%左右。为了提高收敛速度,可以考虑使用批量算法,动量算法;为了提高正确率,可以考虑加深网络层数,使用Dropout,改变激活函数,代价函数……以改变网络性能。

写在最后
作为第一次写博客的新手、深度学习的初学者,希望大家多多包涵。
如有错误,望指正~

2017-06-29 14:54:35 qq_32300341 阅读数 1061

实验图片:手写数字图片

实验软件:opencv1.0 、vc++6.0


之前做到了环境配置和数米粒实验。

对opencv有了简单的了解,对像素的处理也有了一定的基础

由于电脑注册表直接爆炸,重新安装了系统,所以导致之前做好的数字识别运行不了,所以环境配置也很重要。环境配置链接如下

http://blog.csdn.net/qq_32300341/article/details/70186379


今天我们来做一个比较简单的数字识别。

第一步是导入图片。

我对图片导入的方法是建立一个char类型的二维数组每一行存放一个图片的地址。

获得了地址,也就是获得了图片。

第二步是划分样本。

我利用采用的是二路交叉验证的方法,所有的数据一半作为训练样本,

另一半作为测试样本。具体划分是第一次测试前一半图片作为测试样本,后一半作为训练样本。

第三步将没副训练样本图片划分16份

opencv中提供了将图片切割的函数,但是对初学者我建议自己写函数,这样对像素的理解比较深刻。

通过遍历图像数据,统计没个小块的黑色像素与小块整体像素个数的比,将这16块个比值存储到一个数组中。

在这个过程中有一个问题,就是划分成16块之后的数据有很多空白块,这些空白块并没有什么作用,

反而会引起误差,我就想把原始图像切割一下变成一个小的数字占比大的图像。我也就这样去做了。

切割图片代码如下:

void clear(IplImage *image,IplImage *&backImg)//去除多余的空白
{
uchar *data= (uchar *)image->imageData;
int  wp = image->widthStep;
int i,j;
int h1=-1,h2=-1,w1=-1,w2=-1;
for( i = 0; i < image->height; i++)//从上至下查找第一个黑色像素的y坐标
{
for(j = 0; j < image->width; j++)
{
f(data[i * wp +  j]==0)
{
h1=i;
break;
}
}
if(h1!=-1)
break;
}
for( i = image->height-1; i >0; i--)//从下至上查找最后一个黑色像素的y坐标
{
for(j = 0; j < image->width; j++)
{
if(data[i * wp +  j]==0)
{
h2=i;
break;
}
}
if(h2!=-1)
break;
}
for(j = 0; j < image->width; j++)//从左至右查找第一个黑色像素坐标
{
for( i = 0; i < image->height; i++)
{
if(data[i * wp +  j]==0)
{
w1=j;
break;
}
}
if(w1!=-1)
break;
}
for(j = image->width-1; j >0; j--)//从走至左查找最后一个像素坐标
{
for( i = 0; i < image->height; i++)
{
if(data[i * wp +  j]==0)
{
w2=j;
break;
}
}
if(w2!=-1)
break;
}
int length;
if((h2-h1)>(w2-w1))//切割后的图片作为一个正方形,和其他图片的比值就一致
length=h2-h1;
else
length=w2-w1;
backImg=cvCreateImage(cvSize(length,length),8,1);//返回图
uchar *data2= (uchar *)image->imageData;
uchar *data1= (uchar *)backImg->imageData;
int wp1=backImg->widthStep;
for( i = h1; i < h1+length; i++)//将当前图片中数字部分复制到返回图上,即切割后的图片
{
for(j = w1; j < w1+length; j++)
{
data1[(i-h1)*wp1+(j-w1)]=data2[i * wp +  j];
}
}
}

事实证明,切割后的准确率比不切割的准确率高。

第四步统计训练样本黑色像素占比,导入测试样本进行识别。

在获得了训练样本的黑色像素比例后。有两种方法进行识别。一种是利用最近邻分类法,不对数据进行处理直接拿测试样本得到的16个比值

与训练样本中所有个体的16个比值进行比较,与差异最小的那个图片分为同一类。另一种就是将训练样本中每个数字的16个比值取平均,

这样每个数字都只有有一个黑色像素比例的数组。然后导入测试样本,与每个数字的黑色像素进行比较,与差异小的分为同一类。

这样就识别出了数字,我利用的是切割后的图片,以及求黑色像素平均值的方法进行识别。识别率在95%左右。



工程下载网页如下:

http://download.csdn.net/detail/qq_32300341/9884089







2018-10-25 16:53:07 ctyqy2015301200079 阅读数 3889


概要: 本实验基于MNIST数据集,采用朴素贝叶斯分类器,实现了0-9数字手写体的识别。本文将简要介绍这一方法的原理、代码实现以及在编程过程中需要注意的若干问题,代码仍然是用MATLAB写成的。
关键字: MATLAB; 图像处理; 数字手写体识别; 朴素贝叶斯分类器
 

1 背景说明

   我的上一篇博客介绍了基于朴素贝叶斯分类器的语音性别识别,用的也是朴素贝叶斯分类器。我在那篇博客中说“之前曾做过用朴素贝叶斯分类器进行数字手写体识别”并且“之后也将整理到此博客上来”——看来这一篇文章是跑不掉了,那还不如尽早写完——好吧,我今天这是履行诺言来了。不过同样地我也不打算介绍“朴素贝叶斯分类器”到底是什么、可以用来干什么了,因为首先它的原理的确too simple,其次网上这方面的资料非常多,而且讲解的水平比我不知道高到哪里去了。大家可以去我的上一篇博客中的“背景说明”中找我推荐的资料链接。

2 关于数据集

2.1 什么是MNIST

   这是我在博客中首次提到MNIST,因此有必要向读者作简要介绍。而且我也打算做一个MNIST的专栏,把自己学过的各种各样的方法——包括传统的方法和学习的方法——都在这个数据集上用一遍,也相当于是学以致用了。

   MNIST是一个开源数据库,它来自美国国家标准与技术研究所(National Institute of Standards and Technology, NIST)。 其中一共包含了60000条训练集数据和10000条测试集数据。其中训练集由来自 250 个不同人手写的0-9数字构成,其中50%是高中学生,50% 来自人口普查局的工作人员;而测试集的手写数字数据也拥有同样的来源及比例。

2.2 数据集处理

   这个数据集显然要比我上一篇博客中提到的kaggle中的数据集更难处理一些。因为数据集的制作者为了方便数据的上传和下载,首先用自己的编码方式将这些图像以字节存储从而变成了4个特殊格式的文件,从而压缩整个数据集的大小。根据MNIST网站的描述,这四个文件分别是:

   train-images-idx3-ubyte.gz: 包含60000个训练集图像
   train-labels-idx1-ubyte.gz: 包含60000个训练集图像的标记(label)
   t10k-images-idx3-ubyte.gz : 包含10000个测试集图像
   t10k-labels-idx1-ubyte.gz : 包含60000个训练集图像的标记(label)

   这些文件的编码方式在网页上也有详细的介绍,读者只需要根据编码方式反过来操作就可以解码得到图像文件了,至于保存成.png还是.jpg还是其他的什么格式当然任凭读者自己决定了,在MATLAB中也就是一行代码的事儿。

   我上传的代码包里有一个名叫“FILE FORMATS FOR THE MNIST DATABASE.txt”的文件,里面就是编解码规则,对上不了外网的读者可能会比较有帮助。

   数据集解码的代码MINIST网页上没有提供,不过也不是什么难事。我根据自己的习惯把原始文件解码到了20个.mat数据包内,按照数字和类别命名:前10个是train0.mat-train9.mat,代表数字0-9的训练集;后10个是test0.mat-test9.mat,代表数字0-9的测试集。

   最后一步就是将这些.mat数据包中的数据条目一个个转换成图像。每个.mat都是一个 若干行x784 的二维矩阵,其行数代表数据条数,列数的784是28的平方,也就是一张数字手写体图像的像素个数(每张数字手写体是 28x28 的灰度图)。

   说道灰度图,那就还有一个问题值得考虑:需不需要转换为二值图呢?请注意二值化本质上是量化阶为2的量化,而灰度图的取值范围是0-255,那也就是量化阶为256的量化。它们的效果谁好谁坏呢,口说无凭,到了后面我们会拿实验结果表态的。

3 代码实现

3.1 文件目录

   现在来介绍一下代码的文件目录以及各个文件之间的联系。本实验用到的全部程序如图1所示:

图1 所需文件列表

 
   看上去代码文件很多,但是有一些代码是准备材料时所用到的,还有一些是辅助性代码,还有一些是为了对比实验结果而另外写的,所以实际上并没有那么夸张。

   详细介绍如下:

   第一个文件夹matdata里的内容有MNIST原始的4个字节文件、解码所需的.m源代码、1个说明性的.txt文件以及解码得到的20个.mat数据包。
   第二个文件夹models里面的内容是在多种参数下训练得到的模型,一共有16个模型。这个文件夹里的内容是可以删除的,只要每次在训练代码Cpic2mat.m或是Dpic2mat.m中改变参数和产生数据包的文件名就可以在这个文件夹下生成新的模型,注意模型的格式也是.mat数据包。
   第三个文件夹othertestpic里面的内容是若干张其他来源的0-9数字手写体图片,用以测试训练产生的额模型的识别效果。
   第四个文件夹test-images里面的内容是10000张测试图片。
   第五个文件夹test-images-smaller是第四个文件夹的子集。
   第六个文件夹train-images里面的内容是60000张训练图片。
   第七个文件夹train-images-smaller是第六个文件夹的子集。
   文件Cpic2mat.m和Dpic2mat.m是训练代码,区别在于前者的训练图像是0-255灰度图,后者训练的是0-1浮点灰度图。
   文件CBayesTesting.m和DBayesTesting.m是验证代码。
   文件CBTSinglePic.m和DBTSinglePic.m是针对单个目标图像的测试代码。
   文件im_pre_here.m是图像预处理代码,功能是将灰度图/黑白图反色。
   文件mat2pic.m是辅助代码,功能是将第一个文件夹的处理结果——数据包——转换成图像。
   最后2个.m文件都是辅助性代码,功能分别是计算某个取值在某个正态分布下的概率以及对整数矩阵进行数据统计。
   最后的.xls文件是实验记录表单,我会直接在后文截图并附上说明的。

   需要说明的是:

   由于我已经完成了大部分预处理和准备工作,所以读者只需要先运行Cpic2mat.m或Dpic2mat.m得到训练模型(模型以.mat格式存储在文件夹models中),再运行CBayesTesting.m或DBayesTesting.m即可得到每个数字手写体的识别正确率。另外,读者也可以继续运行CBTSinglePic.m或DBayesTesting.m来对单个图像进行检测,包括现场书写的数字(当然别忘了做适当的预处理)。

3.2 核心代码

   核心代码其实很简单,就以C字打头的文件为例进行说明。

   数据训练步骤的核心代码如下:

% 对于0-9共10个数字
for A1=1:10
    % 每个数字有多少个样本
    len = length(img_list{A1,1});
    % 确定样本训练(学习)样本数目
    for A2=1:min(len,train_num)
        img_name = img_list{A1,1}(A2).name;
        im = imread([prefix,img_name]);
%         figure;imshow(im);
        % 例如:拆分成7*7=49个小块(Piece),每个小块有4*4=16个元素
        Piece = 1;
        for A3=1:lPiece:29-lPiece
            for A4=1:lPiece:29-lPiece
                temp = im(A3:A3+lPiece-1,A4:A4+lPiece-1);
                % 例如:只要16个中有超过3个是白色,就标记为1(255*4=1020)
                if sum(sum(temp))>= 255*nthres
                    Pattern(A1).feature(Piece,A2) = 1;
                end
                Piece = Piece+1;
            end
        end
    end
    fprintf('Calss %1d:%5d images are written to mat.\n',A1-1,train_num);
end
% ---------至此,结构体数组Pattern写入完毕,即数据集模板已建立-------------

   数据验证步骤的核心代码如下:

% 对于每一类数字(0-9)
correct_num_all = 0;
for A1=1:10
    % 每个数字的每一个测试样本数
    len = length(img_list{A1});
    % 对0-9每一个数字设置一个正确判断量
    correct_num = 0;
    % 对于一个数字的每一个测试样本
    for A2=1:len
        img_name=img_list{A1}(A2).name;
        im=imbinarize(im2double(imread([prefix,img_name])));
%         figure;imshow(im);
        % 对每一个im分割处理
        fe = zeros(nPiece^2,1);
        piece=1;
        for A3=1:lPiece:29-lPiece
            for A4=1:lPiece:29-lPiece
                temp=im(A3:A3+lPiece-1,A4:A4+lPiece-1);
                fe(piece)=sum(sum(temp));
                piece = piece+1;
            end
        end
        
        % 至此,得到了一个测试样本的特征序列fe,接下来识别该样本
        % 用 Bayes 分类器的方法
        % 计算10个条件概率,即P(X|wi),i=0,1,...,9
        % 而 P(X|wi)=∏(k=1,49)P(xk|wi)
        % 可以理解为下面的 P(A2|A1)
       
        % 条件概率
        cond_prob = 2*ones(10,1);
        % 后验概率
        post_prob = zeros(10,1);
        % 对于10个数字
        for A5=1:10
            % 对于待确定数字的每一位 1:49
            for A6=1:nPiece^2
                cond_prob(A5)=cond_prob(A5)*CPattern(A5).feature_prob(A6,fe(A6)+1)*sqrt(2);
            end
            % 后验概率
            post_prob(A5) = cond_prob(A5)*CPattern(A5).prob;
        end
        % 取概率最大的
        [~,I]=max(post_prob);
        % 数字 I-1 就是识别结果,需要和真实数字 A1-1 进行对比
        if I==A1
            correct_num = correct_num+1;
        end
    end
    correct_rate = correct_num/len;
    correct_num_all = correct_num_all+correct_num;
    fprintf('Judgment rate of Class %1d is %.2f%%\n',A1-1,100*correct_rate);
end

   这就得到了每一条待验证的数据被判断为0-9的概率,通过比较这10个概率值的大小即可得出最终判断。

3.3 注意点

   读者可能注意到这一段的架构和我上一篇博客的架构几乎一模一样,解说文字也一样,这并不是我偷懒,而是这两个实验的确非常相似。同样地,这里的注意点也包括了上一篇博客中第3.3章节所提到的那三点需要注意的地方,在此我就不再赘述了。

   但是,它们两者之间也有几点重要的不同,而正是这些不同使得这个小项目实际上比上一篇博客中的语音性别识别更难了一些。不同点主要有3个,下面进行简要介绍。

   第一、语音性别识别是一个二分类问题,而这是一个10分类问题。

   第二、语音性别识别需要考虑的特征有20个,也就是说后面需要计算20个概率连乘;而这里需要考虑的特征最多可以达到 28x28 ,即784个,也就是说最多需要计算784个概率连乘。而这就使得必须进行常数补偿,因为MATLAB也无法表示10的负784次方这种变态数字。

   第三、语音性别识别的量化阶可以设置为10或20或更高,但是设置更高已经没有意义;而这里图像的量化阶有可能需要设置成256,因为0-255的灰度图我们经常使用到,不能说没有意义。这一点会直接扩大需要查询的概率表,着实让程序员(也就是我了)痛苦了一翻,尤其是在检查bug的时候。当然了,如果是bw二值图就方便多了,此时每一个像素点非0即1,概率表直接降级为一个一维数组。

   第四点是一个说明:为什么上面第二点说“最多”784个,也就是还有可能小于784个。对于一个 28x28 的图像,我们既可以逐像素地处理,也可以“逐组”地处理——每多个像素划分成一组就OK了。例如我可以每 2x2 个像素划为一组,这样一张图就有 14x14 个组,这样就只需要计算 14x14=196 个概率连乘了。那么也可以每 4x4 个像素划为一组,计算 7x7=49 个概率连乘……这样做的好处是可以减少特征数,但是坏处有2个:第一,处理的精细程度下降了,这就相当于把 28x28 的图像resize 变小了,最后的识别效果是会下降的;第二,扩大了需要查询的概率表,对于bw二值图像来说,原来一个像素非0即1,而现在一个 NxN 的“组”的像素取值范围变成了 0-(N^2) 。这之间如何取舍,还得靠实验验证,最后权衡之后选一个最合适的值。

4 实验与结果分析

   影响实验结果的因素包含但不限于以下因素:

   1. 训练集数量;
   2. “组”大小的划分,即下表中的“帧尺寸”;
   3. 图像格式,是二值图还是灰度图,实质上是量化阶数量的选取;
   4. 每“组”内的像素划分,即下表中的“分类法”,相当于是把每“组”作为一个整体视为二值图还是4/16值图等等;
   5. 阈值,即若要二值化处理,怎样判定每“组”是黑还是白。

   在进行了若干次有效试验之后,得到如图4所示的实验结果:

图4 实验结果

   从上图可以得出以下几个结论:

   1. 在一定范围内,帧尺寸越小,识别效果越好。当帧尺寸为 1x1 ,即逐像素处理时,识别正确率最高,可以达到84%左右;
   2. 在一定范围内,训练集数目越多,识别效果越好。
   3. 随着帧尺寸的逐渐减小,识别正确率的提升越来越缓慢。而这也是可以预料的,某单一因素的改善对最终结果的改善总有上限,并且改善效果越来越低;
   4. 比较Lab12的两组数据,前者在概率连乘时没有常数补偿,而后者加了乘以sqrt(2)的补偿,最终效果有明显差异,这提醒我们在使用贝叶斯朴素分类器时一定要注意数值计算——尤其是概率连乘——可能带来的问题;

   读者可以自行设计更多的实验,可能会发现更多的结果。

5 后记

   这是我在18年上半年为《数字图像处理》做的小练习,年代不算久远,而且代码完全是自己写的,所以还记得不少当时的想法。因此,如有读者对代码任何细节有疑问,欢迎随时来与我联系。

   代码最终的识别效果84.59%,看上去似乎还不错,但是如果用自己手写的图像一个一个地去测试就会发现,正确率完全达不到这个数据。什么原因呢?我想很可能是因为训练数据太多了,使得训练出来的模型拥有了太多的“个性”,以至于掩盖了我们所希望它所应当具备的“共性”,或者说是“抽象性”。我有一个同学是用CNN来做的MINIST,最后验证集的识别正确率可以达到97%还多,但是最后测试的时候也免不了会遇到这个问题。

   由于图像实在太多了(7万张),压缩很耗时,所以图像我就不传了,但是图像解码的代码我会上传的,所谓“授人以鱼不如授人以渔”嘛;另外,models文件夹我也清空了,给读者自己设计实验的空间。

   转载时务必注明来源及作者。尊重知识产权从我做起。

   代码与其他资料已上传至网络,欢迎下载,密码是hpyx

手写体数字识别

阅读数 60

手写数字识别

阅读数 4850

没有更多推荐了,返回首页