前言:
文章用于本人在学习记录使用,欢迎各位讨论交流。
K-近邻算法概要
在训练集中数据和标签已知的情况下,输入测试数据,将测试数据的特征与训练集中对应的特征进行相互比较,找到训练集中与之最为相似的前K个数据,则该测试数据对应的类别就是K个数据中出现次数最多的那个分类,其算法的描述为:
1)计算测试数据与各个训练数据之间的距离;
2)按照距离的递增关系进行排序;
3)选取距离最小的K个点;
4)确定前K个点所在类别的出现频率;
5)返回前K个点中出现频率最高的类别作为测试数据的预测分类
如下图:
- 如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
- 如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
python实现
环境:win10、python2.7.13
2.1 KNN算法实现
2.1.1 准备使用python导入数据
#coding=utf8
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
运行结果
2.1.1 分类算法实现
def classify0(inX,dataSet,labels,k):#inX为坐标,dataSet即CreateDataSet中的array,作为参考坐标上的值输入,k为k近邻算法中的选取K值
dataSetSize = dataSet.shape[0]#矩阵有一个shape属性,是一个(行,列)形式的元组
#距离计算((x1-x2)^2+(y1-y2)^2)**0.5
diffMat = tile(inX,(dataSetSize,1)) - dataSet#numpy.tile(A,reps)通过重复A reps给出的次数来构建一个数组
sqDiffMat = diffMat**2#(x1-x2)
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()#argsort是排序,将元素按照由小到大的顺序返回下标,比如([3,1,2]),它返回的就是([1,2,0])
classCount={}
#选择距离最小的k个点
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#计算离目标点距离最近的k个点的类别,这个点是哪个类别哪个类别就加1
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
检验算法是否成功,运行以下函数,可以发现几个点被成功归类:
kNN.classify0([0,0],group,labels,3)
2.2 使用k近邻算法改进约会网站的配对效果
2.2.1 将文本记录转换为NumPy的解析程序
#文档格式化
def file2matrix(filename):
fr = open(filename)
arrayOflines = fr.readlines() #把file全文读取出来
numberOfLines = len(arrayOflines) #读取行数
#得到文件行数
returnMat = zeros((numberOfLines,3))#填充一个(行数,3)的数组
#创建返回的Numpy矩阵
classLabelVector = []
index = 0
#解析文件数据到列表
for line in arrayOflines:
line = line.strip()
listFromLine = line.split('\t')#使用回车字符\t分割元素列表
returnMat[index,:] = listFromLine[0:3]#选取前三个元素存储到特征举证中
classLabelVector.append(int(listFromLine[-1]))
index +=1
return returnMat,classLabelVector
代码检验,命令行执行:
datingDataMat,datingLabels = kNN.file2matrix('datingTestSet2.txt')
datingDataMat
datingLabels[0:20]
可以发现dataTestSet2.txt文件中的数据成功被标准化为了代码可以执行的格式,
2.2.2 分析数据:使用matplotlib创建散点图
命令行进入Python后执行:
import kNN
from numpy import *
import matplotlib
import matplotlib.pyplot as plt
datingDataMat,datingLabels = kNN.file2matrix('datingTestSet2.txt')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
plt.show()
可以看出datingTestSet.txt文件中数据中横坐标为玩视频游戏所耗时间百分比和 每周所消费的冰淇淋公斤数 所表示的散点图
PS:这里可以使用参数定义形成的图像的纵坐标和横坐标,因为是后面才发现的所以图上没有。
重复执行上面的代码 替换散点图代码,并使用颜色以及尺寸标识数据点的属性类别,我们可以看到数据点所属三个样本分类的区域轮廓如下图
PS:如果提示name 'array' is not defined,就在代码前加 from numpy import *,import numpy是不行的,一样会报错。
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
ax.scatter(datingDataMat[:,0],datingDataMat[:,1],15.0*array(datingLabels),15.0*array(datingLabels))
还有横坐标是每年飞行常客里程数与纵坐标为玩视频游戏所占百分比的约会数据散点图
2.2.3 准备数据:归一化数值
这里我们会发现,数字差值最大的属性对计算结果影响较大,但我们希望三个因素是同等重要的
所以我们采用数值归一化的方式,将取值范围处理到0到1之间,下面的公式可以将任意取值范围的特征值转化为0到1区间内的值
√((0-67)^2+(20000-32000)^2+(1.1-0.1)²)
下面的公式可以帮助我们归一化数值
newValue = (oldValue - min)/(max-min)
#归一化特征值函数:newValue = (oldValue - min)/(max-min
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
2.2.4测试算法:作为完整程序验证分类器
PS:书中该行代码:
datingDataMat,datingLabels =file2matrix('datingTestSet2.txt')
将datingTestSet.txt作为测试数据输入检验器,但其实在编写文档格式化函数时我们是将结果的特征值作为int型输入,即datingTestSet.txt中使用string字符作为数据的分类结果,datingTestingSet2.txt中使用int型作为数据的分类结果,如果将datingTestSet.txt作为测试文件输入之后会发现报错如下:
输入datingTestSet2.txt文件作为测试文件或修改datingTestSet.txt中分类的标识即可顺利运行
#测试函数
def datingClassTest():
hoRatio = 0.50
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 "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))
2.2.5使用算法:构造完整可用系统
#支持用户自定义输入参数调用
def classifyPerson():
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 icecream consumed per year? :"))
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]
总结
好酷!持续更新!