MNIST | 基于朴素贝叶斯分类器的0-9数字手写体识别

  • 1 背景说明
  • 2 关于数据集
    • 2.1 什么是MNIST
    • 2.2 数据集处理
  • 3 代码实现
    • 3.1 文件目录
    • 3.2 核心代码
    • 3.3 注意点
  • 4 实验与结果分析
  • 5 后记

概要: 本实验基于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-lPiecefor A4=1:lPiece:29-lPiecetemp = im(A3:A3+lPiece-1,A4:A4+lPiece-1);% 例如:只要16个中有超过3个是白色,就标记为1(255*4=1020)if sum(sum(temp))>= 255*nthresPattern(A1).feature(Piece,A2) = 1;endPiece = Piece+1;endendendfprintf('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:lenimg_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-lPiecefor A4=1:lPiece:29-lPiecetemp=im(A3:A3+lPiece-1,A4:A4+lPiece-1);fe(piece)=sum(sum(temp));piece = piece+1;endend% 至此,得到了一个测试样本的特征序列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:49for A6=1:nPiece^2cond_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==A1correct_num = correct_num+1;endendcorrect_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

MNIST | 基于朴素贝叶斯分类器的0-9数字手写体识别相关推荐

  1. 利用朴素贝叶斯分类器实现手写数字的识别

    利用贝叶斯分类器实现手写数字的识别 贝叶斯决策理论: 条件:类别数一定,

  2. c语言贝叶斯分类,基于朴素贝叶斯分类器的文本分类算法(C语言)

    基于朴素贝叶斯分类器的文本分类算法(C语言) 基于朴素贝叶斯分类器的文本分类算法(C语言).txt两个人吵架,先说对不起的人,并不是认输了,并不是原谅了.他只是比对方更珍惜这份感情.#include ...

  3. 基于朴素贝叶斯分类器的钞票真伪识别模型

    基于朴素贝叶斯分类器的钞票真伪识别模型 内容 本实验通过实现钞票真伪判别案例来展开学习朴素贝叶斯分类器的原理及应用. 本实验的主要技能点: 1. 朴素贝叶斯分类器模型的构建 2. 模型的评估与预测 3 ...

  4. 朴素贝叶斯分类器_MINST手写数字识别

    朴素贝叶斯分类器_MINST手写数字识别 import numpy as np import pandas as pd from sklearn.preprocessing import Binari ...

  5. 基于朴素贝叶斯分类器的西瓜数据集 2.0 预测分类_第十章:利用Python实现朴素贝叶斯模型

    免责声明:本文是通过网络收集并结合自身学习等途径合法获取,仅作为学习交流使用,其版权归出版社或者原创作者所有,并不对涉及的版权问题负责.若原创作者或者出版社认为侵权,请联系及时联系,我将立即删除文章, ...

  6. 基于朴素贝叶斯分类器的西瓜数据集 2.0 预测分类_朴素贝叶斯为什么被称为“朴素”?...

    一.基本理解 1.朴素贝叶斯分类器: 例:如果有一种水果具有红.椭圆形.直径约3英寸等特征,则该水果可以被判定为是苹果. 尽管这些特征相互依赖或者有些特征由其他特征决定,然而朴素贝叶斯分类器认为这些属 ...

  7. 基于朴素贝叶斯分类器的西瓜数据集 2.0 预测分类_朴素贝叶斯(转载自Morgan)...

    什么是朴素贝叶斯 要搞懂朴素贝叶斯分类,首先需要了解什么是贝叶斯定理和特征条件独立假设,朴素贝叶斯算法就是基于这两个来实现的分类方法. 贝叶斯定理 贝叶斯定理通俗点讲就是求在事件 B 已经发生的前提下 ...

  8. 基于朴素贝叶斯分类器的西瓜数据集(实战)

    最近刚开始学习机器学习中的朴素贝叶斯分类器,用西瓜数据集做了一下,最后结果预测正确率75%,其中运用到的python语法并不复杂,适合小白观看. 目录 朴素贝叶斯分类器思想的自然语言描述: 详细步骤在 ...

  9. 基于朴素贝叶斯分类器的西瓜数据集 2.0 预测分类_机器学习之朴素贝叶斯

    1.贝叶斯原理 朴素贝叶斯分类(Naive Bayesian,NB)源于贝叶斯理论,是一类基于概率的分类器,其基本思想:假设样本属性之间相互独立,对于给定的待分类项,求解在此项出现的情况下其他各个类别 ...

最新文章

  1. Java 8 类型转换及改进
  2. 你知道你的模型可以为公司赚多少钱吗
  3. Android Activity使用OnGesture事件以后与子View的Click事件冲突解决办法
  4. android tcp socket框架_socket网络编程知识梳理,让你学会造轮子的能力
  5. Java对象转换成Map
  6. 【Http】Apache HttpClient 4.5实现https
  7. Windows服务器管理(3)——IIS服务器误删了Default Web Site 网站 解决方法
  8. c#调用系统资源大集合(二)
  9. 黑群晖drive套件的使用教程
  10. bootstrap实现树节点、树结构
  11. 无人机运动规划4:ego-swarm无人机群运动规划
  12. ubuntu 20.04安装RTL8821CE无线网卡驱动
  13. AD18 SCH Filter面板——智能查找功能
  14. 华为p4用鸿蒙系统吗_为什么华为有自主研发的鸿蒙系统,却还要用安卓系统,背后的真实原因?...
  15. 前端提效 - js 批量导出 excel 为zip压缩包
  16. 金蝶eas显示连接服务器超时,金蝶EAS常见问题解答_工具及框架应用_2016
  17. 从 RDO到 ADO 的移植
  18. Python基于Django的毕业设计论文提交过程管理系统
  19. 全志 H6 Orange Pi Lite 2 Android 7.0 去除无用配置
  20. SPI协议的Flash

热门文章

  1. android电视查看百度网盘,智能电视也能看百度网盘内的视频,通过当贝市场,方法超简单!...
  2. 笔记本电脑外接显示器无信号 其实是主板静电积压 完全可以不拆机放电
  3. 双臂魔方机器人的学习
  4. 微信号码检测软件是什么?2016年全新升级的微信开通状态检测
  5. check the manual that corresponds to your MySQL server
  6. Python压缩解压--lzma
  7. Oracle 数据库 简单操作命令(新手看 可以收藏哟)
  8. 第一单元 用python学习微积分(五) 隐函数微分法和逆函数导数(上)- 隐函数微分
  9. 网站流量不是很多,还有这几十种途径让网站变现转化
  10. 数据中心机房与机柜理线方法介绍