基于KNN算法的手写体机器识别项目

项目介绍
我们要写一个可以识别0-9的手写体数字的python系统。需要识别的数字已经用图像处理软件处理过。如下图所示,具有相同的像素和形状:都是32像素×32像素的黑白图像。黑白像素分别由0和1表示. 然后再转换成文本存储形式存储。例如,如下三张图是直接从TEXT文档中截取的数字3,6,8。
在这里插入图片描述

基于pandas.numpy的实现过程

首先从GitHub下载土耳其伊斯坦布尔大学的E.Alpaydin和C.Kaynak创建的数据集。https://raw.githubusercontent.com/zakkitao/database/master/trainingDigits.rar 将数据解压缩后发现有两个文件夹“trainingDigits”和“testDigits”. 里面分别有数据集中的1934个训练数据和946个测试数据。

我们先建立一个函数,该函数能从两个文件夹下的任意TXT文件读取字符串,并转化成int格式存储到一个numpy.ndarray中。为了后续处理方便,我们将32行字符串首尾相连,拼接成了一个长度为1024的单行字符串。然后使用内置函数int()转化类型后存储到ndarray中。

在这里插入图片描述
我们已经知道每个文件由1024个0和1组成。可以通过标准库模块os中的函数os.listdir()得到文件夹下完整的文件列表。然后清点文件数量就很容易了。假设文件数量是m,将元组(m, 1024)传递给函数np.zeros()就可以得到一个训练矩阵,将来文件夹“trainingDigits”中的所有文件中的信息都需要保存到这个矩阵中。下面的代码通过循环调用img2vector()函数,依次将所有训练集中的文件转换成一维ndarray,然后存储到矩阵trainingMat中。调用np.array.shape方法可以看到该矩阵有1934行,1024列。
在这里插入图片描述
然后我们将上一步产生的文件名列表hwLabels(hand writing labels的缩写)转换为一个pandas.Series, 以便后面其他部分的使用。可以查看这个Series的头部和形状。
在这里插入图片描述
然后我们要写一个计算欧氏距离的函数,数学公式如下:
在这里插入图片描述
样本之间的距离根据该函数计算,下面是具体的代码实现。

输入两个长度相等的简单float型列表,计算验证代码确实可以计算两个列表的距离。
在这里插入图片描述
然后我们写一个KNN的算法函数,该函数接受一个长度为1024的列表和一个k值。在函数内部会计算传入的列表和内置的二维array1中每一行的距离,然后使用标准库模块collections中的函数Counter()对这些距离排序,我们只需要取出最近的样本参数即可。
在这里插入图片描述然后我们给函数knn()传入列表anon的第一个元素,返回值(4, 10)代表频率最高的标签是4,出现了10次。这说明距离样本anon[0] 最近的10个样本都是手写体4,那我们就可以断定,样本anon[0]就是4. 到这里,knn函数已经实现了手写功能。
在这里插入图片描述
然后,为了让程序更有意思,我们用列表推导式生成一个包括10个测试样本的列表。为了能重复该随机抽样,我们用numpy.random.seed()设定固定的随机数种子。我们想测试一下看946个待测试样本中随机抽取的10个样本knn函数的识别率如何。
在这里插入图片描述
然后我们从这个testList中的每个字符串提取出文件名,然后和文件目录合并,并传递给img2vector()函数。返回值就是文件内二进制数值的ndarray了。我们对这10个文件进行测试,发现有9个文件最近的10个样本都完全匹配,而只有一个样本“1_4.txt”发生了一次不匹配。但当我们最终使用“多数表决”的方法进行投票的时候,并不会影响knn函数对该样本的识别。可以看到该算法在训练数据集足够多的情况下非常好地实现了手写体的识别。
在这里插入图片描述
下面附上的是完整的代码。

import numpy as np 
import pandas as pd 
import operator 
from os import listdir 
import math 
from collections import Counter  

def img2vector(filename):
    returnVect = np.zeros((1, 1024)) 
    fr = open(filename) 
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVect[0, 32*i + j] = int(lineStr[j]) 
    return returnVect  

hwLabels = []
# 将数据集的文件名存储在一个列表中
trainingFileList = listdir(r"D:\python\dataPy\harrington\trainingDigits")  

# m是目录中的文件数目 
m = len(trainingFileList)
# 创建一个m行1024列的训练矩阵,该矩阵的每一行存储一个图像 
trainingMat = np.zeros((m,1024))     

# 从文件名解析出分类数字,该目录的文件按照规则命名,
# 如文件9_45.txt的分类是9,它是数字9的第45个实例

for i in range(m):
    fileNameStr = trainingFileList[i]
    fileStr = fileNameStr.split('.')[0]   # take off '.txt' 
    classNumStr = int(fileStr.split('_')[0]) 
    hwLabels.append(classNumStr)    #类代码存储到变量hwLabels中 

    trainingMat[i,:] = img2vector(r'D:\python\dataPy\harrington\trainingDigits/%s' 

        % fileNameStr) 
 

'''part II starts from this line  '''
# 输入两个由0和1组成的等长列表,计算两个数组的欧几里得距离 

def distance(array1, array2):
    # assert len(array1) == len(array2) 
    container = [] 
    for i in range(len(array1)):
        var = pow(array1[i]-array2[i], 2) 
        container.append(var) 
    result = 0 

    for i in range(len(array1
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值