k-近邻算法(kNN)是机器学习中一个相对比较简单的算法。该算法在训练集中数据和标签已知的情况下,输入测试数据,将测试数据的特征与训练集中对应的特征进行相互比较(比如通过欧氏距离),找到训练集中与之最为相似的前K个数据,则该测试数据对应的类别就是K个数据中出现次数最多的那个分类,其算法的描述为:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
以下代码是通过kNN算法实现的手写识别系统。其中每个输入数据为32*32的二值图像,数据存储在txt文件中(32行*32列)。文件命名为诸如”0_0.txt”的形式。其中,”_”左边的0为其实际标签(实际类),右边的0为数据的编号。
import numpy as np
from os import listdir
def readDatatoList(Filename):
f=open(Filename)
List=np.zeros((1,32*32))
for i in range(0,32):
eachLine=f.readline()
for j in range(0,32):
List[0,32*i+j]=int(eachLine[j])
return List
def kNNclassify(dataSet,label,sample,k):
sizeof_dataSet=len(label)
distanceSet=np.zeros((1,sizeof_dataSet))
for i in range(0,sizeof_dataSet):
distance=0
for j in range(0,32*32):
distance+=((dataSet[i][j]-sample[0][j])**2)
distance=distance**0.5
distanceSet[0,i]=distance
sortDistindex=distanceSet[0].argsort()
# print(sortDistindex)
maxVoted=0
classVoted={}
for i in range(0,k):
if(label[sortDistindex[i]] in classVoted):
classVoted[label[sortDistindex[i]]]+=1
else:
classVoted[label[sortDistindex[i]]]=1
if(classVoted[label[sortDistindex[i]]]>maxVoted):
maxVoted=classVoted[label[sortDistindex[i]]]
nearClass=label[sortDistindex[i]]
return nearClass
def handwritingTest():
trainingFileList=listdir('trainingDigits')
trainingSampleNum=len(trainingFileList)
trainingSet=np.zeros((trainingSampleNum,32*32))
label=[]
for i in range(0,trainingSampleNum):
Filename=trainingFileList[i]
trainingSet[i:]=readDatatoList('trainingDigits/%s' %Filename)
label.append(int(Filename.split('_')[0]))
testFileList=listdir('testDigits')
errorNum=0
testSampleNum=len(testFileList)
for i in range(0,testSampleNum):
Filename=testFileList[i]
realClass=int(Filename.split('_')[0])
sample=readDatatoList('testDigits/%s' %Filename)
testedClass=kNNclassify(trainingSet,label,sample,3)
if(realClass!=testedClass):
errorNum+=1
print("Case %d:The real class is:%d, the answer is:%d" %(i,realClass,testedClass))
print("The total number of errors is:%d" %errorNum)
print("The total error rate is: %f" %(errorNum/float(testSampleNum)))
handwritingTest()
测试结果:
为了提高效率,笔者对32*32的图片进行下采样,改成16*16,修改以下代码:
def readDatatoList(Filename):
f=open(Filename)
List=np.zeros((1,16*16))
for i in range(0,16):
qLine=f.readline()
oLine=f.readline()
for j in range(0,16):
List[0,16*i+j]=int(qLine[2*j])+int(qLine[2*j+1])+int(oLine[2*j])+int(oLine[2*j+1])
return List
得到同样的结果:
当然,k值设置不同、计算距离方式的不同、投票方式的不同、甚至训练样本的权重,等等因素,都可能影响正确率和时间效率。kNN的主要缺点是算法思想相对比较简单,时间效率不高(属于平时不训练,临阵磨枪)。