#基于朴素贝叶斯分类实现对手写数字体的识别
朴素贝叶斯分类就是基于先验概率,类条件概率以及后验概率的分类,后验概率可以由先验概率与类条件概率来求得。下图是书本上一段关于朴素贝叶斯方法的介绍:
由于在对每一类的样本进行识别时,一般都会使每个类型的样本数都保持一致,所以一般来说先验概率可以取值0.1。对于类概率密度的求法则相对复杂,如上图所示,wi代表每个不同的类型,也就是0-9这十个类型,X则表示每一个样本的特征向量,可以理解为X这个向量就可以将数字图片表示出来,然后对于一类数字的类条件概率则是通过对同一类中每一个样本进行类条件概率的求取,然后再计算出总的和。最后通过全概率公式计算出后验概率。后验概率最大值所对应的wi就可以当作最后识别的结果。
##图片二值化
图片二值化是第一步,我们写的就是黑白的数字所以转化也相对简单,最后需注意二值化时转化的数组不要太大,太大的数组会导致后面求特征向量时,出现概率都过小的情况,我取的是8*8,下面是二值化的代码片:
#主要功能:完成手写数字图片到数组的转化
from PIL import Image
import matplotlib.pylab as plt
import numpy as np
import os
#将手写图片转化为8*8的矩阵形式
#可以在识别训练集以及识别测试集时调用
#在对训练集进行转化时可以考虑运用数组循环来实现整个数据集的转化
def pic(imgfilename):
img=Image.open(imgfilename).convert('RGBA') #打开图片并且以‘RGBA’的形式对图片进行转换
raw_data=img.load() #得到图像的像素值
for y in range(img.size[1]): #将其降噪并转化为黑白两色
for x in range(img.size[0]):
if raw_data[x, y][0] < 90:
raw_data[x, y] = (0, 0, 0, 255)
if raw_data[x, y][1] < 136:
raw_data[x, y] = (0, 0, 0, 255)
if raw_data[x, y][2] > 0:
raw_data[x, y] = (255, 255, 255, 255)
img=img.resize((8,8),Image.LANCZOS) #将图片设置为8*8的大小
# img.save('datset/0.png')
array=plt.array(img) #将图片转化为数组形式
gray_array=np.zeros((8,8)) #将数组形式转化为8*8的矩阵形式
# # plt.imshow(img)
# # plt.show()
# # print(img.getpixel(0, 0))
for x in range(array.shape[0]):
for y in range(array.shape[1]):
gary = 0.299 * array[x][y][0] + 0.587 * array[x][y][1] + 0.114 * array[x][y][2]#根据RGB比例计算图片的灰度值
if gary ==255:
gray_array[x][y]=0
else:
gray_array[x][y]=1
name01=imgfilename.split('.')[0]
name01=name01+'.txt'
np.savetxt(name01,gray_array,fmt='%d',delimiter='')#将数组保存到文本文件
if __name__=='__main__':
mainpath=('C:/Users/Administrator/Desktop/手写数字识别—终极版/test_dataset/')
trainpath=os.listdir(mainpath)
for ll in range(0,370):
print(trainpath[ll])
pic(mainpath+trainpath[ll])
以上代码参考链接:
原文链接:https://blog.csdn.net/Leafage_M/article/details/78958101
##类条件概率的求取
我选择的训练集一共有370个数字,0-9分别有35-40个,所以对于每个类型可以得到35-40个88维的数组矩阵,这里我们可以通过flatten函数实现对多维数组的降维处理,将其转化为164的数组,如此一来,每个类型的特征就可以由一个n64的数组来表示。然后就是对这些特征的处理,因为对于每行特征向量,其中的元素就是他的特征值,所以一个图片对应有64个特征值,现在对这个n64的数组进行处理。我们可以找到每列的1的个数,然后除以n,就可以得到每个特征值为1的概率p,因此每个特征值为0的概率也出来了(1-p),以每列1的概率建立一个新的164的数组。对于测试数据集同样化成164的数组然后与前面得到的概率数组进行比较,比较方式可以由下面代码片表示:
if testnum[one]==1 and gailvzhi[one]>0:
finalgailvarr[one]=gailvzhi[one]
testgailv*=finalgailvarr[one]
if testnum[one]==0 and gailvzhi[one]<1:
# finalgailvarr[one]=0
finalgailvarr[one] = gailvzhi[one]
testgailv*=(1-finalgailvarr[one])
解释:对测试集得到的特征数组的元素进行遍历,当其值为1时,表明该值可以取到的概率为p,当值为0时对应概率为(1-p),这里的概率p就是前面训练集所得到的01特征的概率,然后将测试集特征数组元素对应的概率全部相乘,此过程需要对每一个类别都进行一次,然后比较10个类别间的概率大小,将概率最大的对应的类别作为该测试数据的类别。(如果出现概率过小无法判别的情况,则可以对每个类别所得到的概率乘以一个相同的实数)
下面是识别部分的代码:
import numpy as np
from decimal import Decimal
import os
i=0
bb=[None]*370
te=[None]*100
p=[None]*10
currectcount=0
#C:\Users\Administrator\Desktop\手写数字识别—改\手写数字识别—改
mainpath=('C:/Users/Administrator/Desktop/手写数字识别—终极版/train_dataset_txt/')
trainpath=os.listdir(mainpath)
testmainpath=('C:/Users/Administrator/Desktop/手写数字识别—终极版/test_dataset_txt/')
testpath=os.listdir(testmainpath)
for jktest in range(0,10):#测试集为100个,每个类型十个样本,这里是测试集10种类型的循环
for j in range(jktest*10,jktest*10+10):#这里是每个类型10种样本的循环
n = []
testdata = open(testmainpath+testpath[j], 'r')
for test in testdata:
k = test.strip('\n')#去除数组中的'\n'
y = list(map(int, k))
n.append(y)
# print(n)
te[0] = n
# print(te[0])
testcc = np.array(te[0])
# print(testcc)
testf = testcc.flatten()#降维处理
# print(testf)
psum=0
for jk in range(0,10):#训练集10个类型的循环
# print(testf)
finalgailvarr=[0]*64
numgailv = [0] * 64
testgailv = 1
thislabel=jk
traincount=0
for i in range(jk*37, jk*37+37):#训练集37个样本的循环
thisfile=open(mainpath+trainpath[i],'r')
# print(mainpath+trainpath[i])
m = []
for train in thisfile:
w=train.strip('\n')
x=list(map(int,w))
m.append(x)
bb[i]=m
cc=np.array(bb[i])
f=cc.flatten()
testnum=[]
testnum=testf
num=[]
num=f
for zhi in range(0,64):#对同一类型的10*64的数组中每列的1的概率的求解
if num[zhi] == 1:
numgailv[zhi]+=1/37
gailvzhi=[]
gailvzhi=numgailv
for one in range(0,64):#1*64的数组中出现1的概率与出现0的概率的乘积,最后得到类条件概率
if testnum[one]==1 and gailvzhi[one]>0:
finalgailvarr[one]=gailvzhi[one]
testgailv*=finalgailvarr[one]
if testnum[one]==0 and gailvzhi[one]<1:
# finalgailvarr[one]=0
finalgailvarr[one] = gailvzhi[one]
testgailv*=(1-finalgailvarr[one])
# if j==0 :
# print(testgailv)
testgailv=Decimal(testgailv)
psum+=testgailv
p[jk]=testgailv
print("待识别的数为:"+str(jktest)+ "识别结果:"+str(np.argmax(p)))#argmax函数返回最大值的索引
if str(jktest)==str(np.argmax(p)):
currectcount+=1
currectrate=currectcount/100
print("正确率为:"+str(currectrate))
##结果
待识别的数为:6识别结果:6
待识别的数为:6识别结果:6
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:7
待识别的数为:7识别结果:6
待识别的数为:8识别结果:8
待识别的数为:8识别结果:1
待识别的数为:8识别结果:8
待识别的数为:8识别结果:8
待识别的数为:8识别结果:9
待识别的数为:8识别结果:8
待识别的数为:8识别结果:8
待识别的数为:8识别结果:9
待识别的数为:8识别结果:2
待识别的数为:8识别结果:8
待识别的数为:9识别结果:1
待识别的数为:9识别结果:1
待识别的数为:9识别结果:9
待识别的数为:9识别结果:9
待识别的数为:9识别结果:9
待识别的数为:9识别结果:7
待识别的数为:9识别结果:9
待识别的数为:9识别结果:9
待识别的数为:9识别结果:7
待识别的数为:9识别结果:2
正确率为:0.89
这里截取部分