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()
一张不过瘾,我们把所有类的平均图像做出来看一下:
实现
数据了解的差不多,我们可以想一下该怎么实现了。
首先,让我们先考虑一下我们的训练集是什么样的。虽然上面为了更直观的展示,我把图像做成了28 * 28的形状,但是实际上每一个图像的数据都是1 * 784的一长条。也就是说,我们的训练集应当是60000*784的一张大表。(60000个example,784个feature)
训练
搞清楚训练集的的样子和究竟什么是feature以后,我们就可以考虑一下我们训练的究竟是什么了。说白了,Naive Bayes 分类器的训练过程就是在计算label出现的频率()和 分布的参数。假定 我们还是使用Gaussian distribution,那么我们训练的过程就是(这里看不明白的盆友请参考上一篇,手写Naive Bayes分类器):
1、计算各个label(0-9)在训练集出现的频率,即:
2、给定label的情况下,计算每个feature的均值及方差。
举个栗子,假如要算label为0的情况下的均值和方差,绿色的部分就是结果。(这里只是举个例子,具体数据的存储结构可以自行组织)
预测
有了训练好的模型,我们就可以在test上预测一下试试啦。计算的过程也比较简单,其实就是找到使最大的 。
还想要计算accuracy的话,和test label对比一下就好。到此,识别手写数字问题就完完整整的解决了,仅仅想要了解如何在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()
2、将上一步的结果拉成同样的大小(20 * 20)。
img2 = cv2.resize(img1, (20,20))
plt.imshow(img2, cmap='gray')
plt.show()
最终结果证明,在用 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/