• 基于kNN的手写字体识别——《机器学习实战》笔记


    看完一节《机器学习实战》,算是踏入ML的大门了吧!这里就详细讲一下一个demo:使用kNN算法实现手写字体的简单识别

    kNN

     先简单介绍一下kNN,就是所谓的K-近邻算法:

      【作用原理】:存在一个样本数据集合、每个样本数据都存在标签。输入没有标签的新数据后,将新数据的每个特征与样本集数据的对应特征进行比较,然后算法提取样本集中最相似的分类标签。一般说来,我们只选择样本数据集中前k个最相似的数据,最后,选择这k个相似数据中出现次数最多的分类,作为新数据的分类。

      通俗的说,举例说明:有一群明确国籍的人(样本集合,比如1000个):中国人、韩国人、日本人、美国人、埃及人,现在有一个不知国籍的人,想要通过比较特征来猜测他的国籍(当然,特征具有可比较性和有效性),通过比较特征,得出特征与该人最相近的样本集中的9个人(k),其中,1个是韩国人、2个是日本人,6个是中国人,那么这个人是中国人的可能性就很大。

      这就是kNN的基本思想。

    手写体识别数据准备

      kNN输入需要特征矩阵,一般是固定大小的二值图像,这里我们使用书上提供的数据集:这个数据集使用32X32文本文件存储数值图像。例如下图的'9'

      这里每个文本文件存储一个手写体数据,并且文件名写成"number_num.txt"这样的形式,例如9_1.txt,方便后期提取标签

      我们将样本数据放在trainingDigits文件夹中,测试样例存储在testDigits文件夹中

      我们在处理时将每个手写体数据(32x32)转换成1X1024维的向量。

      另外,kNN涉及到相似度计算。这里我们使用的是欧氏距离,由于手写体数据向量是规则的二值数据,因此不需要进行归一化。

    手写体识别算法运行流程

      (一)读取手写体txt文件,转化为1X1024向量

        我们创建一个kNN.py,添加模块img2vector

    1 #识别手写字体模块-图像转向量32x32 to 1x1024
    2 def img2vector(filename):
    3     returnVect = zeros((1,1024))
    4     fr = open(filename)
    5     for i in range(32):
    6         lineStr = fr.readline()
    7         for j in range(32):
    8             returnVect[0,32*i+j] = int(lineStr[j])
    9     return returnVect

        我们的样本数据和测试数据都需要用到该函数

      (二)比较测试数据和样本数据集的距离,返回k近邻中最相似的标签

        在kNN.py中添加classify0模块,附上代码注释  

     1 #---------------------------------------------
     2 #分类模块
     3 #@params
     4 #   inX:输入向量、手写体识别的测试向量
     5 #    dataSet:训练集样本、手写体识别的训练集向量
     6 #    labels:训练集对应的标签向量
     7 #    k:最近邻居数目、本实验为3
     8 #---------------------------------------------
     9 def classifiy0(inX, dataSet, labels, k):
    10     dataSetSize = dataSet.shape[0]     #手写体样本集容量
    11     #(以下三行)距离计算
    12     diffMat = tile(inX, (dataSetSize,1)) - dataSet
    13     sqDiffMat = diffMat**2    
    14     sqDistances = sqDiffMat.sum(axis=1)
    15     distances = sqDistances**0.5   #欧氏距离开平方
    16     sortedDistIndicies = distances.argsort()  #距离排序的索引排序
    17     classCount = {}    
    18     #(以下两行)选择距离最小的k个点
    19     for i in range(k):
    20         voteIlabel = labels[sortedDistIndicies[i]]
    21         classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    22     sortedClassCount = sorted(classCount.items(),
    23     #排序
    24     key = operator.itemgetter(1), reverse = True)
    25     return sortedClassCount[0][0]            

        注意,这里使用了numpy的接口,在kNN.py的开头要加上:from numpy import* 

      (三)比较标签与测试结果,计算正确率

        同样,在kNN.py中添加handwritingClassTest模块,综合以上的两个模块,获得识别正确率

     1 #手写识别的测试代码
     2 def handwritingClassTest():
     3     hwLabels = []
     4     trainingFileList = listdir(path='trainingDigits')  #获取目录内容
     5     m = len(trainingFileList)
     6     trainingMat = zeros((m,1024))
     7     for i in range(m):
     8         #一下三行,从文件名解析分类数字
     9         fileNameStr = trainingFileList[i]
    10         fileStr = fileNameStr.split('.')[0]
    11         classNumStr = int(fileStr.split('_')[0])
    12 
    13         hwLabels.append(classNumStr)
    14         trainingMat[i,:] = img2vector('trainingDigits/%s'%fileNameStr)
    15     testFileList = listdir(path='testDigits')
    16     
    17     errorCount = 0.0  #错误个数计数器
    18     mTest = len(testFileList)
    19 
    20     #从测试数据中提取数据
    21     for i in range(mTest):
    22         fileNameStr = testFileList[i]
    23         fileStr = fileNameStr.split('.')[0]
    24         
    25         classNumStr = int(fileStr.split('_')[0])
    26         vectorUnderTest = img2vector('testDigits/%s'% fileNameStr)
    27         classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
    28         
    29         print("the classifier came back with:%d,the real answer is:%d"%(classifierResult,classNumStr))
    30         if(classifierResult != classNumStr):
    31             errorCount += 1.0
    32    #输出结果
    33     print("
    the total number of errors is:%d"%errorCount)
    34     print("
    the total error rate is: %f"%(errorCount/float(mTest)))

        注意,这里使用到了os模块listdir,在kNN开头加入:from numpy import listdir

      测试结果如下:

      错误率为1.16%,可以看到,识别效果挺不错。

    后记

      通过实验我们可以看到,使用kNN要将训练样本一次性加载入内存、如果训练集的规模很大,势必对机器有很大的要求。另外,kNN不需要训练算法、对异常值不敏感、在后期使用的时候要慎重选择吧

        

     

  • 相关阅读:
    博客搬家申请CSDN至博客园
    关于java8新特性的一些总结
    mysql数据库插入时更改数据
    2017总结
    java中单链表的操作
    工厂设计模式及理解
    筛选法求素数
    tcp粘包,断包问题及处理
    理解B树,B+树
    jvm中锁的优化
  • 原文地址:https://www.cnblogs.com/HolyShine/p/6358554.html
Copyright © 2020-2023  润新知