樸素貝葉斯算法對MNIST數據集分類

1.樸素貝葉斯算法介紹

算法本質的是貝葉斯公式 p(xy)=p(yx)p(x)p(y)p(x \mid y)=\dfrac{p(y \mid x)p(x)}{p(y)},計算在已知數據的條件下,求各個分類的後驗概率,數據的分類結果是概率最大的那個分類。

樸素一詞的來源是假設各特徵之間相互獨立。因此
p(xc1)=p(x1,x2...xnc1)=p(x1c1)p(x2c1)...p(x3c1)p(\vec x\mid c_1) = p(x_1,x_2...x_n|c1) = p(x_1|c1)*p(x_2|c1)...p(x_3|c1)

2.MNIST數據集介紹

數據集地址:http://yann.lecun.com/exdb/mnist/

2.1基本介紹

MNIST數據集是一個手寫數字0~9的圖片數據集,訓練集60000,測試集10000,每張圖28*28.

2.2如何用Python讀取

開始下載了數據集,解壓後本以爲是圖片,結果是二進制文件,然後就不知道怎麼讀取了。查閱之後發現文件的格式是idx3-utype和idx1-utype,在數據集的網站上有介紹,如下圖:
在這裏插入圖片描述
如何讀取這種文件呢?要先讀數據頭,以idx3爲例,前8個數據是數據頭,第一個數是magic number(魔數),暫且不管;第二個數是圖片的數量(60000);第三和四個是圖片的行列像素值(28*28).然後讀取之後的數據,之後的都是像素值(8byte)。
具體程序如下:

def deconde_idx1_ubyte(filename):
    # 讀取二進制數據
    bin_data = open(filename, 'rb').read()

    # 解析文件頭信息,依次爲魔數和標籤數
    offset = 0
    fmt_header = '>ii'
    magic_number, num_images = struct.unpack_from(fmt_header, bin_data, offset)
#     print('魔數:%d, 圖片數量: %d張' % (magic_number, num_images))

    # 解析數據集
    offset += struct.calcsize(fmt_header)
    fmt_image = '>B'
    labels = np.empty(num_images)
    for i in range(num_images):
#         if (i + 1) % 10000 == 0:
#             print ('已解析 %d' % (i + 1) + '張')
        labels[i] = struct.unpack_from(fmt_image, bin_data, offset)[0]
        offset += struct.calcsize(fmt_image)
    return labels
def decode_idx3_ubyte(filename):
    bin_data = open(filename, 'rb').read()

    # 解析文件頭信息,依次爲魔數、圖片數量、每張圖片高、每張圖片寬
    offset = 0
    fmt_header = '>iiii' #因爲數據結構中前4行的數據類型都是32位整型,所以採用i格式,但我們需要讀取前4行數據,所以需要4個i。我們後面會看到標籤集中,只使用2個ii。
    magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, offset)
#     print('魔數:%d, 圖片數量: %d張, 圖片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))

    # 解析數據集
    image_size = num_rows * num_cols
    offset += struct.calcsize(fmt_header)  #獲得數據在緩存中的指針位置,從前面介紹的數據結構可以看出,讀取了前4行之後,指針位置(即偏移位置offset)指向0016。
#     print(offset)
    fmt_image = '>' + str(image_size) + 'B'  #圖像數據像素值的類型爲unsigned char型,對應的format格式爲B。這裏還有加上圖像大小784,是爲了讀取784個B格式數據,如果沒有則只會讀取一個值(即一副圖像中的一個像素值)
#     print(fmt_image,offset,struct.calcsize(fmt_image))
    images = np.empty((num_images, num_rows, num_cols))
    for i in range(num_images):
#         if (i + 1) % 10000 == 0:
#             print('已解析 %d' % (i + 1) + '張')
#             print(offset)
        images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))
        offset += struct.calcsize(fmt_image)
#     images = np.reshape(images, (num_images, image_size))
    return images
    

3.基本步驟

  1. 讀取數據集
  2. 訓練模型
    2.1. 求先驗概率,p(c1), p(c2)…
    2.2. 求條件概率, p(x|c1),p(x|c2)…
  3. 對測試集進行預測,求準確率

4.程序

from PIL import Image
import numpy as np
import struct
import math
import matplotlib.pyplot as plt
from collections import Counter
#將28*28的圖片變化成5*5的圖片
def imageResize(images):
    #images.shape = (60000, 28, 28)
    nx, ny, nz = images.shape
    dataSetResize = np.empty((nx, 25))
    for i in range(nx):
        tempdata = np.array(Image.fromarray(images[i]).resize((5, 5)))
        dataSetResize[i] = np.resize(tempdata, 25)
    return dataSetResize
#二值化
def Binarization(images):
    for i in range(images.shape[0]):
        imageMean = images[i].mean()
        images[i] = np.array([0 if x < imageMean else 1 for x in images[i]])
    return images
#模型訓練
def Bayes_train(train_x, train_y):    
    #先驗概率P(0), P(1)....
    totalNum = train_x.shape[0]
    classNum = Counter(train_y)
    prioriP = np.array([classNum[i]/totalNum for i in range(10)])
    
    #後驗概率
    posteriorNum = np.empty((10, train_x.shape[1]))
    posteriorP = np.empty((10, train_x.shape[1]))
    for i in range(10):
        posteriorNum[i] = train_x[np.where(train_y == i)].sum(axis = 0)  
        #拉普拉斯平滑      
        posteriorP[i] = (posteriorNum[i] + 1) / (classNum[i] + 2)   
    return prioriP, posteriorP
#模型預測
def Bayes_pret(test_x, test_y, prioriP, posteriorP):
    pret = np.empty(test_x.shape[0])
    for i in range(test_x.shape[0]):
        prob = np.empty(10)
        for j in range(10):
            temp = sum([math.log(1-posteriorP[j][x]) if test_x[i][x] == 0 else math.log(posteriorP[j][x]) for x in range(test_x.shape[1])])
            prob[j] = np.array(math.log(prioriP[j]) + temp)
        pret[i] = np.argmax(prob)
    return pret, (pret == test_y).sum()/ test_y.shape[0]
def main():
    train_x_data = decode_idx3_ubyte('./data/train_images')
    # train_x = imageResize(train_x)
    train_y = deconde_idx1_ubyte('./data/train_labels')
    train_x = np.resize(train_x_data, (train_x_data.shape[0], train_x_data.shape[1]*train_x_data.shape[2]))
    train_x = Binarization(train_x)
    
    test_x_data = decode_idx3_ubyte('./data/test_images')
    # test_x = imageResize(test_x)
    test_y = deconde_idx1_ubyte('./data/test_labels')
    test_x = np.resize(test_x_data, (test_x_data.shape[0], test_x_data.shape[1]*test_x_data.shape[2]))
    test_x = Binarization(test_x)
    
    prioriP, posteriorP = Bayes_train(train_x, train_y)
    accuracy = Bayes_pret(test_x, test_y, prioriP, posteriorP)
    
    print(accuracy)

if __name__ == "__main__":
    main()

準確率:

#5*5
0.5856

#28*28
0.8474

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章