KNN算法Python代码实现(包含手写数字识别,代码进行详细解释)

将下载之后的数据集复制到你的Python代码目录下,我的是D:\pythonStudy\机器学习\K-近邻算法

其中datingTestset 与datingTestset2是关于约会网站配对

trainingDigits与testDigists是关于手写数字识别的txt文件

自编KNN代码

# -*- coding: utf-8 -*-
# @Author  : Mathematic
# @Time    : 2022-1-27 11:30
# @Function: KNN Python代码实现(不调用sklearn包)


from numpy import *
from os import listdir  #可以列出给定目录的文件名
import operator
import matplotlib.pyplot as plt
def creatdataset():   #定义训练数据集
    group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])   #定义测试数据
    labels=['A','A','B','B']    #定义标签
    return group,labels


#################以下注释代码可以可视化数据集createdataset#####################################

##########################测试数据集散点图表示(封面代码)##############################
'''
group=array([[1.0,1.1],[1.0,1.0],[0.0,0.0],[0,0.1]])  #获取矩阵group
labels=['A',"A",'B','B']  #获取列表labels
x=group[:,0]    #读取矩阵的第一列
y=group[:,1]    #读取矩阵的第二列
label=['A','A','B','B']
plt.xlim(xmax=1.2,xmin=-0.2)
plt.ylim(ymax=1.2,ymin=-0.2)
####画出散点图
for i in range(4):
    plt.plot(x[i],y[i],'ro')
    plt.text(x[i],y[i],label[i])
#plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.title('labels-data')
plt.show()
'''
##########################################################################################


##################################定义KNN算法分类器1###############################################################
def classify1(inX,dataset,labels,k):
    datasetSize=dataset.shape[0]  #获取dataset数据集的行数,如果是shape[1]就是获得列数
    diffMat=tile(inX,[datasetSize,1])-dataset   #tile(A,[a,b])是将A的行重复a次,列重复b次
    ####计算欧式距离
    sqDiffMat=diffMat**2
    sqdistances=sqDiffMat.sum(axis=1)
    distance1=sqdistances**0.5
    sorteindex=distance1.argsort()  #argsort返回的是从小到大排序的索引
    countdic={}  #定义一个空字典
    for i in range(k):
        newlabel=labels[sorteindex[i]]  #将空字典赋予新的键:newlabel
        #print(newlabel)
        countdic[newlabel]=countdic.get(newlabel,0)+1
        #print(countdic)
    sortedcount=sorted(countdic.items(),key=operator.itemgetter(1),reverse=True)    #sorted函数返回的是列表,itemgetter(1)是指按照第二个元素的顺序进行从小到大排列
    #print(sortedcount)
    return sortedcount[0][0] #45行代码后返回的是列表:[('B', 2), ('A', 1)],列表里面是元组,[0][0]表示的是列表第一个元素(元组)的第一个值,即B


###########################处理文本数据##############################################
def filematrix(filename):
    fr=open(filename)
    arrayofline=fr.readlines()
    numberoflines=len(arrayofline)   #读取文本的列的长度
    Mat=zeros((numberoflines,3))#生成一个与文本文件行数列数相同的零矩阵,由于本题文本前三列是数据,最后一列是标签
    classifylabelvector=[]  #初始化标签为空列表
    index=0   #初始化索引
    for line  in arrayofline:
        line=line.strip()  #截取掉回车符
        listfromline=line.split('\t')   #利用\tab字符将上一步获得的整行数据分割成一个元素列表
        Mat[index,:]=listfromline[0:3]   #选取列表中前三个元素存储到特征矩阵中
        classifylabelvector.append(listfromline[-1])  #Python中索引值-1指向列表最后一个元素,将其存储到标签列表中
        index+=1   #索引值+1
    return Mat,classifylabelvector    #返回特征矩阵与标签列表
###########################################################################


#############################数据归一化处理##################################
def autonorm(dataset):
    minval=dataset.min(0)  #将dataset的每一列的最小值存储到minval中,min(0)的0为取所在列的最小值,而不是当前行的最小值
    maxval=dataset.max(0)  #dataset的每一列的最大值存储到maxval中,max(0)的0为取所在列的最大值,而不是当前行的最大值
    ranges=maxval-minval   #最大值减去最小值
    normdataset=zeros(shape(dataset))  #生成与dataset同类型的零矩阵,即初始化normdataset
    m=dataset.shape[0]      #获取dataset的行数
    normdataset=dataset-tile(minval,(m,1))   #将dataset的每个元素都减去该元素所在列的最小值
    normdataset=normdataset/tile(ranges,(m,1))  #归一化处理
    return normdataset,ranges,minval
#############################################################################

##############################对分类器1进行测试,拿出datingTestset2.txt中的10%数据来进行测试###########################################

def datingclassTest():
    ratio=0.1   #选取数据集的10%作为测试集
    datingmat,datinglabels=filematrix('datingTestSet.txt')   #选取文本类数据datingTestSet.txt
    normMat,ranges,minval=autonorm(datingmat)   #数据归一化处理
    m=normMat.shape[0]   #读取归一化矩阵的行数
    testnumbers=int(m*ratio) #测试集的数据个数
    errorcount=0    #初始化错误预测的数据个数
    for i in range(testnumbers):  #由于该数据集没有先后顺序,故选取前面10%的数据来进行测试
        result=classify1(normMat[i,:],normMat[testnumbers:m,:],datinglabels[testnumbers:m],3)   #输入数据为测试集前m个,原数据为其余的1000-m个
        print('The predict data class is :{0},The real value is : {1}'.format(result,datinglabels[i]))
        if(result!=datinglabels[i]):
            errorcount+=1
    print('The accuracy is ',1-errorcount/testnumbers)
#最后显示准确率为0.95,故该分类器效果比较不错

###########################################################################################


###################################利用分类器对自己输入的数据进行预测#####################################

def predictperson():
    personlabels=['not at all','small doses','large doses']
    percentTats=float(input('percentage of time in playing video games?'))
    ffmiles=float(input('frequent flier mails earned every year?'))
    icecream=float(input('liters of ice cream consumed per year? '))
    datingdatamat,datinglabels=filematrix('datingTestSet2.txt')
    normmat,ranges,minval=autonorm(datingdatamat)
    inarray=array([ffmiles,percentTats,icecream])
    classifyresult=classify1((inarray-minval)/ranges,normmat,datinglabels,3)
    print('You will probably like like this person :',personlabels[int(classifyresult) -1])
###############################################################################################

#########################将图像格式化处理成一个向量#####################################33
def imgtovector(filename):
    returnvector=zeros((1,1024))
    fr=open(filename)
    for i in range(32):
        linestr=fr.readline()
        for j in range(32):
            returnvector[0,32*i+j]=int(linestr[j])
    return returnvector
##########################################################################################


###############################手写数字识别系统的测试代码########################################
def handwritingtest1():
    hwlabels=[]
    #############获取目录内容##################
    trainingfilelist=listdir('trainingDigits')   #listdir可以获得目录所存的文件名称
    m=len(trainingfilelist)    #获得文件的个数
    trainmat=zeros((m,1024))   #初始化训练集矩阵,一共有m个文件,每个文件生成【1,1024】维行向量
    for i in range(m):
        filenamestr=trainingfilelist[i]   #获得第i个文件名称
        filestr=filenamestr.split('.')[0]  #将文件名称分为两部分,分隔符以'.',取第一部分,例如:3_112.txt分割为3_112与txt,取3_112
        classNumstr=int(filestr.split('_')[0])  #将3_112划分为两个部分,分隔符以'_',取第一部分,例如:3_112分割为3和112,取3,也就是待识别的数字3
        #print(classNumstr)
        hwlabels.append(classNumstr)            #将待识别的数字添加到列表hwlabels里面去
        trainmat[i,:]=imgtovector('D:/pythonStudy/机器学习/K-近邻算法/trainingDigits/%s' % (filenamestr))  #存储训练数据
    testfilelist=listdir('testDigits')  #获得测试集的文件名称
    errorcount=0
    mtest=len(testfilelist)
    for i in range(mtest):
        fileNamestr=testfilelist[i] #读取测试集里面第i个文件名称
        fileStr=fileNamestr.split('.')[0]  #同上
        testclassnumber=int(fileStr.split('_')[0]) #同上
        vectortest=imgtovector('D:/pythonStudy/机器学习/K-近邻算法/testDigits/%s' % (fileNamestr))  #注意%两边都是空格
        clssifyresult=classify1(vectortest,trainmat,hwlabels,3)  #由于KNN不需要训练过程,只需要待分类的数据集与测试集即可
        #print('The predict number is:%d,The real number is :%d'%(clssifyresult,testclassnumber))
        if(clssifyresult!=testclassnumber):
            errorcount+=1
    print('The accurray is :%f'%(1-errorcount/float(mtest)))  #输出分类准确率


#####################通过调整k值大小来判断最优k值是多少(针对手写数字识别)############################
def handwritingtest2():
    hwlabels = []
    #############获取目录内容##################
    trainingfilelist = listdir('trainingDigits')  # listdir可以获得目录所存的文件名称
    m = len(trainingfilelist)  # 获得文件的个数
    trainmat = zeros((m, 1024))  # 初始化训练集矩阵,一共有m个文件,每个文件生成【1,1024】维行向量
    for k in range(2,7):
        for i in range(m):
            filenamestr = trainingfilelist[i]  # 获得第i个文件名称
            filestr = filenamestr.split('.')[0]  # 将文件名称分为两部分,分隔符以'.',取第一部分,例如:3_112.txt分割为3_112与txt,取3_112
            classNumstr = int(filestr.split('_')[0])  # 将3_112划分为两个部分,分隔符以'_',取第一部分,例如:3_112分割为3和112,取3,也就是待识别的数字3
            # print(classNumstr)
            hwlabels.append(classNumstr)  # 将待识别的数字添加到列表hwlabels里面去
            trainmat[i, :] = imgtovector('D:/pythonStudy/机器学习/K-近邻算法/trainingDigits/%s' % (filenamestr))  # 存储训练数据
        testfilelist = listdir('testDigits')  # 获得测试集的文件名称
        errorcount = 0
        mtest = len(testfilelist)
        for i in range(mtest):
            fileNamestr = testfilelist[i]  # 读取测试集里面第i个文件名称
            fileStr = fileNamestr.split('.')[0]  # 同上
            testclassnumber = int(fileStr.split('_')[0])  # 同上
            vectortest = imgtovector('D:/pythonStudy/机器学习/K-近邻算法/testDigits/%s' % (fileNamestr))  # 注意%两边都是空格
            clssifyresult = classify1(vectortest, trainmat, hwlabels, k)  # 由于KNN不需要训练过程,只需要待分类的数据集与测试集即可
            # print('The predict number is:%d,The real number is :%d'%(clssifyresult,testclassnumber))
            if (clssifyresult != testclassnumber):
                errorcount += 1
        print('The accurray is :%f' % (1 - errorcount / float(mtest)))  # 输出分类准确率

'''
k-近邻算法是分类数据最简单最有效的算法,本章通过两个例子讲述了如何使用k-近邻算法
构造分类器。k-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数
据。k-近邻算法必须保存全部数据集,如果训练数据集的很大,必须使用大量的存储空间。此外,
由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。
k-近邻算法的另一个缺陷是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均
实例样本和典型实例样本具有什么特征。下一章我们将使用概率测量方法处理分类问题,该算法
可以解决这个问题。
'''

调用编写的KNN

import Knn
'''
group,labels= KNN.creatdataset()
a= KNN.classify1([0, 0], group, labels, 3)     #########对点[0,0]进行分类
print(a)
'''
datingMat,datinglabels=Knn.filematrix('datingTestSet.txt')
normmat,range,minval=Knn.autonorm(datingMat)
#Knn.datingclassTest()
#Knn.predictperson()
#Knn.handwritingtest2()

代码所需数据集在公众号:数学与智能科学 回复:KNN即可获得。

同时在公众号里面有关于算法KNN的详细介绍

你可能感兴趣的:(python,算法,机器学习)