基于Python的MNIST解析图片文件和标签文件实验报告

发布于:2022-12-17 ⋅ 阅读:(379) ⋅ 点赞:(0)

MNIST解析图片文件和标签文件实验报告

实验环境:

CPU:i5 10400F

系统:windows10

python 3.8.0

一、数据处理

编写decodeImages(),decodeLables()函数分别解析图片文件和标签文件

打开二进制文件,用到struct.unpack_from()函数获取数据

# 解析文件头信息,依次为魔数、图片数量、每张图片高、每张图片宽
    offset = 0
    fmt = '>iiii' #unpack_from()函数中读取格式,表示读取4个32bit int型
    magicNumber, numImages, numRows, numCols = struct.unpack_from(fmt, data, offset)
    print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magicNumber, numImages, numRows, numCols))

# 解析图片数据集
    imageSize = numRows * numCols
    offset += 16  #指针位置移动。
    fmt = '>' + str(imageSize) + 'B'  #读取28*28个unsigned char类型的数据,即一个图片的所有像素
    images = np.empty((numImages, numRows, numCols)) #创建数组,存放图片像素信息
    #plt.figure()
    for i in range(numImages):
        if (i + 1) % 10000 == 0:
            print('已解析 %d' % (i + 1) + '张')
        #解析第i张图片,放入数组images中
        imageData = struct.unpack_from(fmt,data,offset)
        imageData=np.array(imageData)
        images[i]=imageData.reshape((numRows,numCols))
        offset += struct.calcsize(fmt) #指针移动

二、模型描述

1.解析文件,获取训练集、测试集图片标签数据

	#获取训练集、测试集数据
    trainImages = decodeImages('train-images.idx3-ubyte')
    trainLables = decodeLables('train-labels.idx1-ubyte')
    testImages = decodeImages('t10k-images.idx3-ubyte')
    testLables = decodeLables('t10k-labels.idx1-ubyte')

2.for循环设置不同k值,将参数传入测试函数test(),获取不同k值下测试的正确率

​ 测试时为了节省时间设置了训练集、测试集用到的数据量TRAIN_NUM=1000,TEST_NUM=100

for k in range(1,13):
    print('k=',k,',开始测试')
    temp = test(trainImages[:TRAIN_NUM],trainLables[:TRAIN_NUM],testImages[:TEST_NUM],testLables[:TEST_NUM],k)
    print('测试结束')
    print('k=',k,'的正确率为:',temp)
    result.append(temp)

3.test()函数,测试函数,参数=(训练集图片,训练集标签,测试集图片,测试集标签,k值)

​ 作用是在指定k值下用knn算法判断测试集中每个图片的数字,并与测试集标签中的数据比对,计算判断的正确率并返回

​ 为提高效率,运用多进程模块,创建进程数为8的进程池,最多同时进行8个图片的计算

for i in range(num):	#num为测试图片数量
    arg = (trainImages,trainLables,testImages[i],k,i)   #创建进程
    p = pool.apply_async(knn, args=arg)
    result.append(p)
for i in range(num):
    p=result[i]
    res = p.get()       #返回结果
    print('第',i+1,'张图片预测结果为:',res,',  正确结果为:',int(testLables[i]))
    if testLables[i]==res:
        print('判断正确')
        count+=1
    else :
        print('判断错误')

return count/num

4.knn()函数,用knn算法计算待测图片数字,参数=(训练集图片,训练集标签,测试图片,k,测试图片序号)

过程:

​ 依次计算测试图片与训练集中每个点的距离,此处采用欧式距离distance1(x,y),并将距离存入列表distances;

    #计算x与trainImages中每个点的距离,放入数组
    for i in trainImages:
        temp = distance1(i,x)
        distances.append(temp)

​ 找出列表前k个最小值的索引,算法为遍历k次列表找最小值并记录索引;

    #找出前k个最小的值
    Lst = distances[:]                        
    indexs = []
    for i in range(k):
        index_i = Lst.index(min(Lst))    #得到列表的最小值,并得到该最小值的索引
        indexs.append(index_i)           #记录最小值索引
        Lst[index_i] = float('inf')      #将遍历过的列表最小值改为无穷大,下次不再选择

​ 通过索引,找出训练集标签中对应的标签存入列表lables,为每个标签计数,找出数量最多的标签返回结果

    #取前k个标签
    lables = []
    for i in range(k):
        lables.append(trainLables[indexs[i]])
    #为每个标签计数
    count = np.zeros(10)
    for i in lables:
        count[int(i)]+=1

三、实验效果

实验过程中采用TRAIN_NUM=1000, TEST_NUM=100,取部分数据进行测试,测试k的范围为1-15

1.解析文件过程如图:

在这里插入图片描述

2.多进程效果展示,CPU利用率接近100%,速度明显提升

理论上会比单进程逐个计算的时间缩短8倍

在这里插入图片描述

3.结果展示

粗测:

取训练集前1000项数据,测试集前100项数据,k取值1-15,测试完总用时为162s,用matplotlib库绘制折线图如图

此时正确率最高的点出现在k=5,6,8,10这4处,正确率为0.84

在这里插入图片描述

第二次实验:

将实验数据调至取训练集前4000项,测试集前100项,k取值1-10,结果如下:

总用时412s,正确率最大点k=8,正确率为0.96

4.结论

由于电脑性能有限,不便测量完整的数据,于是暂且得出结论:在k=8时,knn算法判断准确率最高

四、模型优化

1.可以改变点与点之间的距离公式

两点间的距离公式可采用曼哈顿距离、欧式距离、切比雪夫距离等

程序中仅需编写一个新的距离计算函数distance2,并替换knn函数中distance1的位置就可以改变距离计算公式

def knn(trainImages,trainLables,x,k,procnum):
    
    print('第',procnum+1,'张图片测试开始')
    distances = []
    #计算x与trainImages中每个点的距离,放入数组
    for i in trainImages:
        temp = distance1(i,x)
        distances.append(temp)

换成曼哈顿距离公式之后的测试结果如图,k=8时正确率最高,为0.93,总用时333s

相比欧氏距离正确率下降了

在这里插入图片描述

2.多进程提高效率

在test()函数中运用了multiprocessing模块,最高开8个进程同时计算,提高了效率。

可根据CPU性能增加或减少进程数,充分利用CPU

3.排序算法优化

在计算完待测点与训练集的距离之后,需要选出前k个距离最小的点。

此处最快的算法为依次将结果放入队列,再从头到尾遍历k次,找出最小的。复杂度为n*k

若要维持一个最小堆,在插入时需要逻辑判断,会增加耗时;若要内部排序则更增加耗时。


网站公告

今日签到

点亮在社区的每一天
去签到