基于KNN算法的手写数字识别系统 非常详细!!!!

版权声明:未经原作者允许不得转载本文内容,否则将视为侵权 https://blog.csdn.net/springhammer/article/details/89511047

可爱聪明善良的你关注有惊喜哦

在这里插入图片描述

实验知识点:

1、K 近邻分类算法
2、从文本文件中解析和导入数据
3、使用 Matplotlib 创建扩散图
4、归一化数值

首先介绍下监督学习和无监督学习:

监督学习:(数据有输入有输出),通过已有的一部分数据和输出数据之间的相应关系,生成一个函数,将输入映射到合适的输出,在准确的说有监督学习就是训练样本中必须有标签或者标记;
无监督学习:(数据只有输入),给定的训练样本中难以标注类别或者标注成本过高,这类没有类别标注(未被标记)的训练集建模模式称为无监督学习
半监督学习:一部分数据有标签一部分数据没标签称为半监督学习

监督学习常用算法:

分为 分类(classification)和回归(regression)俩大类
分类:
K近邻、支持向量机、朴素贝叶斯、决策树、随机森林、人工神经网络等;
回归:
线性回归、神经网络等

无监督学习常用算法:

无监督学习算法主要为 聚类:
K-Means聚类,高斯混合模型等;
监督学习—KNN(k最近邻分类算法)

KNN算法思想:

K最近邻算法,顾名思义就是搜寻最近的K个已知类别样本用于未知类别样本的预测。
"最近"的度量就是应用点之间的距离或相似性,距离越小或相似度越高,说明他们之间越近
“预测”,对于离散型的因变量来说,从k个最近的已知类别样本中挑选出频率最好的类别用
于未知样本的判断;对于连续性的因变量来说,则是将K个最近的已知样本均值用作未知样
本的预测。

最佳K值的选择:

为了在模型中防止出现过拟合或欠拟合状态,也为了获得最佳的k值,可以考虑俩种解决方
案:一种是设置k近邻样本投票权重,假设读者在使用KNN算法进行分类或预测时设置的
k值比较大,担心模型发生欠拟合的现象,一个简单有效的方法就是设置近邻样本的投票权
重,如果一直样本距离未知样本比较远,则对应的权重就设置的低一些,否则权重高一些
通常可以将权重设置为距离的倒数,另一种是采用多重交叉验证,最后选出平均误差最小的
k值,当然还可以将俩种方法的有点相结合,选出理想的k值。

K近邻算法概述:

简单地说,K 近邻算法采用测量不同特征值之间的距离方法进行分类。它具有的优缺点如下:
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
K 近邻算法适用数据范围为:数值型和标称型。
K 近邻算法的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。

输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前 kk 个最相似的数据,这就是 K 近邻算法中 kk 的出处,通常 kk 是不大于 20 的整数。最后,选择 kk 个最相似数据中出现次数最多的分类,作为新数据的分类。

K近邻算法的一般流程:

1、收集数据:可以使用任何方法。
2、准备数据:距离计算所需要的数值,最好是结构化的数据格式。
3、分析数据:可以使用任何方法。
4、训练算法:此步骤不适用于 K 近邻算法。
5、测试算法:计算错误率。
6、使用算法:首先需要输入样本数据和结构化的输出结果,然后运行K 近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

开发准备:

为了简单起见,这里构造的系统只能识别数字 0 到 9,参见图 2-6。需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小 1:宽高是 32 像素 x 32 像素的黑白图像。

尽管采用文本格式存储图像不能有效地利用内存空间,但是为了方便理解,还是将图像转换为文本格式。

在这里插入图片描述

实验步骤:

准备数据:将图像转换为测试向量

为了使用前面两个例子的分类器,我们必须将图像格式化处理为一个向量。我们将把一个 32x32 的二进制图像矩阵转换为 1x1024 的向量,这样前两节使用的分类器就可以处理数字图像信息了。

首先编写一段函数 img2vector,将图像转换为向量:该函数创建 1x1024 的 NumPy 数组,然后打开给定的文件,循环读出文件的前 32 行,并将每行的头 32 个字符值存储在 NumPy 数组中,最后返回数组。
代码

def img2vector(filename):
    # 创建向量
    returnVect = np.zeros((1, 1024))
    # 打开数据文件,读取每行内容
    fr = open(filename)
    for i in range(32):
        # 读取每一行
        lineStr = fr.readline()
        # 将每行前 32 字符转成 int 存入向量
        for j in range(32):
            returnVect[0, 32*i+j] = int(lineStr[j])

return returnVect

分析数据:

实现这个算法的核心部分:计算「距离」。
当有一定的样本数据和这些数据所属的分类后,输入一个测试数据,就可以根据算法得出该测试数据属于哪个类别,此处的类别为 0-9 十个数字,就是十个类别。

算法实现过程:

1、计算已知类别数据集中的点与当前点之间的距离;
2、按照距离递增次序排序;
3、选取与当前点距离最小的 k 个点;
4、确定前 k 个点所在类别的出现频率;
5、返回前 k 个点出现频率最高的类别作为当前点的预测分类。

实现代码:

import operator
def classify0(inX, dataSet, labels, k):

    """
    参数: 
    - inX: 用于分类的输入向量
    - dataSet: 输入的训练样本集
    - labels: 样本数据的类标签向量
    - k: 用于选择最近邻居的数目
    """

    # 获取样本数据数量
    dataSetSize = dataSet.shape[0]

    # 矩阵运算,计算测试数据与每个样本数据对应数据项的差值
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet

    # sqDistances 上一步骤结果平方和
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)

    # 取平方根,得到距离向量
    distances = sqDistances**0.5

    # 按照距离从低到高排序
    sortedDistIndicies = distances.argsort()
    classCount = {}

    # 依次取出最近的样本数据
    for i in range(k):
        # 记录该样本数据所属的类别
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1

    # 对类别出现的频次进行排序,从高到低
    sortedClassCount = sorted(
        classCount.items(), key=operator.itemgetter(1), reverse=True)

    # 返回出现频次最高的类别
return sortedClassCount[0][0]

在这里插入图片描述
计算完所有点之间的距离后,可以对数据按照从小到大的次序排序。然后,确定前 kk 个距离最小元素所在的主要分类,输入 kk 总是正整数;最后,将 classCount 字典分解为元组列表,然后使用程序第二行导入运算符模块的 itemgetter 方法,按照第二个元素的次序对元组进行排序。

此处的排序为逆序,即按照从最大到最小次序排序,最后返回发生频率最高的元素标签。

测试算法:使用 K 近邻算法识别手写数字

已经将数据处理成分类器可以识别的格式。接下来,将这些数据输入到分类器,检测分类器的执行效果。在写入这些代码之前,必须确保将 from os import listdir 写入文件的起始部分,这段代码的主要功能是从 os 模块中导入函数 listdir,它可以列出给定目录的文件名。

测试的步骤:

1、读取训练数据到向量(手写图片数据),从数据文件名中提取类别标签列表(每个向量对应的真实的数字)
2、读取测试数据到向量,从数据文件名中提取类别标签
3、执行K 近邻算法对测试数据进行测试,得到分类结果
4、与实际的类别标签进行对比,记录分类错误率
5、打印每个数据文件的分类数据及错误率作为最终的结果

测试代码:

from os import listdir
def handwritingClassTest():
    # 样本数据的类标签列表
    hwLabels = []

    # 样本数据文件列表
    trainingFileList = listdir('digits/trainingDigits')
    m = len(trainingFileList)

    # 初始化样本数据矩阵(M*1024)
    trainingMat = np.zeros((m, 1024))

    # 依次读取所有样本数据到数据矩阵
    for i in range(m):
        # 提取文件名中的数字
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        hwLabels.append(classNumStr)

        # 将样本数据存入矩阵
        trainingMat[i, :] = img2vector(
            'digits/trainingDigits/%s' % fileNameStr)

    # 循环读取测试数据
    testFileList = listdir('digits/testDigits')

    # 初始化错误率
    errorCount = 0.0
    mTest = len(testFileList)

    # 循环测试每个测试数据文件
    for i in range(mTest):
        # 提取文件名中的数字
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])

        # 提取数据向量
        vectorUnderTest = img2vector('digits/testDigits/%s' % fileNameStr)

        # 对数据文件进行分类
        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)

        # 打印 K 近邻算法分类结果和真实的分类
        print("测试样本 %d, 分类器预测: %d, 真实类别: %d" %
              (i+1, classifierResult, classNumStr))

        # 判断K 近邻算法结果是否准确
        if (classifierResult != classNumStr):
            errorCount += 1.0

    # 打印错误率
    print("\n错误分类计数: %d" % errorCount)
    print("\n错误分类比例: %f" % (errorCount/float(mTest)))

上面的代码中,将 trainingDigits 目录中的文件内容存储在列表中,然后可以得到目录中有多少文件,并将其存储在变量 m 中。接着,代码创建一个 m 行 1024 列的训练矩阵,该矩阵的每行数据存储一个图像。

可以从文件名中解析出分类数字。该目录下的文件按照规则命名,如文件 9_45.txt 的分类是 9,它是数字 9 的第 45 个实例。然后可以将类代码存储在 hwLabels 向量中,使用前面讨论的 img2vector 函数载入图像。

在下一步中,我们对 testDigits 目录中的文件执行相似的操作,不同之处是并不将这个目录下的文件载入矩阵中,而是使用 classify0() 函数测试该目录下的每个文件。
最后,输入 handwritingClassTest(),测试该函数的输出结果。
K 近邻算法识别手写数字数据集,错误率为 1.05%。改变变量 k 的值、修改函数 handwritingClassTest 随机选取训练样本、改变训练样本的数目,都会对 K 近邻算法的错误率产生影响。

测试结果:
在这里插入图片描述

【实验体会】

K 近邻算法是分类数据最简单有效的算法。K 近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据。K 近邻算法必须保存全部数据集,如果训练数据集很大,必须使用大量的存储空间。此外,由于必须对数据集中的每个数据计算距离值实际使用是可能非常耗时。是否存在一种算法减少存储空间和计算时间的开销呢?K 决策树就是 K 近邻算法的优化版,可以节省大量的计算开销。

实验数据关注上面公众号私聊小编!

猜你喜欢

转载自blog.csdn.net/springhammer/article/details/89511047