机器学习实战(一)—K-近邻算法

前言:

    文章用于本人在学习记录使用,欢迎各位讨论交流。

K-近邻算法概要

       在训练集中数据和标签已知的情况下,输入测试数据,将测试数据的特征与训练集中对应的特征进行相互比较,找到训练集中与之最为相似的前K个数据,则该测试数据对应的类别就是K个数据中出现次数最多的那个分类,其算法的描述为:

1)计算测试数据与各个训练数据之间的距离;

2)按照距离的递增关系进行排序;

3)选取距离最小的K个点;

4)确定前K个点所在类别的出现频率;

5)返回前K个点中出现频率最高的类别作为测试数据的预测分类

如下图:


  • 如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
  • 如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
于此我们看到,当无法判定当前待分类点是从属于已知分类中的哪一类时,我们可以依据统计学的理论看它所处的位置特征,衡量它周围邻居的权重,而把它归为(或分配)到权重更大的那一类。这就是K近邻算法的核心思想。
KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
KNN 算法本身简单有效,它是一种 lazy-learning 算法,分类器不需要使用训练集进行训练,训练时间复杂度为0。KNN 分类的计算复杂度和训练集中的文档数目成正比,也就是说,如果训练集中文档总数为 n,那么 KNN 的分类时间复杂度为O(n)。
KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。

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]

总结

好酷!持续更新!




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值