基于python的手写数字识别(KNN算法)

基于python的手写数字识别(KNN算法)

文章已迁移至:http://www.codeci.cn/

  • 准备数据
  • knn算法
  • 简单图片识别(txt格式)
  • 简单图片识别

一、准备数据

txt格式:
text_digits数据集
training_digits数据集
图片格式:
text_digits数据集
trainning_digits数据集
数据集(训练集和测试集的下载)


二、knn算法

欧拉距离计算公式

d = ( a i − b i ) + ⋯ + ( a n − b n ) d=\sqrt{(a_i-b_i)+\cdots+(a_n-b_n)} d=(aibi)++(anbn)

KNN取值

选取k个欧拉距离最近的值,判断k个值里面出现的频率最高的特征(即欧拉距离对应训练集图片的数字)。
knn算法简单来说通过欧拉距离比较相似度,从k个结果里面选取频率最高的那个。


三、简单识别

文本向量化(32*32 -> 1*1024)

代码:

#文本向量化 32x32 -> 1x1024
def img2vector(filename):
    returndigits = []
    f = open(filename)    #打开txt文件
    for i in range(32):
        linestr = f.readline()    #逐行读取数据
        for j in range(32):
            returndigits.append(int(linestr[j]))    #读取数据逐行追加到列表后面
    return returndigits

单独测试时,可以将filename改成txt格式图片的文件地址,用print函数查看运行结果。

文件名解析(从文件名分离出数字)

def classnumCut(fileName): 
    fileStr = fileName.split('.')[0]        #根据文件名中的字符"."进行切片 
    classNumStr = int(fileStr.split('_')[0])     #根据文件切片结果中的"_"进行再次切片
    return classNumStr

图片文件名格式:num1_num2,获取一系列数字num1存入列表。从文件名中分离出数字主要涉及到python的切片操作知识(python的一个重点)。

构建训练集向量(n*1024)

def trainingDataSet():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')    #获取目录内容
    m = len(trainingFileList)    #获取文件目录长度
    trainingMat = zeros((m,1024))     #m维向量的训练集
    for i in range(m):
        fileNameStr = trainingFileList[i]    #获取第i行文件
        hwLabels.append(classnumCut(fileNameStr))    #文件所表示的数字追加到列表(列表内的数字与文件txt显示的数字一一对应)
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)    #将向量化的数据追加到trainingMat列表,一行代表一个文件的数据,与hwLabels列表和txt文件对应)
    return hwLabels,trainingMat

将训练集的图片向量化,形成一个二元列表。knn算法就是从n个列表中(训练集图片)选取n个距离最近的值。

knn算法

#knn算法
def sort(inputimage,dataSet,labels,k):
    dataSetSize = dataSet.shape[0]     #已知分类的数据集(训练集)的行数
    #先tile函数将输入点拓展成与训练集相同维数的矩阵,再计算欧氏距离
    diffMat = tile(inputimage,(dataSetSize,1))-dataSet  #样本与训练集的差值矩阵
    sqDiffMat = diffMat ** 2                    #差值矩阵平方
    sqDistances = sqDiffMat.sum(axis=1)         #计算每一行上元素的和
    distances = sqDistances ** 0.5              #开方得到欧拉距离矩阵
    sortedDistIndicies = distances.argsort()    #按distances中元素进行升序排序后得到的对应下标的列表(argsort函数排序的是列表元素的下标)
    #选择距离最小的k个点
    classCount = {}
    for i in range(k):
        voteIlabel = labels[ sortedDistIndicies[i] ]    #读取labels列表中相同下标的值(即训练组中的值)
        classCount[voteIlabel] = classCount.get(voteIlabel,0)+1  #计算classCount列表中voteIlabel值出现的次数
   
    sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)  #按classCount字典的第2个元素(即类别出现的次数)从大到小排序
    
    return sortedClassCount[0][0]

将测试数据拓展成相同维度矩阵([0,1,……,1,0]转换成[[0,1,……,1,0],[0,1,……,1,0],[0,1,……,1,0],[0,1,……,1,0],……,[0,1,……,1,0],[0,1,……,1,0]]),相当于将测试图片与训练集每个图片进行比较,计算欧拉距离。
排序时比较的是距离大小,排列的是对应列的下标。
拓展后的测试矩阵下标与训练组矩阵下标一一对应,读取下标在训练组中对应的值并形成字典[“key”(出现次数):“value”(测试组数字),……]。最后比较字典第二个元素,即测试组对应的数字(value),第一个元素(key)最大的即是测试结果。

测试函数

#测试函数
def handwriting():
    hwLabels,trainingMat = trainingDataSet()    #构建训练集
    testFileList = listdir('testDigits')        #获取测试集
    errorCount = 0.0                            #错误数
    mTest = len(testFileList)                   #测试集总样本数
    for i in range(mTest):
        fileNameStr = testFileList[i]
        classNumStr = classnumCut(fileNameStr)
        data= img2vector('testDigits/%s' % fileNameStr)                                                              #调用knn算法进行测试
        classifierResult = sort(data, trainingMat, hwLabels,6)
        print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
        if (classifierResult != classNumStr):
            errorCount += 1.0
            #print "error:the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
    print "\nthe total number of tests is: %d" % mTest               #输出测试总样本数
    print "the total number of errors is: %d" % errorCount           #输出测试错误样本数
    print "the total error rate is: %f" % (errorCount/float(mTest))  #输出错误率

#确定主函数    
if __name__ == "__main__":
    handwriting()

上述函数可以实现knn算法图片txt文件的识别。相对来说,图片格式的识别对于数据处理的要求更高,但其原理类似,将图片格式转二值化后进行数据比较。

四、图片识别

图片识别需要更改上述函数涉及到训练组数据的部分,将调用img2vector函数改成调用imgBinary函数。图片二值化的方法较多,此处采用PIL库进行比较粗糙的二值化处理,总的来说,knn算法下,二值化的准确程度对于测试结果的影响较大。


#图片二值化处理
def imgBinary(inputimage): 
    img = Image.open(inputimage)  #调用PIL库Image函数

    img = img.resize((32, 32))    #调整图片大小
    width, height = img.size      #获取图片的规格(宽、高)
    img = img.convert('L')    #调用PIL库中的convert函数:图片进行灰度化,模式L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度
    
    data = []
    #灰度图片二值化处理
    for i in range(height):   
        for j in range(width):
            pixel = img.getpixel((j, i))  #调用PIL库中的getpixel函数获取像素点
            
            if (pixel > 220):             #比较灰度值进行二值化
                pixel = 0
            else:
                pixel = 1
            data.append(pixel)          #数据追加到列表
    return data

knn算法中的重点主要是knn算法的理解,文件名中数字的提取,数据比较的处理,图像二值化的处理,注意列表,元组,字典的用法。在实现过程中注意使用函数进行测试,查看数据处理的结果是否合理。遇到调用函数的问题时多看官方文档,查清函数的格式,输入,输出。

你可能感兴趣的:(python,python,knn算法,图片识别,手写数字识别)