kNN_hand_writing(机器学习)(实验报告)

一、实验目的:

通过本实验掌握kNN算法的原理,熟悉kNN算法如何应用在真实世界问题中,同时掌握sklearn机器学习库的使用。

二、实验内容:

本实验首先使用基于Python实现kNN算法实现手写字识别,然后使用sklearn库的kNN算法实现手写字识别。

三、实验环境:

  • python 3.6.5
  • sklearn 0.19.1
  • CourseGrading在线实验环境

四、实验内容

    def classify0(inX, dataSet, labels, k):

    ### Start Code Here ###

    #具体实现逻辑如KNN算法中所示

     num = len(dataSet)

    #行向量方向上将inX复制m次,然后和dataSet矩阵做相减运算

    n=num-1

    ans=np.asarray(inX)

    tmp=np.asarray(inX)

    while n!=0:

        ans = np.append(ans,tmp)

        n -= 1

    ans = np.reshape(ans,[num,2])

    ans = ans-dataSet

    #减完后,对每个数做平方

    ans = ans*ans

    #平方后按行求和,axis=0表 示列相加,axis-1表示行相加

    dis = np.zeros([num,1],dtype=int)

    #开方计算出欧式距离

    for i in range(num):

        dis[i] = ans[i][0]+ans[i][1]

    ans = np.reshape(dis,[1,num])

    #对距离从小到大排序,注意argsort函数返回的是数组值从小到大的索引值2

    y = np.argsort(ans)

    #用于类别/次数的字典,key为类别, value为次数

    y=list(y[0])

    #取出第近的元素对应的类别

    dic  =  dict.fromkeys(list(set(labels)), 0)

    #对类别次数进行累加

    for i in range(k):

        dic[labels[y[i]]]+=1

    #根据字典的值从大到小排序

    dic = sorted(dic.items(),key =lambda item:item[1],reverse=True)

    #返回次数最多的类别,即所要分类的类别

    return dic[0][0]

### End Code Here ###

dataSet=np.array([[250,100],[270,120],[111,230],[130,260],[200,80],[70,190]])

labels=["理科生","理科生","文科生","文科生","理科生","文科生"]

inX=[105,210]

print(classify0(inX,dataSet,labels,3))

加载手写字训练集数据

接下来,我们将加载手写字训练集数据,由于kNN核心算法中,每个点都是用向量表示的。尽管已经转换为文本形式了,不过图片数据依然是32X32的二维数据格式,首先需要将其转换为一维数组,即表示成一个向量。在kNN_hand.py中添加函数img2vector用于将32X32的二维数组转换为一维数组,该函数传入参数文件名,返回转换后的一维数组。函数实现如下所示:

def img2vector(filename):

    ### Start Code Here ###

    #创建1x1024零向量

    returnVect = np.zeros((1, 1024))

    #打开文件

    fr = open(filename)

    #按行读取

    for i in range(32):

        #读一行数据

        lineStr = fr.readline()

        #每一行的前32个元素依次添加到returnVect中

        for j in range(32):

            returnVect[0, 32*i+j] = int(lineStr[j])

    #返回转换后的1x1024向量

    return returnVect

   

    ### End Code Here ###

1到2行测试函数img2vector是否正确,传入的文件路径为:kNN_hand_writing/trainingDigits/1_1.txt,由于np打印矩阵时,默认情况下会省略中间内容,需要进行设置,使所有数据都能显示出来,第1行进行了设置。从以上代码可以看到打印结果和预期结果相符合。接下来我们编写loadTrainingData函数,加载所有训练集数据。该函数将返回表示训练集数据的矩阵以及每个点对应的分类标签:

np.set_printoptions(threshold=np.inf)

print(img2vector("kNN_hand_writing/trainingDigits/1_1.txt"))

def loadTrainData():

    ### Start Code Here ###

    #定义测试集的Labels

    hwLabels = []

    #返回trainingDigits目录下的文件名

    trainingFileList = listdir('kNN_hand_writing/trainingDigits')

    #返回文件夹下文件的个数

    m = len(trainingFileList)

    #初始化训练的Mat矩阵,训练集

    trainingMat = np.zeros((m, 1024))

    #从文件名中解析出训练集的类别

    for i in range(m):

        #获得文件的名字

        fileNameStr = trainingFileList[i]

        #获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        #将获得的类别添加到hwLabels中

        hwLabels.append(classNumber)

        #将每一个文件的1x1024数据存储到trainingMat矩阵中

        trainingMat[i,:] = img2vector('kNN_hand_writing/trainingDigits/%s' % (fileNameStr))

    #返回标签数组和训练数据集

    #构建kNN分类器

    neigh =KNN(n_neighbors = 3, algorithm = 'auto')

    #拟合模型, trainingMat为训练矩阵,hwLabels为对应的标签

    neigh.fit(trainingMat, hwLabels)

    #返回testDigits目录下的文件列表

    testFileList = listdir('kNN_hand_writing/testDigits')

   def handwritingClassTest():

   

    ### Start Code Here ###

    #定义测试集的Labels

    hwLabels = []

    #返回trainingDigits目录下的文件名

    trainingFileList = listdir('kNN_hand_writing/trainingDigits')

    #返回文件夹下文件的个数

    m = len(trainingFileList)

    #初始化训练的Mat矩阵,训练集

    trainingMat = np.zeros((m, 1024))

    #从文件名中解析出训练集的类别

    for i in range(m):

        #获得文件的名字

        fileNameStr = trainingFileList[i]

        #获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        #将获得的类别添加到hwLabels中

        hwLabels.append(classNumber)

        #将每一个文件的1x1024数据存储到trainingMat矩阵中

        trainingMat[i,:] = img2vector('kNN_hand_writing/trainingDigits/%s' % (fileNameStr))

    #构建kNN分类器

    neigh = kNN(n_neighbors = 3, algorithm = 'auto')

    #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签

    neigh.fit(trainingMat, hwLabels)

    #返回testDigits目录下的文件列表

    testFileList = listdir('kNN_hand_writing/testDigits')

    #错误检测计数

    errorCount = 0.0

    #测试数据的数量

    mTest = len(testFileList)

    #从文件中解析出测试集的类别并进行 分类测试

    for i in range(mTest):

        #获得文件的名字

        fileNameStr = testFileList[i]

        #获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        #获得测试集的1x1024向量,用于训练

        vectorUnderTest = img2vector('kNN_hand_writing/testDigits/%s' % (fileNameStr))

        #获得预测结果

        classifierResult = neigh.predict(vectorUnderTest)

       

    ### End Code Here ###

   

        print(fileNameStr+"分类返回结果为%d\t真实结果为%d" % (classifierResult, classNumber))

        if(classifierResult != classNumber):

            errorCount += 1.0

    print("总共错了%d个数据\n错误率为%f%%" % (errorCount, errorCount/mTest))

最后,我们来验证一下我们的kNN手写字识别算法的准确性,测试使用的数据集位于目录testDigits下。测试的流程为:依次读取testDigits目录下的每个文件,根据文件中的数据,使用分类函数classify0确定其分类,并和其真实分类进行对比,如果一致,表示分类正确,如果不一致,表示分类错误。统计错误的数量,计算错误率,错误率为分类出错的数量除以测试集中测试数据总量。

编写kNNHandWrittenTest函数测试基于kNN算法实现的手写字识别系统准确性。

errorCount变量用于记录分类出错的样本数量,并获取标签数据和训练集数据,使用测试集数据进行测试。

依次读取testDigits目录下的每个文件,根据文件名称获取其真实分类数字,然后使用classify0函数基于kNN算法进行分类,对比两个分类结果。如果不一致,就对errorCount自增1。

def handwritingClassTest():

   

    ### Start Code Here ###

    #定义测试集的Labels

    hwLabels = []

    #返回trainingDigits目录下的文件名

    trainingFileList = listdir('kNN_hand_writing/trainingDigits')

    #返回文件夹下文件的个数

    m = len(trainingFileList)

    #初始化训练的Mat矩阵,训练集

    trainingMat = np.zeros((m, 1024))

    #从文件名中解析出训练集的类别

    for i in range(m):

        #获得文件的名字

        fileNameStr = trainingFileList[i]

        #获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        #将获得的类别添加到hwLabels中

        hwLabels.append(classNumber)

        #将每一个文件的1x1024数据存储到trainingMat矩阵中

        trainingMat[i,:] = img2vector('kNN_hand_writing/trainingDigits/%s' % (fileNameStr))

    #构建kNN分类器

    neigh = kNN(n_neighbors = 3, algorithm = 'auto')

    #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签

    neigh.fit(trainingMat, hwLabels)

    #返回testDigits目录下的文件列表

    testFileList = listdir('kNN_hand_writing/testDigits')

    #错误检测计数

    errorCount = 0.0

    #测试数据的数量

    mTest = len(testFileList)

    #从文件中解析出测试集的类别并进行 分类测试

    for i in range(mTest):

        #获得文件的名字

        fileNameStr = testFileList[i]

        #获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        #获得测试集的1x1024向量,用于训练

        vectorUnderTest = img2vector('kNN_hand_writing/testDigits/%s' % (fileNameStr))

        #获得预测结果

        classifierResult = neigh.predict(vectorUnderTest)

       

    ### End Code Here ###

   

        print(fileNameStr+"分类返回结果为%d\t真实结果为%d" % (classifierResult, classNumber))

        if(classifierResult != classNumber):

            errorCount += 1.0

    print("总共错了%d个数据\n错误率为%f%%" % (errorCount, errorCount/mTest))

由于基于sklearn提供的kNN算法,修改kNNHandWrittenTest函数,将调用classify0的部分逻辑修改为sklearn提供的kNN接口。两个版本的其他部分的逻辑是完全一样的。具体修改的部分如下图红色框中的代码所示:

from sklearn.neighbors import KNeighborsClassifier as kNN

def handwritingClassTest():

   #测试集的Labels

    hwLabels = []

    #返回trainingDigits目录下的文件名

    trainingFileList = listdir('kNN_hand_writing//trainingDigits')

    #返回文件夹下文件的个数

    m = len(trainingFileList)

    #初始化训练的Mat矩阵,测试集

    trainingMat = np.zeros((m, 1024))

    #从文件名中解析出训练集的类别

    for i in range(m):

        #获得文件的名字

        fileName_Str = trainingFileList[i]

        #获得分类的数字

        classNumber = int(fileName_Str.split('_')[0])

        #将获得的类别添加到hwLabels中

        hwLabels.append(classNumber)

        #将每一个文件的1x1024数据存储到trainingMat矩阵中

        trainingMat[i,:] = img2vector('kNN_hand_writing//trainingDigits/%s' % (fileName_Str))

    #构建kNN分类器

    neigh = kNN(n_neighbors = 3, algorithm = 'auto')

    #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签

    neigh.fit(trainingMat, hwLabels)

    #返回testDigits目录下的文件列表

    testFileList = listdir('kNN_hand_writing/testDigits')

    #错误检测计数

    errorCount = 0.0

    #测试数据的数量

    mTest = len(testFileList)

    ### End Code Here ###

   

   

    #从文件中解析出测试集的类别并进行分类测试

    for i in range(mTest):

        #获得文件的名字

        fileNameStr = testFileList[i]

        #获得分类的数字

        classNumber = int(fileNameStr.split('_')[0])

        #获得测试集的1x1024向量,用于训练

        vectorUnderTest = img2vector('kNN_hand_writing/testDigits/%s' % (fileNameStr))

        #获得预测结果

        classifierResult = neigh.predict(vectorUnderTest)

        print(fileNameStr+"分类返回结果为%d\t真实结果为%d" % (classifierResult, classNumber))

        if(classifierResult != classNumber):

            errorCount += 1.0

    print("总共错了%d个数据\n错误率为%f%%" % (errorCount, errorCount/mTest))

五、实验结论

  1.掌握内容

通过本实验,你应该至少掌握了机器学习的以下几点:

kNN算法的数学原理

kNN算法的步骤流程。

基于Python实现kNN算法。

使用kNN算法解决手写字识别问题。

学会使用sklearn提供的kNN算法。

知道怎样查阅sklearn的使用手册。

学会使用sklearnkNN算法解决手写字识别问题。

2.kNN算法的优缺点

优点:

简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;

可用于数值型数据和离散型数据;

训练时间复杂度为O(n);无数据输入假定;

对异常值不敏感。

缺点:

计算复杂性高;空间复杂性高;

样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);

一般数值很大的时候不用这个,计算量太大。但是单个样本又不能太少,否则容易发生误分。

最大的缺点是无法给出数据的内在含义。

六、教师评议

    

成绩或评语:                              指导教师签名:

                                                                      2022 3 31

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aaron_Liu0730

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值