一、K近邻算法概念:
找一个输入数据周围K个样本里大多数的类别,则输入数据就是这个类别。
二:优缺点
优点:
1.简单,易于理解,易于实现,无需估计参数,无需训练;
2.适合对稀有事件进行分类;
3.特别适合于多分类问题(multi-modal,对象具有多个类别标签), kNN比SVM的表现要好。
缺点:
1.该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。
2.惰性算法,计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。时间和空间复杂度都高。
3.可理解性差,无法给出像决策树那样的规则。
适用数据范围:数值型和标称型
三、算法步骤:
输入数据与样本数据集合里的每个样本计算距离,设置一个整数K,从距离里找出前K个,里面最多的类别就是输入数据的类别
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
注:一般距离公式可采用欧式距离,不过不同情景有其他的距离公式选择:
欧氏距离和余弦距离各自有不同的计算方式和衡量特征,因此它们适用于不同的数据分析模型:
1.欧氏距离能够体现个体数值特征的绝对差异,所以更多的用于需要从维度的数值大小中体现差异的分析,如使用用户行为指标分析用户价值的相似度或差异。
2.余弦距离更多的是从方向上区分差异,而对绝对的数值不敏感,更多的用于使用用户对内容评分来区分兴趣的相似度和差异,同时修正了用户间可能存在的度量标准不统一的问题(因为余弦距离对绝对数值不敏感)。
四、常见问题(此部分转载自网络)
1、k值设定为多大?
k太小,分类结果易受噪声点影响;k太大,近邻中又可能包含太多的其它类别的点。(对距离加权,可以降低k值设定的影响)
k值通常是采用交叉检验来确定(以k=1为基准)
经验规则:k一般低于训练样本数的平方根
2、类别如何判定最合适?
投票法没有考虑近邻的距离的远近,距离更近的近邻也许更应该决定最终的分类,所以加权投票法更恰当一些。
3、如何选择合适的距离衡量?
高维度对距离衡量的影响:众所周知当变量数越多,欧式距离的区分能力就越差。
变量值域对距离的影响:值域越大的变量常常会在距离计算中占据主导作用,因此应先对变量进行标准化。
4、训练样本是否要一视同仁?
在训练集中,有些样本可能是更值得依赖的。
可以给不同的样本施加不同的权重,加强依赖样本的权重,降低不可信赖样本的影响。
5、性能问题?
kNN是一种懒惰算法,平时不好好学习,考试(对测试样本分类)时才临阵磨枪(临时去找k个近邻)。
懒惰的后果:构造模型很简单,但在对测试样本分类地的系统开销大,因为要扫描全部训练样本并计算距离。
已经有一些方法提高计算的效率,例如压缩训练样本量等。
6、能否大幅减少训练样本量,同时又保持分类精度?
浓缩技术(condensing)
编辑技术(editing)
机器学习实战里的主要代码部分加了点注释
from numpy import *
from os import listdir
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
#knn,inX测试集,dataSet训练集 ,labels训练集标签,k
def classify0(inX,dataSet,labels,k):
#返回第一维度长度
dataSetSize=dataSet.shape[0]
#inX 列复制1次,行dataSetSize(4)次
# array([[0, 0],
# [0, 0],
# [0, 0],
# [0, 0],])
#然后减去dataset
diffMat=tile(inX,(dataSetSize,1))-dataSet
#平方
sqDiffMat=diffMat**2
#axis=1就是将一个矩阵每一行相加
sqDistances=sqDiffMat.sum(axis=1)
distances=sqDistances**0.5
#返回排序以后的索引值 从小到大
sortedDistIndicies=distances.argsort()
classCount={}
#for循环在训练集里找3个离它最近的点,看他们分类是什么的多 312
for i in range(k):
#找到最近k个点的分类
voteIlabel=labels[sortedDistIndicies[i]]
#从字典里找,找不到就放里这个分类,并且数目+1;有,直接加一
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
#按照字典中第一个位置的值降序排序,找数目最多的分类
# Python3.5中:iteritems变为item
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
#读取数据并简单处理,返回矩阵和类别向量
def file2matrix(filename):
fr=open(filename)
arrayOLines=fr.readlines()
numberOfLines=len(arrayOLines)
#生成若干行,3列的0矩阵
returnMat=zeros((numberOfLines,3))
classLabelVector=[]
index=0
for line in arrayOLines:
line=line.strip()
listFromLine=line.split('\t')
#复制进returnMat
returnMat[index,:]=listFromLine[0:3]
#存储下最后一列的数据,这里是类型
classLabelVector.append(int(listFromLine[-1]))
index+=1
return returnMat,classLabelVector
#归一化,消除某些大数据的影响
def autoNorm(dataSet):
#min(0)是从没列开始找最小
minVals=dataSet.min(0)
maxVals=dataSet.max(0)
ranges=maxVals-minVals
normDataSet=zeros(shape(dataSet))
#读取e的第一维度长度,使用shape[0],获取行数
m=dataSet.shape[0]
#minVals行复制m次,列1次
normDataSet=dataSet-tile(minVals,(m,1))
#在这里是具体特征值相除,矩阵对应位置;而不是两个矩阵相除
normDataSet=normDataSet/tile(ranges,(m,1))
return normDataSet,ranges,minVals
#分类器针对约会网站的测试代码
# def datingClassText():
# hoRatio=0.10
# datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
# normMat,ranges,minVals=autoNorm(datingDataMat)
# m=normMat.shape[0]
# #numTestVecs为测试集数目
# numTestVecs=int(m*hoRatio)
# errorCount=0.0
# for i in range(numTestVecs):
# #normMat一行一行循环作为测试集;训练集用从测试集的行数开始到结尾;标签;K
# 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.0
# print("the total error rate is: %f" %(errorCount/float(numTestVecs)))
# #print ("the errorCount is: %f" %errorCount)
def datingClassTest():
hoRatio = 0.10 #hold out 10%
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #load data setfrom file
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
# #numTestVecs为测试集数目
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
# #normMat一行一行循环作为测试集;训练集用从测试集的行数开始到结尾;标签;K
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.0
print ("the total error rate is: %f" % (errorCount/float(numTestVecs)))
print ("the errorCount is: %f" %errorCount)
#约会网站预测函数
def classifyPerson():
resultList=['not at all','in small doses','in large doses']
#py3 raw_input 是input
percentTats=float(input("percentage of time spent playing video games?"))
ffMiles=float(input("frequent flier miles earned per year?"))
iceCream=float(input("liters of ice cream consumed per years?"))
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
normMat,ranges,minVals=autoNorm(datingDataMat)
inArr=array([ffMiles,percentTats,iceCream])
classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
print("you will probably like this person :",resultList[classifierResult-1])
#2.3
#把图像处理成vector
def img2vector(filename):
returnVect=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')
#返回行数,即文件数
m=len(trainingFileList)
#创建一个m行1024列的矩阵,每行存储一个图像
trainingMat=zeros((m,1024))
#从文件名解析类别数据
for i in range(m):
fileNameStr=trainingFileList[i]
#按照.截取,取第0个
fileStr=fileNameStr.split('.')[0]
#按照_截取,取第0个
classNumStr=int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:]=img2vector('trainingDigits/%s'%fileNameStr)
testFileList=listdir('testDigits')
errorCount=0.0
mTest=len(testFileList)
for i in range(mTest):
fileNameStr=testFileList[i]
fileStr=fileNameStr.split('.')[0]
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 error is: %d" % errorCount)
print("\nthe total error rate is: %f" % (errorCount/float(mTest)))