一 、什么是K邻近算法?
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个(一般K是不大于20的整数)最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
![](https://gss2.bdstatic.com/9fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D220/sign=bc08521ad3160924d825a519e406359b/b151f8198618367ad0414a912d738bd4b21ce5b5.jpg)
例如:
有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的圆所标示的数据则是待分类的数据。也就是说,现在, 我们不知道中间那个绿色的数据是从属于哪一类(蓝色小正方形or红色小三角形),下面,我们就要解决这个问题:给这个绿色的圆分类。
如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
工作原理详细解释:一个样本数据集中每个样本都存在标签,也称作训练样本,即我们知道样本集中每一数据与所属分类的对应关系。然后输入没有标签的新数据(测试数据),将新数据的每个特征与样本集中数据对应的特征进行比较,然后用样本集中特征最享受的数据作为新数据的分类标签。
二 、算法的python编程
首先,我们创建名为kNN.py的python模块,代码如下:
# -*- coding: utf-8 -*-
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 classify0(intX,dataSet,labels,k):
dataSetSize=dataSet.shape[0] #计算dataSet行数
diffMat=tile(intX,(dataSetSize,1))-dataSet #tile():对intX按(4,1)进行复制,4代表复制4行,1代表列不变
sqDiffMat=diffMat**2
sqDistances=sqDiffMat.sum(axis=1)#sum(axis=1)按行求和
distances=sqDistances**0.5#计算两个点间的距离
sortedDistIndicies=distances.argsort()#argsort()按升序排序后取索引值
classCount={}
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1#统计类别出现的次数{'A': 1, 'B': 2}
sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
#将无序的字典迭代为有序的元组后,itemgetter(1)取出元组第二个域,并reverse=True按降序排序
return sortedClassCount[0][0]
其中createDataSet()函数是创建数据集和分类标签,classify0()函数是使用K近邻算法将每组数据划分到某个类中,其伪代码为:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。
之后,在Python开发环境输入:
import kNN
group,labels = kNN.createDataSet()
kNN.classify0([0,0],group,lables,3)
便可以得到该样本数据属于B类。
三 、算法的应用
接下来我们通过一个实际的案例来更好的理解K近邻算法:约会网站的配对效果
样本数据集来源于1000行约会数据,主要包含以下三种特征:
(1)每年获得的飞行常客里程数
(2)玩视频游戏所耗时间百分比
(3)每周消费的冰淇淋公升数
首先,在数据添加到分类器前,我们要将数据处理为分类器可以接受的Numpy矩阵形式,输出为训练样本矩阵和类标签向量
def file2matrix(filename):#将分类数据转换为Numpy解析程序,这样便于分类器可接收
fr = open(filename)
numberOfLines = len(fr.readlines()) #读取文件,得到行数
returnMat = zeros((numberOfLines,3)) #先创建一个1000行,3列均为0的返回矩阵,用于接下来存放样本矩阵
classLabelVector = [] #准备类标签数组
fr = open(filename)
index = 0
for line in fr.readlines():
line = line.strip()#去掉首尾空格
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]#取样本数据的前3列,存储到返回矩阵中
classLabelVector.append(int(listFromLine[-1]))#存储类标签到数组中
接着,因为样本特征数据的单位不统一,有的很大,有的很小,所以我们要归一化特征值,将所有特征值转化为(0,1)的数据,公式为newValue=(OldValue-min)/(max-min)
def autoNorm(dataSet):#归一化特征值,将所有特征值转化为(0,1)的数据,公式为newValue=(OldValue-min)/(max-min)
minVals = dataSet.min(0)
maxVals = dataSet.max(0)#取出每列中最大,最小的特征值
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1)) #element wise divide
return normDataSet, ranges, minVals
然后,我们从训练集选取10%的样本数据来测试算法的错误率
def datingClassTest():#分类器测试代码,一般选取10%的已知类别的样本数据进行测试
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 = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
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 errorCount
最后,我们使用算法,构建完整的分类器
def classiyPerson():
# 定义分类结果的类别
resultList = ['not at all','in small doses','in large doses']
# 读取输入数据
percentTats = float(raw_input("percentage of time spent playing video games?"))
ffMiles = float(raw_input("frequent flier miles earned per year?"))
iceCream = float(raw_input("liters of ice cream consumed per year?"))
# 从文件中读取已有数据
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
# 对数据进行归一化
normMat,ranges,minVals = autoNorm(datingDataMat)
# 将单个输入数据定义成一条数据
inArr =array([ffMiles,percentTats,iceCream])
# 对输入数据进行分类
classifierResult = classify0(inArr,datingDataMat,datingLabels,3)
# 输出预测的分类类别
print "You will probably like this person:",resultList[classifierResult - 1]
分类结果如图所示: