链接: 数据集下载地址.
链接中的数据集共有四个,
train-images-idx3-ubyte:存放的是训练用的图片信息
t10k-images-idx3-ubyte:存放的是测试用的图片信息
train-labels-idx1-ubyte:训练用的图片的实际数字
t10k-labels-idx1-ubyte:测试用的实际数字
第一步,数据集的读取
先读取说明性信息,再读取真正的数据集内容
对于imgs,最终读取为60000 X 28 X 28的矩阵
def loadImageSet(which=0):
print('load image set')
binfile=None
if which==0:
binfile = open(r"train-images-idx3-ubyte", 'rb')
else:
binfile= open(r"t10k-images-idx3-ubyte", 'rb')
head = struct.unpack_from('>IIII' , buffers ,0) #打包四个,是说明信息
print("head,",head)
offset=struct.calcsize('>IIII') #偏移量
imgNum=head[1] #共有多少个
width=head[2] #图片长和宽
height=head[3]
#[60000]*28*28
bits=imgNum*width*height #剩下的数据总长度
bitsString='>'+str(bits)+'B' #like '>47040000B'
imgs=struct.unpack_from(bitsString,buffers,offset) #读取剩下的信息
binfile.close()
imgs=np.reshape(imgs,[imgNum,width,height]) #变成矩阵形式
print('load imgs finished')
return imgs
读取label数据集,也是先读取说明性信息
def loadLabelSet(which=0):
print ('load label set')
binfile=None
if which==0:
binfile=open(r"train-labels-idx1-ubyte", 'rb')
else:
binfile=open(r"t10k-labels-idx1-ubyte", 'rb')
buffers = binfile.read()
head = struct.unpack_from('>II' , buffers ,0) #大端的字节顺序打包两个4字节无符号整型数据
print("head,",head)
imgNum=head[1]
offset = struct.calcsize('>II') #偏移量
numString='>'+str(imgNum)+"B"
labels= struct.unpack_from(numString , buffers , offset)
binfile.close()
labels=np.reshape(labels,[imgNum,1])
print ('load label finished')
return labels
第二部,训练模型
首先,我们将图片分为7 X 7个4 X 4的图片,实现降维
然后统计4 X 4的图片中,像素值大于0的点数,如果大于一半(8个点),就认为其特征值为1,否则为0
这样,对于每一个样本,我们得到了7*7个特征值
对7*7个特征值进行编号,0,1,2,…,48
首先我们要获得先验概率P(wi):
以及条件概率:
可以看出,在求每个先验概率与条件概率时 i 是不变的,也就是说我们在进行计算时,要从6w个样本中提取出属于 i 的样本
假设我们已经提取出了属于数字0的所有样本,假设共有6000个,即我们得到了6000 X 28 X 28 的图片,按照上述说的,要进行降维。
也就是我们会有 7 X 7个 6000 X 4 X4个图片,然后我们将 6000 X 4 X4降维成6000个0/1数据的一维数组;也就是49个一维数组
按照条件概率公式,我们最终要将6000个求和的,也就是最后变成了一个49个元素的一维数组
实现起来非常简单:
P=np.zeros([10,49]) #条件概率
Pw=np.zeros(10) #先验概率
def train(label):
Pw[label]=np.argwhere(labels == label)[:,0].shape[0]/labels.shape[0] #统计P(wi)
for i in [0,4,8,12,16,20,24]:
for j in [0,4,8,12,16,20,24]:
imgs_0=imgs[np.argwhere(labels == label)[:,0],j:j+4,i:i+4];
P[label][i//4*7+j//4]=(count(imgs_0)+1)/(imgs_0.shape[0]+2) #获得Pj(wi)
def count(img): #统计累计xkj
img=img>0 #统计大于0的点,
img=img.sum(axis=2)
img=img.sum(axis=1) #两次求和得到每个面的和
tem=img>=8 #大于一半
return tem.sum()
函数count(img),作用是统计:
将 6000 X 4 X4降维成6000个0/1数据的一维数组,其实就是将平面求和,通过两次sum就可以实现
最后一次sum是将6000个0/1求和
对于一个数字 i 来说,该函数应该被调用7 X 7次(因为被分成了这么多份)
而train(label)的作用是统计数字为label的所有条件概率,顺便求一下先验概率
如上述所说,会调用7*7次count
而这个函数应该被调用10次(因为有10个数字)
因此在主函数中,会有
#计算全部的Pj(wi),得到训练结果
for label in range(0,10):
train(label)
''' 训练完成 '''
至此,训练完成,最终得到了两个概率矩阵
第三步,进行测试
首先还是要读取其数据集
#读入测试集
new_imgs=loadImageSet(1)
new_labels=loadLabelSet(1)
之后,逐个样本的去判断,并统计正确个数
#逐个进行识别
count=0
for k in range(0,new_imgs.shape[0]):
test_k=test_condition(new_imgs[k,:,:])
if test_k==new_labels[k]:
count=count+1
关键是要计算类条件概率:
因此对于我们每一张图片,还是要降维,然后判断是1还是0,是1就返回P[label][i],是0就返回1-P[label][i],
def count_test(img,label,i):
img=img>0 #传入的img是4*4的
flag=img.sum()>=8 #降维
if flag:
return P[label][i]
else:
return 1-P[label][i]
因此,对于一个测试样本,上面函数要被调用10 X 7 X 7次(共有10个数,每个数进行49次)
def test_condition(img):
condition_P=[]
after_P=[] #储存P(wi|X)
#计算条件概率
for label in range(0,10):
condition_P.append(1)
after_P.append(0)
for i in [0,4,8,12,16,20,24]:
for j in [0,4,8,12,16,20,24]:
img_=img[j:j+4,i:i+4]
condition_P[label]=condition_P[label]*count_test(img_,label,i//4*7+j//4)
#计算后验概率
for label in range(0,10):
after_P[label]=Pw[label]*condition_P[label]
return after_P.index(max(after_P)) #选取最大的对应的位置,就是判断的数字
完整主函数为:
#读取训练集
imgs=loadImageSet()
labels=loadLabelSet()
#计算全部的Pj(wi),得到训练结果
for label in range(0,10):
train(label)
''' 训练完成 '''
#读入测试集
new_imgs=loadImageSet(1)
new_labels=loadLabelSet(1)
#逐个进行识别
count=0
for k in range(0,new_imgs.shape[0]):
test_k=test_condition(new_imgs[k,:,:])
if test_k==new_labels[k]:
count=count+1
print("正确个数:",count)
print("\n正确率:",count/new_labels.shape[0])
总共代码为120行左右,正确率在0.74左右