k近邻算法:
或者说K最近邻(kNN,k-NearestNeighbor)
在一个空间中有许多样本,这时候来了一个新的样本即测试点,那么如何判断这个样本的类别。做法就是求测试点和空间中每个样本的距离,用距离最近的前K个判断。
比如下图新来了一个点,这时候K=3,离它最近的3个点就是一个为正方形,两个为三角形,那么就把新的点判定为三角形。
再比如:
训练集是二维数组[1.0,1.1],[1.0,1.0],[0.1,0],[0,0.1]
标签 labels=[‘A’,‘A’,‘B’,‘B’]
测试集[0.2,0.1]
k=3
首先计算测试集和训练集之间的距离
点[0.2,0.1]与[1.0,1.1]之间的距离计算为:
计算完所有点之间的距离之后从小到大排序,选取前k个距离,对进行遍历,将对应标签加一。如[A:1,B:2]这里就输出A
算法的优点:
1、简单,易于理解,易于实现,无需估计参数,无需训练
2、适合对稀有事件进行分类
3、特别适合于多分类问题(multi-modal,对象具有多个类别标签), kNN比SVM的表现要好
算法的缺点:
1、该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。
2、该方法的另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。
程序实现:
from numpy import *
import operator
def createDataSet(): #创建标签和数据
group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels=['A','A','B','B']
return group,labels
def classifify0(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] # dataSetSize=4 计算距离
diffMat=tile(inX,(dataSetSize,1))-dataSet #tile(inX,(dataSetSize,1)),让inX变为和dataSet一样的类型
sqDiffMat=diffMat**2 #
sqDistances=sqDiffMat.sum(axis=1)
distances=sqDistances**0.5
sortedDistIndicies=distances.argsort()
classCount={}
for i in range(k): #选择距离最小的k个节点
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
sortedCalssCount=sorted(classCount.items(),
key=operator.itemgetter(1),reverse=True)
return sortedCalssCount[0][0]
group,labels=createDataSet()
print(classifify0([1, 0.5], group, labels, 3))
手写数字体识别
图片大小:28×28
为了使用上述分类器,需将图片转换为向量形式1×784
from numpy import *
import operator
from keras.datasets import mnist
import numpy as np
(X_train, Y_train),(X_test, Y_test) = mnist.load_data()
X_train=X_train[0:10000] #取10000个训练
Y_train=Y_train[0:10000]
X_test=X_test[0:100] #取100个测试
Y_test=Y_test[0:100]
aa=X_train[1].reshape(1,784)
def classifify0(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] # dataSetSize=4
diffMat=tile(inX,(dataSetSize,1))-dataSet #tile(inX,(dataSetSize,1)),让inX变为和dataSet一样的类型
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
sortedCalssCount=sorted(classCount.items(),
key=operator.itemgetter(1),reverse=True)
return sortedCalssCount[0][0]
def handwriting(X_train,Y_train,X_test,k):
hwlabels=[]
m=len(X_train)
trainingMat=zeros((m,784))
for i in range(m):
trainingMat[i,:]=X_train[i].reshape(1,784) #将图片转换为向量形式
mTest=len(X_test)
for i in range(mTest):
wordtest=X_test[i].reshape(1,784)
classresult=classifify0(wordtest,trainingMat,Y_train,k)
hwlabels.append(classresult)
#print("result:{},true{}".format(classresult,Y_test[i]))
return hwlabels
k=[10,50,100,150] #取前k个
for i in range(len(k)):
classresult=handwriting(X_train,Y_train,X_test,k[i])
aa=(classresult == Y_test).astype(np.int32)
acc=sum(aa)/len(Y_test)
print(acc)
总结:k近邻算法比较简单和有效,但是需要大量的存储空间,比较耗时