一 什么是KNN算法
KNN(K-最近邻)算法是一种用于分类和回归的基本机器学习算法。在KNN分类中,当需要对新数据点进行分类时,算法会查找与该数据点最相似的K个训练数据点(最近邻),然后通过这K个最近邻的投票来确定新数据点所属的类别。在KNN回归中,算法会根据最近邻点的值来对新数据点进行预测。
KNN算法的工作原理很简单,但在实践中却十分有效。它无需事先训练模型,而是存储训练集,并且在进行预测时需要对测试数据集进行比较。KNN算法的性能受到K值的选择、距离计算方法和特征标准化等因素的影响。
二 KNN算法优缺点
KNN算法有以下优缺点:
优点:
- 简单易理解:KNN算法非常直观和简单,易于理解和实现。
- 无需训练过程:KNN算法属于懒惰学习算法,不需要显式的训练过程,新的数据点可以随时添加到已知数据集中。
- 适用于多类别问题:KNN算法适用于多分类问题,可以处理多类别的数据集。
- 对异常值不敏感:KNN对异常值不敏感,因为它在预测时主要考虑的是周围的邻居。
缺点:
- 计算复杂度高:在预测阶段,KNN算法需要计算测试数据点与所有训练数据点的距离,这可能会导致计算复杂度较高。
- 存储开销大:KNN算法需要存储全部的训练数据,当训练集很大时,会产生较大的存储开销。
- 需要选择合适的K值:选择合适的K值对KNN算法的性能影响很大,选择不当会导致预测的不准确性。
- 对特征值的量纲敏感:KNN算法对特征值的量纲敏感,需要进行特征标准化以获得更好的性能。
因此,在应用KNN算法时,需要考虑这些优缺点并谨慎地选择合适的参数和数据预处理方法。
三 案例背景
我的朋友海伦一直使用在线约会网站寻找合适自己的约会对象。尽管约会网站会推荐不同的人选,但她并不是喜欢每一个人。经过一番总结,她发现曾交往过三种类型的人: (1)不喜欢的人; (2)魅力一般的人; (3)极具魅力的人; 尽管发现了上述规律,但海伦依然无法将约会网站推荐的匹配对象归入恰当的分类,她觉得可以在周一到周五约会那些魅力一般的人,而周末则更喜欢与那些极具魅力的人为伴。海伦希望我们的分类软件可以更好地帮助她将匹配对象划分到确切的分类中。此外,海伦还收集了一些约会网站未曾记录的数据信息,她认为这些数据更助于匹配对象的归类。
四 案例分析
当涉及KNN算法时,数据集的特征通常包括约会对象的各种属性,比如兴趣爱好、外貌特征等。标签则是该约会对象所属的类型,比如"不喜欢的人"、"魅力一般的人"和"极具魅力的人"。
下面是一个简单的假设数据集,其中包含一些特征和相应的标签:
通过使用KNN算法,我们可以根据这些特征来预测新的约会对象所属的类型。在这个案例下,KNN算法会根据已知的特征数据来寻找与新约会对象最相似的K个训练数据点,然后通过这K个最近邻的投票来确定新约会对象所属的类型。
假设我们有一个新的约会对象,我们会将其特征输入KNN算法,然后算法会输出该约会对象所属的类型,从而为海伦推荐合适的约会对象。
五 代码实现
from numpy import *
import operator
#使用KNN算法为每组数据分类
def classify(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0]
diffMat=tile(inX,(dataSetSize,1))-dataSet
sqDiffMat=diffMat**2
sqDistance=sqDiffMat.sum(axis=1)
distances=sqDistance**0.5
sortedDistIndicies=distances.argsort()
classCount={}
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
#从文本中解析数据
def file2matrix(filename):
with open(filename) as file:
#求得样例的个数
arrayOLines=file.readlines()
numberOfLines=len(arrayOLines)
#求KNN算法所使用的数据集dataSet和类别labels
returnMat=zeros((numberOfLines,3)) #初始化一个数据集矩阵
classLabelVector=[] #初始化一个类别向量
index=0
for line in arrayOLines:
line=line.split()
returnMat[index,:]=line[0:3] #矩阵的每一行表示一个测试样例
classLabelVector.append(int(line[-1])) #文本中每行的最后一个数据作为该样例所属的类别
index+=1
return returnMat,classLabelVector
#归一化特征值
def autoNorm(dataSet):
minVals=dataSet.min(0) #将每列最小的特征值放在minVals中,参数0表示使得函数可以从列中选取最小值
maxVals=dataSet.max(0) #将每列最大的特征值放在maxVals中
ranges=maxVals-minVals
normalDataSet=zeros(shape(dataSet))
m=dataSet.shape[0] #样本数量
normalDataSet=dataSet-tile(minVals,(m,1))
normalDataSet=normalDataSet/tile(ranges,(m,1))
return normalDataSet,ranges,minVals
#利用分类器针对数据进行测试(数据的前10%用做测试数据,剩下的数据用作训练数据),并计算错误率
def Test():
hoRatio=0.10
dataSet,labels=file2matrix('dataTestSet') #从文件中读取数据集和每组数据所属分类
normDataSet,ranges,minVals=autoNorm(dataSet) #对数据集进行归一化处理
m=normDataSet.shape[0] #一共有多少组数据
numTest=int(m*hoRatio) #用于测试的数据组数
errorCount=0.0
for i in range(numTest):
classifierResult=classify(dataSet[i,:],dataSet[numTest:m,:],labels[numTest:m],3)
#print('classifier result:%d, real answer:%d'%(classifierResult,labels[i]))
if (classifierResult!=labels[i]):
errorCount+=1.0
print('the total error rate is: %f'%(errorCount/float(numTest)))
#估计约会对象的类型
def classifyPerson():
resultList=['not at all','in small doses','in large doses']
#输入约会对象的一些特征
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 year:'))
#从文本文件中解析数据
dataSet,labels=file2matrix('dataTestSet')
#特征归一化处理
normMat,ranges,minVals=autoNorm(dataSet)
inArr=array([ffMiles,percentTats,iceCream]) #输入的约会对象的特征
intX=(inArr-minVals)/ranges #对输入的数据进行归一化处理
classifierResult=classify(intX,normMat,labels,3)#判断约会对象的类型
print('you will probably like this person: ',resultList[classifierResult-1])
Test() #测试算法错误率的函数
classifyPerson() #使用这个算法为海莉判断约会对象的类型
六 运行结果
the total error rate is: 0.240000
percentage of time spent playing video games:10
frequent flier miles earned per year:10000
liters of ice cream consumed per year:0.5
you will probably like this person: in small doses