K最近邻(k-Nearest Neighbor,KNN)分类算法可以说是最简单的机器学习算法了。它采用测量不同特征值之间的距离方法进行分类。它的思想很简单:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
其算法描述如下:
1)计算已知类别数据集中的点与当前点之间的距离;
2)按照距离递增次序排序;
3)选取与当前点距离最小的k个点;
4)确定前k个点所在类别的出现频率;
5)返回前k个点出现频率最高的类别作为当前点的预测分类。
对于机器学习而已,Python需要额外安装三件宝,分别是Numpy,scipy和Matplotlib。前两者用于数值计算,后者用于画图。安装很简单,直接到各自的官网下载回来安装即可。安装程序会自动搜索我们的python版本和目录,然后安装到python支持的搜索路径下。反正就python和这三个插件都默认安装就没问题了。
一般实现一个算法后,我们需要先用一个很小的数据库来测试它的正确性,否则一下子给个大数据给它,它也很难消化,而且还不利于我们分析代码的有效性。
补充用python实现的代码,要给python装numpy和matplotlib库,建议直接装anaconda,装好了anaconda默认安装了spyder,里面集成了这两个库,比较方便。
首先,我们新建一个kNN.py脚本文件,文件里面包含两个函数,一个用来生成小数据库,一个实现kNN分类算法。代码如下:
# -*- coding: utf-8 -*-
from numpy import * def createDataSet():#创建一个很小的数据库 group=array([[1.0,0.9],[1.0,1.0],[0.1,0.2],[0.0,0.1]]) labels=['A','A','B','B'] return group,labels #[1.2,1.0] def kNNClassify(newInput,dataset,labels,k):#knn算法核心 #先求数据库中每个点到所求点之间的距离 numSamples=dataset.shape[0] #获取数据库的行数 diff=tile(newInput,(numSamples,1))-dataset#使用tile函数迭代创建一个numSample行1列的array与dataset做差 squaredDiff=diff**2#diff中的每一个数都平方 squaredDist=sum(squaredDiff,axis=1)#每一行两数求和 distance=squaredDist**0.5#再开方 #再对距离进行排序 sortedDistIndices=argsort(distance)#argsort函数对distance中元素从小到大排序,返回序号 classCount={} #统计距离小于等于k的每个点的类别 for i in xrange(k): voteLabel=labels[sortedDistIndices[i]] classCount[voteLabel]=classCount.get(voteLabel,0)+1 maxCount=0 #找出离所求点最近的k个点中最多的类别 for key,value in classCount.items(): if maxCount<value: maxCount=value maxIndex=key #返回所求点的类型,算法到此结束 return maxIndex
然后我们在命令行中或在建一个python文件这样测试即可:
import kNN from numpy import * dataSet,labels=kNN.createDataSet() testX=array([0,0.05]) k=3 maxIndex=kNN.kNNClassify(testX,dataSet,labels,3) print maxIndex
运行程序:此时点[0,0.05]最接近B类。
应用:这里我们用kNN来分类一个大点的数据库,包括数据维度比较大和样本数比较多的数据库。这里我们用到一个手写数字的数据库,可以到这里下载。这个数据库包括数字0-9的手写体。每个数字大约有200个样本。每个样本保持在一个txt文件中。手写体图像本身的大小是32x32的二值图,转换到txt文件保存后,内容也是32x32个数字,0或者1.
数据库解压后有两个目录:目录trainingDigits存放的是大约2000个训练数据,testDigits存放大约900个测试数据。
编写kNN2.py:
# -*- coding: utf-8 -*- from numpy import * import os def kNNClassify(newInput,dataset,labels,k):#knn算法核心 #先求数据库中每个图像与所要分类图像像素值差的平方再开方,用这种计算方法表示距离(相似度)俗称欧氏距离 numSamples=dataset.shape[0] #获取数据库的行数(即文件夹下的文件数) diff=tile(newInput,(numSamples,1))-dataset#使用tile函数迭代创建一个numSample行1列的array与dataset做差 squaredDiff=diff**2#diff中的每一个数都平方 squaredDist=sum(squaredDiff,axis=1)#每一行的数求和 distance=squaredDist**0.5#再开方 #再对距离进行排序 sortedDistIndices=argsort(distance) classCount={} #统计距离为k的每个图像的类别(即统计相似度最小的k个图像所表示的数字) for i in xrange(k): voteLabel = labels[sortedDistIndices[i]] classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 maxCount=0 #找出离所求图像类别最近的k个图像中最多的类别 for key,value in classCount.items(): if maxCount<value: maxCount=value maxIndex=key #返回所求图像的类型(类型即数字) return maxIndex #函数img2vector把一张32*32的图像转化成一行向量imgVector def img2vector(filename): rows=32 cols=32 imgVector=zeros((1,rows*cols)) fileIn=open(filename) for row in xrange(rows): lineStr=fileIn.readline() for col in xrange(cols): imgVector[0,row*32+col]=int(lineStr[col]) return imgVector #函数loadDataSet从文件夹中加载多个文件数据,python对文件数据流加载到内存的操作很方便,这里的代码可以仔细理解加记忆一下 def loadDataSet(): dataSetDir='/home/chao/Desktop/python_work/knn/' trainingFileList=os.listdir(dataSetDir+'trainingDigits') numSamples=len(trainingFileList) train_x=zeros((numSamples,1024))#使用zeros函数为train_x分配numSamples行,每行1024列,每行为一个图像转化后的数据,总共numSamples行 train_y=[]#用来存放每个图像的真实值 for i in xrange(numSamples): filename=trainingFileList[i] train_x[i,:]=img2vector(dataSetDir+'trainingDigits/%s'%filename) label=int(filename.split('_')[0]) train_y.append(label) testingFileList=os.listdir(dataSetDir+'testDigits') numSamples=len(testingFileList) test_x=zeros((numSamples,1024))#同train_x,但这里表示的是测试图像文件的 test_y=[] for i in xrange(numSamples): filename=testingFileList[i] test_x[i,:]=img2vector(dataSetDir+'testDigits/%s'%filename) label=int(filename.split('_')[0]) test_y.append(label) return train_x,train_y,test_x,test_y #测试预测准确率 def testHandWritingClass(): print "第一步:加载数据。。。" train_x,train_y,test_x,test_y=loadDataSet() numTestSamples=test_x.shape[0]#返回待测试图像的个数 print "数据加载完成" matchCount=0#用来表示预测正确的图像的个数 #每个待测图像都要调用一次knn预测其值 for i in xrange(numTestSamples): print i predict=kNNClassify(test_x[i],train_x,train_y,3)#这里k=3准确率达98.63%,如果改成k=1的话会达到98.97% if predict==test_y[i]: matchCount+=1 accuracy=float(matchCount)/numTestSamples print matchCount#打印正确预测个数 print "accuracy is:%.2f%%"%(accuracy*100)#打印正确预测准确率
测试非常简单,编写一个main.py:
# -*- coding: utf-8 -*- import kNN2 kNN2.testHandWritingClass()
然后运行main.py观察正确率:
933个预测正确 accuracy is:98.63%