knn:给定数据集,对于新的输入实例,在训练集中找到与该实例最邻近的k个实例,k的实例的多数属于某个类,则新的输入实例就属于某个类。
knn三个基本要素:k值的选择,距离度量,分类决策规则
算法实现:
1.计算已知类型数据集中的点与当前点的距离
2.按照距离递增次序进行排序。
3.选取与当前点距离最小的k个点。
4.确定前k个点所在类别出现的频率。
5.返回出现频率最高的类别作为预测分类。
首先进行一个knn对约会网站的实现
import numpy as np
import operator #运算符模块
def createDataSet():
group=np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels=['A','A','B','B']
return group,labels
def classify0(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] #训练集的样本数
# =============================================================================
# 计算输入向量和所有训练样本之间的距离 二范数
# =============================================================================
diffMat=np.tile(inX,(dataSetSize,1)) - 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 #统计前k个各个label的个数
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1)
,reverse=True) #将字典分解为元组 运用itemgetter方法按照第二个元素的次序对元组进行排序,为逆序
return sortedClassCount[0][0]
# =============================================================================
# 将文本记录转换到numpy的程序
# =============================================================================
def file2matrix(filename):
fr=open(filename)
arrayOLines=fr.readlines() #一行一行读进来
numberOfLines=len(arrayOLines) #行数
returnMat=np.zeros((numberOfLines,3)) #构造1000*3的0矩阵
classLabelVector=[]
index=0
for line in arrayOLines:
line=line.strip() #删除开头和结尾的字符
listFromLine=line.split('\t') #在\t处进行切片操作,得到了4个值
returnMat[index,:]=listFromLine[0:3] #第index行填入获取到的4个中的前3个值
classLabelVector.append(int(listFromLine[-1]))
index+=1
return returnMat,classLabelVector
# =============================================================================
# 特征进行归一化
# =============================================================================
def autoNorm(dataSet):
minVals=dataSet.min(0) #每列的最小值
maxVals=dataSet.max(0) #每列的最大值
ranges=maxVals-minVals
normDataSet=np.zeros(dataSet.shape)
m=dataSet.shape[0] #行数
normDataSet=dataSet-np.tile(minVals,(m,1)) #np.tile(minVals,(m,1))代表这一个m行的矩阵,值为minVals
normDataSet=normDataSet/np.tile(ranges,(m,1))
return normDataSet,ranges,minVals
# =============================================================================
#画图
# import matplotlib
# import matplotlib.pyplot as plt
# fig=plt.figure()
# ax=fig.add_subplot(111)
# ax.scatter(returnMat[:,1],returnMat[:,2]) 训练集的第二列 第三列数据
# plt.show()
# =============================================================================
# =============================================================================
# 测试代码
# =============================================================================
def datingClassTest():
hoRatio=0.1
datingDataMat,datinglabels=file2matrix('datingTestSet2.txt')
normMat,ranges,minVals=autoNorm(datingDataMat)
m=normMat.shape[0] #样本数
numTestVecs=int(m*hoRatio)
errorCount=0
for i in range(numTest):
classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],datinglabels[numTestVecs:m],3)
print('the classifier came back with: %d,the real answer is: %d' %(classifierResult,datinglabels[i]))
if (classifierResult != datinglabels[i]):
errorCount+=1
print('the total error rate is :%f' % (errorCount/float(numTestVecs)))
if __name__ == '__main__':
datingClassTest()
接下来进行了一个knn对于手写数字识别的实现
import numpy as np
import operator #运算符模块
from os import listdir #返回指定的文件夹包含的文件或文件夹的名字的列表
def classify0(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] #训练集的样本数
# =============================================================================
# 计算输入向量和所有训练样本之间的距离 二范数
# =============================================================================
diffMat=np.tile(inX,(dataSetSize,1)) - 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 #统计前k个各个label的个数
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1)
,reverse=True) #将字典分解为元组 运用itemgetter方法按照第二个元素的次序对元组进行排序,为逆序
return sortedClassCount[0][0]
# =============================================================================
# 将图像转化为向量 32*32到1*1024
# =============================================================================
def img2vector(filename):
returnVect=np.zeros((1,1024))
fr=open(filename)
for i in range(32):
lineStr=fr.readline()
for j in range(32):
returnVect[0,32*i+j]= int(lineStr[j])
return returnVect
# =============================================================================
# 测试代码
# =============================================================================
def handwritingClassTest():
hwLabels = []
trainingFileList = listdir('trainingDigits') #load the training set
m = len(trainingFileList)
trainingMat = np.zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] #take off .txt
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
testFileList = listdir('testDigits') #iterate through the test set
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0] #take off .txt
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
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)))
if __name__ == '__main__':
handwritingClassTest()
优点:精度高,对于异常值不敏感,无数据输入假定。
缺点:计算复杂度高,空间复杂度高。
可以用kd树来对传统的knn算法进行优化。