MNIST手写数字图像分类 - Naive Bayes Classifier

MNIST手写数字图像分类 - Naive Bayes Classifier

引入

在上一篇手写Naive Bayes分类器中,我们成功用自己写的 Naive Bayes 分类器处理了医疗记录数据。那我们的分类器能否用来处理其他类型的数据呢?答案当然是 可! 以! 的!在本次的实验中,我们将尝试一下处理图像数据。有木有很酷炫呢,那就一起搞起来吧!

实际问题

对于我们人类来说,识别手写数字并不是什么太大的问题。但是我们能否让机器也拥有识别手写数字的能力呢?下面我们就要在MNIST数据集上建立 Naive Bayes 分类器,教会机器识别手写数字。

MNIST介绍

MNIST其实是一个被广大人民群众用来练手的数据集,广到搜索引擎搜MNIST就会有大量相关的教程(手动微笑)。言归正传,MNIST中的每个图像由28*28个像素点组成,整个数据集一共包含了60000个训练样本和10000个测试样本,label有10个(0-9)。没错,它已经很贴心的分好了训练和测试。所以打开MNIST会发现有4个文件,分别是训练的image和label,测试的image和label。

MNIST读取

文件下下来以后,定睛一看,发现事情并不简单,它的后缀竟然是".gz"。不必惊慌,这就来解决这个问题。数据的读取方式请参考这里
我们实际需要写的代码如下:

from mnist import MNIST 
mndata = MNIST('保存四个文件的目录')
images,labels = mndata.load_training()
test_images,test_labels = mndata.load_testing()

为了更直观的了解数据集,咱们直接搞一个例子出来看一下:

import numpy as np
import matplotlib.pyplot as plt
p = np.array(images[9], dtype='uint8') 
p = p.reshape((28, 28))
plt.imshow(p, cmap='gray')
plt.show()

MNIST手写数字图像分类 - Naive Bayes Classifier
一张不过瘾,我们把所有类的平均图像做出来看一下:
MNIST手写数字图像分类 - Naive Bayes Classifier

实现

数据了解的差不多,我们可以想一下该怎么实现了。
首先,让我们先考虑一下我们的训练集是什么样的。虽然上面为了更直观的展示,我把图像做成了28 * 28的形状,但是实际上每一个图像的数据都是1 * 784的一长条。也就是说,我们的训练集应当是60000*784的一张大表。(60000个example,784个feature)

训练

搞清楚训练集的的样子和究竟什么是feature以后,我们就可以考虑一下我们训练的究竟是什么了。说白了,Naive Bayes 分类器的训练过程就是在计算label出现的频率(p(y)p(y))和 p(x(i)y))p(x^{(i)}|y))分布的参数。假定 p(x(i)y)p(x^{(i)}|y) 我们还是使用Gaussian distribution,那么我们训练的过程就是(这里看不明白的盆友请参考上一篇,手写Naive Bayes分类器):
1、计算各个label(0-9)在训练集出现的频率,即:
p(y=0),p(y=1)...p(y=9)p(y=0),p(y=1)...p(y=9)
2、给定label的情况下,计算每个feature的均值及方差。
举个栗子,假如要算label为0的情况下的均值和方差,绿色的部分就是结果。(这里只是举个例子,具体数据的存储结构可以自行组织)
MNIST手写数字图像分类 - Naive Bayes Classifier

预测

有了训练好的模型,我们就可以在test上预测一下试试啦。计算的过程也比较简单,其实就是找到使(iP(x(i)y))×P(y)(\prod_iP(x^{(i)} |y))\times P(y)最大的 yy
还想要计算accuracy的话,和test label对比一下就好。到此,识别手写数字问题就完完整整的解决了,仅仅想要了解如何在MNIST上建Naive Bayes Classifier的盆友可以不用看后面了。但是站在解决手写数字识别问题的角度上来看,其实还是有很多可以改进的地方,比如,数据处理,模型选择等等部分都可以改进。

改进

数据处理

仔细观察一下之前做出来的图像就会发现,我们的图像中其实有很大一部分都是空着的。
MNIST手写数字图像分类 - Naive Bayes Classifier
宏观上考虑一下,这一部分的数据可能并没有提供给我们太多的信息,甚至有可能稀释了我们有价值信息的浓度。这么考虑的原因是,我们脑子里可以假设一个极端情况,空白部分如果变得更大,那么图像之间的差距是不是会变得更小(具体情况也取决于差距是如何计算的)。同样如果我们把有笔迹的部分占比放大,那是不是图像之间的差距会相对变大。当然这些都还是猜测和感觉上大概率是对的东西,是否真的有用还要取决于最终的实验结果。
那么具体我们应该如何处理这个图像呢?
1、用最小的矩形框住笔迹部分,只留下框内的部分。

import cv2
x,y,w,h = cv2.boundingRect(p)
img1 = p[y:(y+h), x:(x+w)]
plt.imshow(img1, cmap='gray')
plt.show()

MNIST手写数字图像分类 - Naive Bayes Classifier
2、将上一步的结果拉成同样的大小(20 * 20)。

img2 = cv2.resize(img1, (20,20))   
plt.imshow(img2, cmap='gray')
plt.show()

MNIST手写数字图像分类 - Naive Bayes Classifier
最终结果证明,在用 Naive Bayes 且选择 Gaussian distribution 的情况下,图像处理后的accuracy比处理前要提高很多。但是若换成 Bernoulli distribution 或者换成 Decision Forest,accuracy变化都没有这么明显。

模型选择

感兴趣的盆友可以尝试一下把 Gaussian distribution 换成 Bernoulli distribution。或者干脆把 Naive Bayes 换成 Decission Forest 看看 accuracy 会有怎样的变化。
在我自己的实验过程中,数据处理后的 Gaussian distribution 与 Bernoulli distribution 的 accuracy 没有太大差别。且以上的数据处理方式对 Bernoulli distribution 的 accuracy 没有太大影响。
至于 Random Forest 的表现就要看具体参数的选择了,树深低、颗数少的情况下表现和 Naive Bayes 比并没有好很多。但是如果选择了合适的树深,模型表现会有极大的提高。

其实可做的改进还有很多,但是限于主题和篇幅问题,就进行到这,欢迎各位盆友讨论。

Reference

1、 手写Naive Bayes分类器
https://blog.****.net/qq_31584013/article/details/88543933
2、THE MNIST DATABASE of handwritten digits http://yann.lecun.com/exdb/mnist/
3、Simple MNIST and EMNIST data parser written in pure Python
https://pypi.org/project/python-mnist/