使用k-近邻算法识别手写数字。

2023-05-16

在之前的文章中介绍了k-近邻算法的原理知识并且用Python实现了一个分类器,而且完成了一个简单的优化约会网站配对效果的实例。在《机器学习实战》中有关kNN的后一部分内容就是一个手写识别系统,可以识别手写的0-9的数字。下面就基于这一章的内容完成这样一个手写数字识别系统。


案例的描述以及流程介绍。

既然我们明白了kNN算法是根据计算新数据和样本数据集之间的距离,然后找到距离最小的样本的分类作为新数据的分类。所以我们也需要考虑如何计算0-9之间这十个数字的距离。首先书中已经提供了0-9数字的样本集,每个数字提供了大概200左右的样本,在trainingDigits目录下。

这里写图片描述

比如其中的0_0.txt就是数字0的第一个样本,又比如9_45.txt就是数字9的第45个实例,数字样本采用的是32*32文本存储方式:

比如:0_0.txt,从图中也能看出来像数字0。

这里写图片描述

但是提供的有些数据集并不是日常所见的数字写法,比如数字7:

这里写图片描述

由于我们后续需要计算的距离就是根据这些0和1的排列方式计算的,所以类似数字7这种写法差异的话,最后分类结果也可能有差异。如果可能的话,自己也是可以根据自己的书写习惯写样本集,然后将其转换为32*32文本存放,这样一来分类结果肯定要比这种的好一点。

至于如何将图片转换为32*32数组,可以参考之前的文章:手写数字图片二值化转换为32*32数组。

在本次案例中kNN算法的使用流程:

  1. 收集数据:提供文本文件。
  2. 准备数据:自己提供的32*32数组。
  3. 分析数据:查看数据,确保符合要求。
  4. 训练算法:此步骤不适用于k-近邻算法。
  5. 测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
  6. 使用算法:将一个图片作为输入数据,然后使用分类器进行分类得到结果。

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

上面已经介绍了本次的样本集是一个32*32的数组,为了使用前面的分类器,我们必须将这个32*32的数组转换为一个1*1024的向量。首先编写一个img2vector的函数,传入一个样本数据的文件名,可以将这个文件中的32*32数组转换为一个1*1024的向量。

def img2vector(filename):
    """
    将32*32的图像矩阵转化为1*1024的向量
    :param filename: 文件名
    :return:
    """
    # 制造一个空数组
    returnVect = np.zeros((1,1024))

    # 打开文件
    fr = open(filename)

    # 将矩阵转换为1*1024的
    for i in range(32):
        # 读取一行数据
        lineStr = fr.readline().strip()

        # 将每行的数字存放在数组中
        for j in range(32):
            returnVect[0, 32*i+j] = int(lineStr[j])

    return returnVect

编写一个简单的测试代码:

这里写图片描述

首先将0_13.txt作为参数传入img2vector函数中,然后使用len计算结果的长度是不是1*1024的,然后输出第一行内容,输出结果:

这里写图片描述

其中输出的testVector[0, 0:31]就是0_13.txt的第一行内容,可以打开0_13.txt查看对比一下:

这里写图片描述

能够看到结果一致,依此论推,testVector[0, 32:63]就是第二行的内容等等,所以确实是将0_13.txt的32*32数组转换为了1*1024向量数组。


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

我们在上一步中将32*32数组转化为1*1024向量数组的目的就是为了能够使用之前编写的kNN分类器。现在就可以将样本数据集输入到分类器中进行使用了。

我们先来测试一下这个算法,编写一个handwritingClassTest函数,该函数将trainingDigits目录下的所有样本进行读取,然后创建一个m*1024的训练矩阵进行存储,很显然该矩阵每一行都代表着一个图像。然后再读取testDigits目录下的测试样本集,每次读取一个文件,也就代表着一个图像,然后将其转换为1*1024数组,和原来的训练矩阵共同输入到之前的kNN分类器中进行分类。分类器会计算这个测试样本集和训练矩阵的距离,根据欧氏距离的计算方法,这个测试数据共计算m次距离,然后找到距离最近的,这个计算量还是比较巨大的。

def handwritingClassTest():
    """
    数字测试
    :return:
    """
    # 数字对应的标签,也就是数字的本身
    hwLabels = []

    # 得到目录下的所有文件名称
    trainingFileList = listdir('trainingDigits')

    # 计算共有多少个文件
    m = len(trainingFileList)

    # 构造m*1024 数组,用来存放所有的数字
    trainingMat = np.zeros((m, 1024))

    # 遍历所有的文件,将其加载到数组中
    for i in range(m):
        # 得到文件名称
        fileNameStr = trainingFileList[i]

        # 去除后面的.txt,得到有用的文件名
        fileStr = fileNameStr.split('.')[0]

        # 解析出来当前是哪个数字
        classNumStr = int(fileStr.split('_')[0])

        # 添加到标签上
        hwLabels.append(classNumStr)

        # 将文件转化为数组并存放到总的数组中
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)

    # 得到测试文件的目录
    testFileList = listdir('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('testDigits/%s' % fileNameStr)

        # 使用分类器得到结果
        classifierResult = kNN.classify0(vectorUnderTest, trainingMat, hwLabels, 3)

        # 打印结果
        print('使用分类器得到的结果为:%s,真实的结果为:%s' % (classifierResult, classNumStr))

        # 错误的话记录下来
        if classifierResult != classNumStr:
            errorCount += 1.0

    print('识别错误的个数为:%s' % errorCount)
    print('分类器的正确率为:%f' % (errorCount/float(mTest)))

编写一个测试代码执行这个函数:

这里写图片描述

查看一下输出结果:

这里写图片描述

可以看到错误率大概为1.2%,其实这些都是根据计算得到的结果,如果我们修改分类器k的值,或者选取不同的测试样本或者训练样本,都会影响这个错误率,不过可以看到这些训练样本集的正确率已经很高了,我们现在就可以真正的使用分类器进行识别了。


使用算法:将手写数字图片进行识别。

在测试完分类器之后,我们就可以使用了。最终我们的目的就是输入一个自己手写的数字图片,然后分类器告诉我这个数字是几。编写classifyHandwriting函数来使用完整的系统,该函数需要一个图像名称参数,然后会将这个图像转换为32*32数组,接下来转换为1*1024数组,在读取训练样本集,然后将新数据和训练样本集共同输入到分类器中得到分类结果。

def classifyHandwriting(filename):
    """
    将图片转化为01矩阵,然后使用分类器进行分类
    :return:
    """
    # 得到32*32的01数组
    imgTo01.picTo01(filename)

    # 得到对应名称的txt文件
    name01 = filename.split('.')[0]
    name01 = name01 + '.txt'

    # 将文件中的32*32 转化为1*1024的
    hwMat = img2vector(name01)

    # 数字对应的标签,也就是数字的本身
    hwLabels = []

    # 得到目录下的所有文件名称
    trainingFileList = listdir('trainingDigits')

    # 计算共有多少个文件
    m = len(trainingFileList)

    # 构造m*1024 数组,用来存放所有的数字
    trainingMat = np.zeros((m, 1024))

    # 遍历所有的文件,将其加载到数组中
    for i in range(m):
        # 得到文件名称
        fileNameStr = trainingFileList[i]

        # 去除后面的.txt,得到有用的文件名
        fileStr = fileNameStr.split('.')[0]

        # 解析出来当前是哪个数字
        classNumStr = int(fileStr.split('_')[0])

        # 添加到标签上
        hwLabels.append(classNumStr)

        # 将文件转化为数组并存放到总的数组中
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)

    # 进行分类
    classifierResult = kNN.classify0(hwMat, trainingMat, hwLabels, 3)

    print('使用分类器的结果为:%d' % classifierResult )

编写测试代码:

这里写图片描述

准备手写数字:

这里写图片描述

运行分类器得到结果:

这里写图片描述

能够看到识别出了数字,但是能够正确识别的关键点在于能否准确的将图片转换为32*32数组。因为使用的图片转换为32*32数组是本人简单写的一个程序,在转换过程中可能会出现问题导致分类结果不准确,如果能够用其他办法准确的将图片转换为32*32数组的话,这个分类器还是比较准确的。

全部代码及数据集地址:MachineLearningNote

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用k-近邻算法识别手写数字。 的相关文章

随机推荐

  • CentOS 7 为firewalld添加开放端口及相关资料

    目录 一 运行 停止 禁用firewalld 1 1启动 xff1a 1 2 查看状态 xff1a 1 3 停止 xff1a 1 4禁用 xff1a 二 查看firewall是否运行 下面两个命令都可以 三 查看default zone和a
  • MicroStrategy的面经(from bbs.byr..

    搜了一下论坛 xff0c 发现基本上没有MicroStrategy的面经 xff0c 既然有幸参加了他们的面试 xff0c 这里把面试过程给大家描述一下 xff0c 希望对后来的人有点帮助吧 1 公司介绍 xff1a 中文叫凌策软件 xff
  • 程序员迷茫:30岁以上的“大龄程码农”出路在哪?java码农该怎么办?

    程序员生存 成功 制胜的法则源自IT精英的职业发展秘诀热爱工作 xff0c 享受生活 为什么程序员过了30就不行了 xff1f 我们被固定在 敲代码 的坑里 xff0c 一干就是10年 xff0c 再干别的早已不会 敲代码已经成了一项流水线
  • Ubuntu添加和设置开机自动启动程序的方法

    ubuntu 18 04 设置开机启动脚本 ubuntu 18 04 设置开机启动脚本 参阅下列链接 https askubuntu com questions 886620 how can i execute command on sta
  • Codewars 刷题笔记(Python)5.Disemvowel Trolls

    题目 Trolls are attacking your comment section A common way to deal with this situation is to remove all of the vowels fro
  • 华为笔试题(4)

    一 计算n x m的棋盘格子 xff08 n为横向的格子数 xff0c m为竖向的格子数 xff09 沿着各自边缘线从左上角走到右下角 xff0c 总共有多少种走法 xff0c 要求不能走回头路 xff0c 即 xff1a 只能往右和往下走
  • ubuntu 16.04 LTS + xgboost 0.7 + GPU support

    记录下安装xgboost 踩过的坑 xff0c 也是为了日后万一再需要 xff0c 可以翻看 cpu版的安装倒是很容易的 xff1a sudo pip install xgboost gpu版 xff1a 为了避免import的时候把旧版的
  • mariaDB JSON函数

    官方文档 xff1a https mariadb com kb en json search
  • #最详细# Github Page 个人博客绑定二级域名

    文章目录 1 必要条件 xff1a 2 操作步骤 xff1a 3 操作3 1 在阿里云控制台添加子域名解析记录3 2 在 Github 中修改配置 1 必要条件 xff1a 已申请个人域名已配置好Github Page 2 操作步骤 xff
  • springboot日志输出到文件

    今天来谈一谈日志 xff0c 主要是说一说springboot的日志 xff0c 因为最近在学习springboot 首先在写代码的时候 xff0c 要养成记日志的习惯 xff0c 这点真的很重要 xff0c 因为之前吃了很多亏 过去我对日
  • Spring--开源的轻量级的Java开发框架

    目录 xff1a 一 Spring 简介1 什么是Spring2 Spring 框架的优点3 Spring 体系结构 二 Spring 容器1 什么是Spring容器2 Spring 容器的实例化3 Spring 容器的使用 三 Sprin
  • viewBinding的搭建,通过反射获取绑定

    再使用viewBinding 的时候报出下面这个异常 java lang NoSuchMethodError No interface method getTypeName Ljava lang Str 代码是这样 Type types 6
  • Java怎么去除字符串中的所有数字?

    String string span class token operator 61 span span class token string 34 abc123zxc56qwer89ws5 34 span span class token
  • Android系统10 RK3399 init进程启动(三十一) SeAndroid实战之定义策略

    配套系列教学视频链接 xff1a 安卓系列教程之ROM系统开发 百问100ask 说明 系统 xff1a Android10 0 设备 xff1a FireFly RK3399 xff08 ROC RK3399 PC PLUS xff09
  • Codewars 刷题笔记(Python)6.Multiples of 3 or 5

    题目 If we list all the natural numbers below 10 that are multiples of 3 or 5 we get 3 5 6 and 9 The sum of these multiple
  • Java多种方式解决生产者消费者问题(十分详细)

    一 问题描述 生产者消费者问题 xff08 Producer consumer problem xff09 xff0c 也称有限缓冲问题 xff08 Bounded buffer problem xff09 xff0c 是一个多线程同步问题
  • Centos6 yum安装VNC-server

    一 环境 Linux操作系统 xff1a centos6 9 二 安装步骤 1 检查是否已经安装了vnc server root 64 VM 0 11 centos rpm q tigervnc tigervnc server packag
  • Docker部署rabbitmq遇到的两个问题

    当使用docker部署rabbitmq时遇到两个问题 xff0c 访问交换机时报错 xff0c 另一种是访问channel时报错 xff0c 本文给大家分享解决方案 xff0c 感兴趣的朋友跟随小编一起看看吧 1 背景 Docker部署ra
  • Python pip源配置修改

    由于某些不可抗因素 xff0c Python官方的包在国内有时无法访问或出现网络不稳定现象 为了解决这个问题就需要将Pip中自带的源地址修改为镜像地址 目前收集的比较好的镜像地址有 xff1a http pypi v2ex com simp
  • 使用k-近邻算法识别手写数字。

    在之前的文章中介绍了k 近邻算法的原理知识并且用Python实现了一个分类器 xff0c 而且完成了一个简单的优化约会网站配对效果的实例 在 机器学习实战 中有关kNN的后一部分内容就是一个手写识别系统 xff0c 可以识别手写的0 9的数