基于python-KNN完成MNIST手写体数字识别

       选修了模式识别这门课,完成一个简单的基于KNN的分类项目:手写体分类任务。

      首先从网站上下载数据,格式为.idx3-ubyte和.idx1-ubyte,分别对应的是图片和标签的数据格式。首先需要对其进行解码,即转化成可以进行分类的格式,换个说法就是对文件中的数据进行读取。

       读取部分按照文件中的存储格式进行读取,分别对两种文件进行解析。函数代码如下:

def decode_idx3_ubyte(idx3_ubyte_file):
    bin_data = open(idx3_ubyte_file, 'rb').read()

    # 解析文件头信息,依次为魔数、图片数量、每张图片高、每张图片宽
    offset = 0
    fmt_header = '>iiii'
    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)
    fmt_image = '>' + str(image_size) + 'B'
    images = np.empty((num_images, num_rows, num_cols))
    for i in range(num_images):
        if (i + 1) % 10000 == 0:
            print '已解析 %d' % (i + 1) + '张'
        images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))
        offset += struct.calcsize(fmt_image)
    return images


def decode_idx1_ubyte(idx1_ubyte_file):
    bin_data = open(idx1_ubyte_file, '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

       完成读取任务以后便进行分类,此处使用KNN方法,选取最近的K的点进行判定。具体原理可以baidu一下。KNN在实际使用的时候还是有很多不太好的地方,比如当训练集较大时,KNN要求每一个测试集的图片都需要与所有的样本进行比较,所以速度较慢。当然,对于MNIST60000+10000的数据量来说,我们还是可以接受的。此处不再赘述。

       分类过程包含在classify方法中。前面使用欧氏距离进行计算两个图片向量之间的距离,之后进行排序后查找最小的K个向量,统计其标签后选取最多的一项作为分类结果。最后方法返回标签,即分类结果。

def classify(inX,dataset,labels,k):
    datasetsize = dataset.shape[0]
    ###以下距离计算公式
    diffMat = np.tile(inX,(datasetsize,1))-dataset
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    ###以上是距离计算公式
    #距离从大到小排序,返回距离的序号
    sortedDistIndicies = distances.argsort()
    #字典
    classCount = {}
    #前K个距离最小的
    for i in range(k):
        #sortedDistIndicies[0]返回的是距离最小的数据样本的序号
        #labels[sortedDistIndicies[0]]距离最小的数据样本的标签
        voteIlabel = labels[sortedDistIndicies[i]]
        #以标签为key,支持该标签+1
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    #排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]
       之后在主函数中进行各种函数的调用,解码、设置存储向量、进行分类(调用classify)、统计正确率,此处不再一一赘述,代码中也有注释。读者如有不理解的地方,欢迎讨论。

附完整代码:(文件须去MNIST网站上下载)

# -*- coding:utf-8 -*-

import operator
import struct
import numpy as np

# 训练集
train_images_idx3_ubyte_file = 'train-images.idx3-ubyte'
# 训练集标签
train_labels_idx1_ubyte_file = 'train-labels.idx1-ubyte'
# 测试集
test_images_idx3_ubyte_file = 't10k-images.idx3-ubyte'
# 测试集标签
test_labels_idx1_ubyte_file = 't10k-labels.idx1-ubyte'

def decode_idx3_ubyte(idx3_ubyte_file):
    bin_data = open(idx3_ubyte_file, 'rb').read()

    # 解析文件头信息,依次为魔数、图片数量、每张图片高、每张图片宽
    offset = 0
    fmt_header = '>iiii'
    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)
    fmt_image = '>' + str(image_size) + 'B'
    images = np.empty((num_images, num_rows, num_cols))
    for i in range(num_images):
        if (i + 1) % 10000 == 0:
            print '已解析 %d' % (i + 1) + '张'
        images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))
        offset += struct.calcsize(fmt_image)
    return images


def decode_idx1_ubyte(idx1_ubyte_file):
    bin_data = open(idx1_ubyte_file, '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 classify(inX,dataset,labels,k):
    datasetsize = dataset.shape[0]
    ###以下距离计算公式
    diffMat = np.tile(inX,(datasetsize,1))-dataset
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    ###以上是距离计算公式
    #距离从大到小排序,返回距离的序号
    sortedDistIndicies = distances.argsort()
    #字典
    classCount = {}
    #前K个距离最小的
    for i in range(k):
        #sortedDistIndicies[0]返回的是距离最小的数据样本的序号
        #labels[sortedDistIndicies[0]]距离最小的数据样本的标签
        voteIlabel = labels[sortedDistIndicies[i]]
        #以标签为key,支持该标签+1
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    #排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]


if __name__ == '__main__':
    train_images = decode_idx3_ubyte(train_images_idx3_ubyte_file)
    train_labels = decode_idx1_ubyte(train_labels_idx1_ubyte_file)
    test_images = decode_idx3_ubyte(test_images_idx3_ubyte_file)
    test_labels = decode_idx1_ubyte(test_labels_idx1_ubyte_file)

    m = 60000  # 创建一个读入数据的数组,进行图片信息的记录
    trainingMat = np.zeros((m, 784))  # 置为零

    # 文件名下划线_左边的数字是标签
    for i in range(m):
        for j in range(28):
            for k in range(28):
                trainingMat[i, 28*j+k] = train_images[i][j][k]
    errorCount = 0.0
    mTest = 10000

    for i in range(mTest):
        classNumStr = test_labels[i]
        vectorUnderTest = np.zeros(784)
        for j in range(28):
            for k in range(28):
                vectorUnderTest[28*j+k] = test_images[i][j][k]  #第i幅测试图

        Result = classify(vectorUnderTest, trainingMat, train_labels, 3)
        print("识别结果:%d 正确答案:%d" % (Result, classNumStr))
        if (Result != classNumStr):
            errorCount += 1.0
            print("错误")
    print("\n错误数: %d" % errorCount)
    print("\n错误率: %f" % (errorCount / float(mTest)))
    print '数据处理结束'

  • 9
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值