用朴素贝叶斯法对MNIST数据集分类

朴素贝叶斯法

用于MNIST分类任务

准确率可达到84.15%

数学原理参考:《统计学习方法》by 李航


Load MNIST Database

通过Keras框架能够比较方便的获得已经打乱顺序的MNIST训练集和测试集。

1
2
3
from keras.datasets import mnist

(X_train, y_train), (X_test, y_test) = mnist.load_data()

获取的数据集构成:

Training set Test set
60000 examples 10000 examples

Visualize the First Six Training Images

看看MNIST数据集的图片是什么样的,查看前6张图片。

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
%matplotlib inline # add this because i use jupyter notebook
import matplotlib.cm as cm
import numpy as np

# plot first six training images
fig = plt.figure(figsize=(20,20))
for i in range(6):
ax = fig.add_subplot(1, 6, i+1, xticks=[], yticks=[])
ax.imshow(X_train[i], cmap='gray')
ax.set_title(str(y_train[i]))

显示如下,可以看出,是灰度值图片:

Binarization

为了方便处理,将灰度图图片二值化。这里先将非0的像素值全置1。

1
X_train = (X_train>0).astype('uint8')

对比一下二值化前后的图片:

产生上面两幅图的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def visualize_input(img, ax):
ax.imshow(img, cmap='gray')
width, height = img.shape
thresh = img.max()/2.5
for x in range(width):
for y in range(height):
ax.annotate(str(round(img[x][y],2)), xy=(y,x),
horizontalalignment='center',
verticalalignment='center',
color='white' if img[x][y]<thresh else 'black')

fig = plt.figure(figsize = (12,12))
ax = fig.add_subplot(111)
visualize_input(X_train[46], ax)

Train

老办法,先对训练集数据进行reshape操作,将一幅图展开为行向量。整个训练集(60000张图片)就变成了一个大小为60000×784的数组,之后尽量进行矩阵操作。

1
X_train = np.reshape(X_train, (X_train.shape[0], -1))

带拉普拉斯平滑的训练函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def train(train_data,train_label): #with Laplace smoothing
image_num,dim_num = train_data.shape
label_num = 10

prior_sum = np.zeros(label_num)# prior probability P(Y = C_k)
conditional_sum = np.zeros((label_num,dim_num))# condition probability

for i in range(image_num):
label = train_label[i]
prior_sum[label] += 1
for j in range(dim_num):
conditional_sum[label][j] += train_data[i][j]

prior_prob = (prior_sum + 1) / (image_num + label_num)
conditional_prob = (conditional_sum.T + 1) / (prior_sum + 2)

print("prior_prob shape:",prior_prob.shape)
print("conditional_prob shape:",conditional_prob.shape)

return prior_prob, conditional_prob

注意:函数输出的conditional_prob是将对应位置像素值判断为1的概率矩阵,若想判断0概率,用1和它做差就可以。

Test

单张图片的预测:

1
2
3
4
5
6
7
8
9
10
11
12
13
def test_one_image(test_image, prior_prob_log, 
conditional_one_prob_log, conditional_zero_prob_log):
#the test_image should be pre-treatment
#test_image = (test_image > 0).astype('uint8')

predict_prob = np.zeros(10)

for i in range(10):
for j in range(784):
predict_prob[i] += conditional_one_prob_log[j][i] \
if test_image[j]>0 else conditional_zero_prob_log[j][i]

return np.argmax(predict_prob)

预测所有测试集图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def test_all_images(test_data, prior_prob_log, 
conditional_one_prob_log, conditional_zero_prob_log):

test_data = (test_data > 0).astype('uint8')
test_data = np.reshape(test_data, (test_data.shape[0], -1))
print("test_data shape:",test_data.shape)

predict = np.zeros(test_data.shape[0])

for i in range(test_data.shape[0]):
predict[i] = test_one_image(test_data[i],
prior_prob_log,
conditional_one_prob_log,
conditional_zero_prob_log)

return predict

计算准确率:

1
2
3
4
def print_accuracy(predict_label, real_label):
accuracy = (predict_label == real_label).sum() / len(real_label)
print("test accuracy is %.2f%%" % (accuracy*100))
return accuracy

这里将乘法用log转为加法(这个例子中均为单调函数,不影响结果),提高运算速度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time

train_start_time = time.time()
prior_prob, conditional_prob = train(X_train, y_train)
train_end_time = time.time()
print("train time:%.2fs"%(train_end_time-train_start_time))

prior_prob_log = np.log(prior_prob)
conditional_one_prob_log = np.log(conditional_prob)
conditional_zero_prob_log = np.log(1 - conditional_prob)

test_start_time = time.time()
predict_label = test_all_images(X_test,
prior_prob_log,
conditional_one_prob_log,
conditional_zero_prob_log)
test_end_time = time.time()
print("test time:%.2fs"%(test_end_time-test_start_time))

print_accuracy(predict_label, y_test)

测试的输出:

1
2
3
4
5
6
prior_prob shape: (10,)
conditional_prob shape: (784, 10)
train time:29.16s
test_data shape: (10000, 784)
test time:134.71s
test accuracy is 84.15%

Conclusion

  1. 本次实验将图像展开、对单个像素独立判断,损失了图像的空间信息,而这种空间信息正是我们人眼识别图像的关键。所以最终的准确率不会太高。
  2. 测试数据的准确率为84.15%,比我想象中的要好很多(共有10个分类,随机猜测的准确率只有10%)。我认为,能使准确率达到84.15%的原因主要是:(1) MNIST数据集被预先处理过。通过上面的6张示例图片可以看出,MNIST中的图片较为纯净,没有噪声干扰,非常清晰。所以实验中可以直接进行二值化。(2) MNIST数据集中,图片中的数字总是在中心位置,大小合适,比较饱满。这一点保留了部分的空间信息。
  3. 本例中的二值化过程是直接将非0值置1,如果采用更合理的阈值(比如取当前图片最大像素值的一半)进行二值化操作,进行训练、测试过程,最终的准确率可能会提升。

----------over----------


文章标题:用朴素贝叶斯法对MNIST数据集分类

文章作者:Ge垚

发布时间:2018年05月04日 - 13:05

最后更新:2018年05月23日 - 13:05

原始链接:http://geyao1995.com/用朴素贝叶斯法对MNIST数据集分类/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。