本节实验我们将利用k近邻算法对手写数字进行识别,通过python命令行构建k近邻算法函数,输入实验提供的数据,进行数据分类,查看分类结果和错误率。通过本实验的学习,我们将掌握k近邻算法的基本原理和及其分类过程等内容
实验时长:45分钟
主要步骤:
数据准备
Anaconda环境部署
编写文本转换向量函数
编写k近邻分类器函数
编写调用测试函数
虚拟机数量:1
系统版本:CentOS 7.5
Python 3.5
从文本文件中解析和导入数据
k近邻
6.1准备工作
6.1.1准备数据集
6.1.2拷贝并解压数据
[zkpk@master ~]$ cd experiment/KNN
[zkpk@master KNN]$ unzip digit.zip
6.1.3数据介绍:解压完成后digits目录下有两个文件夹,分别是:
6.1.3.1trainingDigits
:训练数据,1935个文件,每个数字大约200个文件。
6.1.3.2testDigits
:测试数据,947个文件,每个数字大约100个文件。
[zkpk@master KNN]$ cd digits
[zkpk@master digits]$ ls
6.1.3.3每个文件中存储一个手写的数字,文件的命名类似`0_7.txt`,第一个数字`0`表示文件中的手写数字是0,后面的`7`是序号。
6.1.3.4我们使用目录trainingDigits中的数据训练分类器,使用目录testDigits中的数据测试分类器的效果。两组数据没有重叠,你可以检查一下这些文件夹的文件是否符合要求。根据这些数据我们开始实现KNN算法。
6.1.3.5该数据原本是32*32的黑白图片数据,这里为了方便理解,我们提供的是由图像转换为的文本格式数据,如下
6.2Anaconda环境安装部署
6.2.1Anaconda是一个用于科学计算的Python发行版,支持 Linux, Mac, Windows系统,提供了包管理与环境管理的功能,可以很方便地解决多版本python并存、切换以及各种第三方包安装问题。Anaconda利用工具/命令conda来进行package和environment的管理,并且已经包含了Python和相关的配套工具
6.2.2切换到root用户(密码为zkpk),安装Anaconda包依赖bzip2包,如果确认已安装则可跳过此步骤
[zkpk@master digits]$ cd
[zkpk@master ~]$ su
[root@master zkpk]$ yum install -y bzip2
6.2.3回到zkpk用户,从公共目录下拷贝Anaconda软件包到zkpk家目录下
[root@master zkpk]$ exit
[zkpk@master ~]$ cd /home/zkpk/tgz
[zkpk@master tgz]$ pwd
/home/zkpk/tgz
[zkpk@master tgz]$ cp Anaconda3-4.0.0-Linux-x86_64.sh ~/
[zkpk@master tgz]$ cd
6.2.4使用命令安装部署
6.2.4.1使用bash安装Anaconda,看到如下提示,回车继续
[zkpk@master ~]$ bash Anaconda3-4.0.0-Linux-x86_64.sh
6.2.4.2出现许可证界面输入yes
6.2.4.3看到如下提示,继续回车确定,指定安装目录,开始安装
6.2.4.4安装完成会提示配置Anaconda 的环境变量,输入yes即可
6.2.4.5使得添加进去的环境变量生效
[zkpk@master ~]$ source ~/.bashrc
6.2.4.7这里需要配置相关包的下载源,我们配置国内镜像源,从而加快下载软件的速度;执行如下命令即可
[zkpk@master ~]$conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
[zkpk@master ~]$conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
[zkpk@master ~]$conda config --set show_channel_urls yes
6.2.4.8创建实验需要的虚拟环境,使用如下命令,表示创建一个名字为“zkbc”的python3.5的虚拟环境
[zkpk@master ~]$ conda create -n zkbc python=3.5
6.2.4.10进入虚拟环境,此时我们就进入了一个与本机环境隔离的python环境中
[zkpk@master ~]$ source activate zkbc
6.2.4.11如何退出虚拟环境
(zkbc) [zkpk@master ~]$ source deactivate
6.3开始代码编写
6.3.1进入虚拟环境中
[zkpk@master ~]$ source activate zkbc
6.3.2利用conda安装实验需要的安装包,期间输入y让安装继续,安装完成后,进入python命令行中;安装过程中,输入y让安装继续
(zkbc)[zkpk@master ~]$ conda install numpy
(zkbc)[zkpk@master ~]$ python
6.3.3导入实验需要用到的相关包
>>>from os import listdir
>>>import operator
>>>import numpy as np
6.3.4我们自定义一个将数据处理成一个向量的函数tovector,该函数首先创建了一个1*1024的Numpy数组returnVect,然后打开指定的文件路径下的数据文件,循环读取文件的前32行,并将每行的前32列的值存储在数组returnVect中,这里其实就是将每一个数据文件转换成一个向量。(方法编写完成后需要回车两次,才能退出该方法编写,继续编写下面的代码)
>>>def tovector(filename):
... # 创建向量
... returnVect = np.zeros((1,1024))
... # 打开数据文件,读取每行内容
... fr = open(filename)
... for i in range(32): # 因为文件共32行
... # 读取每一行
... lineStr = fr.readline()
... # 将每行前 32 字符转成 int 存入向量
... for j in range(32): # 因为文件每行,共32个字符
... returnVect[0,32*i+j] = int(lineStr[j])
... return returnVect
6.3.5KNN算法的核心是:计算“距离”,这里我们定义KNN算法实现函数KNNAlg,函数的参数包括:
6.3.5.1inputvector:用于分类的输入向量
6.3.5.2trainDataSet:输入的训练样本集
6.3.5.3labels:样本数据的类标签向量
6.3.5.4k:用于选择最近邻居的数目
6.3.6算法实现过程为:
6.3.6.1计算已知类别数据集中的点与当前点之间的距离;
6.3.6.2按照距离递增次序排序;
6.3.6.3选取与当前点距离最小的k个点;
6.3.6.4确定前k个点所在类别的出现频率;
6.3.6.5返回前k个点出现频率最高的类别作为当前点的预测分类
>>>def KnnAlg(inputvector, trainDataSet, labels, k):
... # 获取样本数据数量
... trainDataSize = trainDataSet.shape[0]
... # 矩阵运算,计算测试数据与每个样本数据对应数据项的差值
... diffMat = np.tile(inputvector, (trainDataSize,1)) - trainDataSet
... # sqDistances 上一步骤结果平方和
... sqDiffMat = diffMat**2
... sqDistances = sqDiffMat.sum(axis=1)
... # 取平方根,得到距离向量
... distances = sqDistances**0.5
... # 按照距离从低到高排序,返回对应元素的索引
... sortedDistIndicies = distances.argsort()
... classCount={}
... # 依次取出最近的样本数据
... for i in range(k):
... # 记录该样本数据所属的类别
... voteIlabel = labels[sortedDistIndicies[i]]
... classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
... # 对类别出现的频次进行排序,从高到低
... sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
... # 返回出现频次最高的类别
... return sortedClassCount[0][0]
6.3.7测试算法:使用k近邻算法识别手写数字
6.3.8我们已经定义好了能够将数据处理成分类器识别的函数,接下来我们定义测试函数handwritingClassTest读取数据文件并将数据输入处理函数,再输入到分类器函数中,检测分类器的执行效果
6.3.9测试函数的步骤:
6.3.9.1读取训练数据(手写图片数据)到向量,从数据文件名中提取类别标签列表(每个向量对应的真实的数字)
6.3.9.2读取测试数据到向量,并从数据文件名中提取类别标签
6.3.9.3执行KNN算法对测试数据进行测试,得到分类结果
6.3.9.4与实际的类别标签进行对比,记录分类错误率
6.3.9.5打印每个数据文件的分类数据及错误率作为最终的结果
>>>def handwritingClassTest():
... # 定义样本数据的类标签列表
... hwLabels = []
... # 读取训练数据文件列表
... trainingFileList = listdir('/home/zkpk/experiment/KNN/digits/trainingDigits')
... m = len(trainingFileList)
... # 初始化训练数据矩阵(M*1024)
... trainingMat = np.zeros((m,1024))
... # 依次读取所有训练数据到数据矩阵
... for i in range(m):
... # 提取文件名中的数字
... fileNameStr = trainingFileList[i]
... fileStr = fileNameStr.split('.')[0]
... classNumStr = int(fileStr.split('_')[0])
... hwLabels.append(classNumStr)
... # 将训练数据存入矩阵
... trainingMat[i,:] = tovector('/home/zkpk/experiment/KNN/digits/trainingDigits/%s' % fileNameStr)
... # 读取测试数据列表
... testFileList = listdir('/home/zkpk/experiment/KNN/digits/testDigits')
... # 初始化错误率
... errorCount = 0.0
... mTest = len(testFileList)
... # 循环测试每个测试数据文件
... for i in range(mTest):
... # 提取文件名中的数字标识
... fileNameStr = testFileList[i]
... fileStr = fileNameStr.split('.')[0]
... classNumStr = int(fileStr.split('_')[0])
... # 调用tovector函数提取数据向量
... vectorUnderTest = tovector('/home/zkpk/experiment/KNN/digits/testDigits/%s' % fileNameStr)
... # 调用KnnAlg分类器函数对测试数据文件进行分类
... classifierResult = KnnAlg(vectorUnderTest, trainingMat, hwLabels, 3)
... # 打印KNN算法分类结果和真实的分类
... print ("the classifier came back with: %d, the real answer is: %d" %(classifierResult, classNumStr))
... # 判断KNN算法结果是否准确
... if (classifierResult != classNumStr): errorCount += 1.0
... # 打印错误率
... print("\nthe total number of errors is: %d" % errorCount)
... print("\nthe total error rate is: %f" % (errorCount/float(mTest)))
6.3.10至此我们需要定义的函数已经定义完成,共包括文本转换向量函数tovector、KNN分类器算法KnnAlg、测试算法handwritingClassTest
6.3.11最后我们调用测试函数handwritingClassTest,查看终端返回的分类结果
>>>handwritingClassTest()
6.3.12可以看到分类错误的有11个,错误比率约为1.16%,这个错误率已经比较低了,而在k近邻算法中改变变量k的值、修改函数handwritingClassTest随机选取训练样本、改变训练样本的数目,都会对k近邻算法的错误率产生影响,感兴趣的话可以改变这些变量值,观察错误率的变化
本实验使用k近邻算法,对手写数字图片转换成的文本数据进行分类预测。通过本节实验我们应当掌握了k近邻算法的基本原理及其分类过程,我们可以看到k近邻算法的核心是“距离”计算,而另一个影响分类准确的关键因素就是k值的选择,大家也可以尝试改变本实验中给定的k值,对比不同k值下的分类准确度。
分类错误的有11个,错误比率约为1.16%,这个错误率已经比较低了,而在k近邻算法中改变变量k的值、修改函数handwritingClassTest随机选取训练样本、改变训练样本的数目,都会对k近邻算法的错误率产生影响,感兴趣的话可以改变这些变量值,观察错误率的变化