一.概述
k-近邻算法采用测量不同特征值的方法信息分类。模型三个基本要素为:距离度量、k值选择和分类决策规则。
(1)距离度量:常用“欧式距离”表示两n维实数向量之间的距离。
(2)k值一般选取比较小的数值,通常采用交叉验证法来选取最优k值。
(3)k近邻模型的分类决策规则通常为多数表决规则,即由输入实例的k个临近的训练实例中的多数类决定输入实例的类。k个训练实例点组成的集合为Nk (x)。
误分类率:
误分类率最小即经验风险最小,就要使最大,因此多数表决规则等价于经验风险最小化。
二.工作原理
(1)存在一个样本数据集合(训练样本集),并且其中每个数据都有标签(分类);
(2) 输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征比较,提取样本集中特征最相似数据(最近邻)的分类标签;
(3)选择样本数据集中前k个最相似的数据,通常k是不大于20的整数;
(4)最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
三.优缺点及适用范围
(1)优点:精度高、对异常值不敏感、无数据输入假定
(2)缺点:计算复杂度高、空间复杂度高
(3)适用数据范围:整值型和标称型
四.一般流程
收集数据→准备数据→分析数据→训练算法→测试算法→使用算法
注意事项:
(1)准备数据最好为结构化数据格式;
(2)k-近邻算法不需要训练算法(无参);
(3)通过计算错误率来测试算法。
五.python代码实现
案例 利用K近邻算法改进约会网站的配对效果:
步骤如下:
对未知类别数据集中的每个点依次执行如下操作。
(1)计算已知类别数据集中点与当前点之间的距离;
(2)按照距离递增次序排序;
(3)选取与当前点距离最小的k个点;
(4)确定前k个点所在类别的出现频率;
(5)返回前k个点出现频率最高的类别作为当前点的预测分类。
第一步:编写clssify0()函数代码,使用k-近邻算法将每组数据划分到某个类中。
# 分类算法classify0,计算欧氏距离
def classify0(inX,dataSet,lables,k):
#shape 返回行行列数,shape[0]是行数,表示有多少元组,shape[1]返回列
dataSetSize=dataSet.shape[0]
#tile复制inX,使其与dataset一样大小,二者之差形成一个差值矩阵
diffMat=tile(inX,(dataSetSize,1))-dataSet
sqDiffMat=diffMat**2
sqDistances=sqDiffMat.sum(axis=1) #对差值矩阵按行进行求和
distances=sqDistances**0.5
#argsort()进行排序,返回从小到大索引值
sortedDistances=distances.argsort()
classCount={}#新建用于计数的空字典
for i in range(k):
votelable=lables[sortedDistances[i]] #获取前k个样本对应的类别标签
#统计对应类别标签出现的次数,生成一个元组存储数据
classCount[votelable]=classCount.get(votelable,0)+1
#按照第二个元素进行降序排列,迭代器进行降序排序(类似冒泡) sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
#返回出现次数最多的哪一个label的值
return sortedClassCount[0][0]
第二步:编写file2matrix(filename)方法,读取txt文件中数据
#获取文件数据
def file2matrix(filename):
fr=open(filename)
arrayOLines=fr.readlines()
numberOfLines=len(arrayOLines) #获得文件行数
returnMat=zeros((numberOfLines,3)) #创建m行,n列的零矩阵
classLabelVector=[]#创建类标签元组
index=0
for line in arrayOLines:
line =line.strip()#截取所有的回车字符
listFromLine=line.split('\t') #根据分隔符将每一行划分成元素列表
#将取得的每一行的内容存储起来,选取前三个元素作为特征值
returnMat[index,:]=listFromLine[0:3]
#获取最后一个元素作为类标签
classLabelVector.append(int(listFromLine[-1]))
index +=1
return returnMat,classLabelVector
第三步:对样本集数据进行两两特征分类,生成可视化散点图
注意事项:
(1)导入datingTestSet2.txt,前三列为属性特征值,最后一列(-1)为分类标签。
(2)#-- coding:utf-8 --声明代码,才会显示代码中的中文字符;
(3)需要对matplotlib中字体进行指定,才会在figure图表中显示中文。
#-*- coding: utf-8 -*-
import kNN
from numpy import *
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
fig = plt.figure()
# matplotlib中的图像函数,一行一列一维
ax = fig.add_subplot(111)
datingDataMat,datingLabels = kNN.file2matrix('datingTestSet2.txt')
ax.scatter(datingDataMat[:,0], datingDataMat[:,1],15.0*array(datingLabels), 15.0*array(datingLabels))
# ax.axis([-2,25,-0.2,2.0])
plt.ylabel(u'玩游戏耗时百分比')
plt.xlabel(u'每年获取的飞行常客里程数')
plt.show()
通过上图,可发现选取不同的属性值会得到不同的显示效果,相比第一张分类图,第二张图像更能区分不同特征对样本的分类。但是由于各属性数值范围大小的不同,对欧式距离计算结果影响的程度也不同,因此需要对各样本属性数据进行归一化特征值处理,将取值范围的特征值转化为0-1区间内的值。
第四步:对数据进行优化,进行归一化特征值处理
# 归一化特征值
def autoNorm(dataSet):
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))
return normDataSet,ranges,minVals
第五步:利用分类器对测试数据集进行测试,并计算错误率
# 分类器使用测试代码
def datingClassTest():
#选取测试集数据比例
hoRatio=0.10
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
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 "dfa: %d,real: %d" %(classifierResult,datingLabels[i])
if(classifierResult!=datingLabels[i]):errorCount+=1.0
#计算错误率汇总
print "somebat: %f" %(errorCount/float(numTestVecs))
通过测试集训练算法(测试集占样本比率0.1,k=3),可以得到错误率为5%,具备较好预测效果。实际中,可以通过调整测试集数据比率和k值得到不同的错误率来验证算法的效果。