使用K-邻近算法实现手写数字识别系统

 k-近邻法简介

    k近邻法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法。它的工作原理是:存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

k-近邻法的工作原理

 存在一个样本数据集合,也称作训练样本集,并且样本集合每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最邻近)的分类标签。一般来说我们只选择样本数据集中前K个最相似的数据,这就是K-邻近算法中K的出处,通常K是不大于20的整数。最后,选择K个最相似数据中出现最多的分类,作为新数据的分类。K邻近模型由三个基本要素–距离度量、K值选择和分类决策规则决定。

KNN算法的一般流程:

1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。

手写数字识别系统

 1.1准备:使用Python导入数据

        首先,创建名为kNN.py的Python模块,创建实验文件夹KNN:

cd Code
mkdir KNN
cd KNN
touch kNN.py

 1.2 对我们的 kNN.py文件进行编辑。在kNN.py文件中增加下面的代码

    from numpy import *
    import operator
 
    def createDataSet():
        group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
        labels = ['A','A','B','B']
        return group, labels

 1.3上述命令创建了变量grouplabels,在Python命令提示符下输入下列命令,输入变量的名字以检验是否正确地定义变量:

    >>> import kNN
    >>> group,labels = kNN.createDataSet()
    >>> group
    array([[ 1. , 1.1],
           [ 1. , 1. ],
           [ 0. , 0. ],
           [ 0. , 0.1]])
    >>> labels
    ['A', 'A', 'B', 'B']

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

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

from numpy import *
 
def img2vector(filename):
    # 创建向量
    returnVect = 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

 得到.txt文件类型的测试数字数据:

 

00000000000000110000000000000000
00000000000000111100000000000000
00000000000001111110000000000000
00000000000011111111000000000000
00000000000111111100000000000000
00000000000111111110000000000000
00000000000111111110000000000000
00000000011111110000000000000000
00000000001111110000000000000000
00000000011111100000000000000000
00000000111111100000000000000000
00000000111111000000000000000000
00000000111110000000000000000000
00000000111111000000000000000000
00000000111110000000000000000000
00000000111110011111110000000000
00000000111111111111111000000000
00000000111111111111111100000000
00000000111111111111111111000000
00000000111111111101111111000000
00000000111111000000000011110000
00000000111110000000000011110000
00000000111110000000000011110000
00000000011111000000000001111000
00000000011111000000000001111000
00000000001111100000000001111100
00000000001111110000001111111000
00000000001111110000111111111000
00000000000011111111111111110000
00000000000001111111111111100000
00000000000000111111111111100000
00000000000000001111111100000000

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

测试的步骤:

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

 实现代码:

from os import listdir
 
def handwritingClassTest():
    # 样本数据的类标签列表
    hwLabels = []
 
    # 样本数据文件列表
    trainingFileList = listdir('digits/trainingDigits')
    m = len(trainingFileList)
 
    # 初始化样本数据矩阵(M*1024)
    trainingMat = 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)
 
        # 打印KNN算法分类结果和真实的分类
        print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
 
        # 判断KNN算法结果是否准确
        if (classifierResult != classNumStr): errorCount += 1.0
 
    # 打印错误率
    print "\nthe total number of errors is: %d" % errorCount
    print "\nthe total error rate is: %f" % (errorCount/float(mTest))

 测试结果:

    >>> reload(kNN)
    >>> kNN.handwritingClassTest()
    the classifier came back with: 0, the real answer is: 0 
    the classifier came back with: 0, the real answer is: 0
    .
    .
    the classifier came back with: 7, the real answer is: 7 
    the classifier came back with: 7, the real answer is: 7 
    the classifier came back with: 8, the real answer is: 8 
    the classifier came back with: 8, the real answer is: 8 
    the classifier came back with: 8, the real answer is: 8 
    the classifier came back with: 6, the real answer is: 8
    .
    .
 
 
    the total number of errors is: 12
 
    the total error rate is: 0.012685

 k-近邻算法识别手写数字数据集,错误率为1.2%。

4.1实验总结

kNN算法的优缺点

优点

  • 简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;
  • 可用于数值型数据和离散型数据;
  • 训练时间复杂度为O(n);无数据输入假定;
  • 对异常值不敏感。

缺点:

  • 计算复杂性高;空间复杂性高;
  • 样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);
  • 一般数值很大的时候不用这个,计算量太大。但是单个样本又不能太少,否则容易发生误分。
  • 最大的缺点是无法给出数据的内在含义。

 

 

 

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值