机器学习 K-Nearst-Neighbors算法

来源:互联网 发布:企业海关数据查询 编辑:程序博客网 时间:2024/06/09 22:43

机器学习 K近邻(K-Nearest-Neighbors)算法剖析

一、 读懂该算法所具备的相关知识

  1. 熟练掌握Python语言

  2. 线性代数矩阵常见的基本运算

  3. 欧几里得定律,计算矩阵中两点之间的距离。

二、 生活案例-K近邻算法

电影可以按照题材分类,然而题材本身是如何定义的?由谁来判定某部电影属于哪个题材?也就是说同一题材的电影具有哪些公共特征?这些都是在进行电影分类时必须要考虑的问题。每部电影在风格上的确有可能会和同题材的电影相近。那么动作片具有哪些共有特征,使得动作片之间非常类似,而与爱情片存在着明显的差别呢?动作片中也会存在接吻镜头,爱情片中也会存在打斗场景,我们不能单纯依靠是否存在打斗或者亲吻来判断影片的类型。但是爱情片中的亲吻镜头更多,动作片中的打斗场景也更频繁,基于此类场景在某部电影中出现的次数可以用来进行电影分类。

基于电影中出现的亲吻、打斗出现的次数,使用K近邻算法构造程序,自动划分电影的题材类型。

简单地说,K近邻算法采用测量不同特征值之间的距离方法进行分类。

K近邻算法(K-NN), 它的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输人没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前K个最相似的数据,这就是K-近邻算法中K的出处,通常K是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

现在我们回到前面电影分类的例子,使用K-近邻算法分类爱情片和动作片。有人曾经统计过很多电影的打斗镜头和接吻镜头,图2-1显示了6部电影的打斗和接吻镜头数。

假如有一部未看过的电影,如何确定它是爱情片还是动作片呢?我们可以使用k-近邻算法来解决这个问题。

接下来有一部未知电影,如图,首先我们需要知道这个未知电影存在多少个打斗镜头和接吻镜头,图中问号位置是该未知电影出现的镜头数图形化展示,具体数字如下图

即使不知道未知电影属于哪种类型,我们也可以通过某种方法计算出来。首先计算未知电影与样本集中其他电影的距离,如下表所示。此处暂时不要关心如何计算得到这些距离值,使用 Python实现电影分类应用时,会提供具体的计算方

以上主要讲解如何在实际环境中应用K-近邻算法,如果通过Python编程语言来实现K-近邻算法进而让机器帮助人类来对电影进行分类,这就是机器学习。   在动物界,我们可以开发一个鸟类分类系统,这样鸟类专家就可以下岗了,当有一只未知类型的鸟,我们只需要通过机器学习k近邻算法就可以区分出这只鸟是什么类型的。

现在我们得到了样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到K个距离最近的电影。假定K=3,则三个最靠近的电影依次是<<CaliforniaMan>> <<He’s Not Really into Dudes>> <<BeautifulWoman>> K-近邻算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片

三、 K-近邻算法的基本实现

K-近邻算法的一般实现流程如下:

a) 收集数据:可以使用任何方法

b) 准备数据:距离计算所需要的数值,最好是结构化的数据格式。

c) 分析数据:可以使用任何方法。

d) 训练算法:此步驟不适用于1 近邻算法。

e) 测试算法:计算错误率。

f) 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行女-近邻算法判定输 入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

  1. K-近邻算法训练数据

    在一个坐标中有四组数据如下图,并且这四个数据都有标签,能够标识出这四个数据点都属于什么类别的,即标签类别。现在有一个陌生的数据点过来,请你通过机器学习算法让机器能够判断出该数据点是那个类别的。

group([1, 1], [1, 1.1], [0, 0], [0, 0.1])

A(1, 1) A(1, 1.1) B(0, 0) B(0, 0.1)

Labels= [A, A, B, B]

注意:Label包含的元素个数等于group矩阵的行数

  1. K-近邻算法实现原理

l 计算已知类别数据集中的点与当前点之间的距离;

l 按照距离递增次序排序;

l 选取与当前点距离最小的k个点;

l 确定前k个点所在类别的出现频率;

l 返回前k个点出现频率最高的类别作为当前点的预测分类。

  1. 创建一个名为kNN.pyPython文件,内容如下

from numpy import *

import operator

#算法训练数据集

def createDataSet():

group =array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])   # x坐标和y坐标在1附近的归类到A,在0附近的点归类到B   labels =['A','A','B','B']   returngroup,labels

#算法实现方法

def classify0(inX, dataSet, labels, k ) :

dataSetSize = daCaSet.shape[0]

#距离计算

diffMat = tile(inX, (dataSetSize,1)) - dataSet

sqDiffMat = diffMat**2

sqDistances = sqDiffMat.sum(axis=l)

distances = sqDistances**O.5

sortedDistIndicies =distances.argaort ()

#选 择 距 离 最 小 的k个点

classCount={}

for i in range(k):

voteIlabel = labels[sortedDistIndicies[i]]

classCount[votellabel] = classCount.get{voteIlabel,0) + 1

sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)

return sortedClassCount[0] [0]

  1. 代码详细注释如下:

'''

导入numpy模块,该模块用于数据NumPy系统

是Python的一种开源的数值计算扩展。

这种工具可用来存储和处理大型矩阵。

'''

fromnumpy import *

importoperator #提供了一系列的函数操作

"""

定义机器学习训练数据集和分类标签数据。

"""

defcreateDataSet():

group =array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])   # x坐标和y坐标在1附近的归类到A,在0附近的点归类到B   labels = ['A','A','B','B']   return group,labels

"""

实现K-近邻算法,对数据点进行机器自动分类   inX:需要让机器进行分类的数据点例如:[0,0] 或者[1.4,1.6]等数据点   dataSet:机器学习的训练数据   labels:   机器学习的训练数据的已知分类   k: inX输入数据与训练数据之间的距离个数的前k个,   这个距离会按照从小到达进行排序。

"""

defclassify0(inX, dataSet, labels, k):

'''   shape函数用于读取矩阵的长度,shape[0]   就是读取矩阵的第一维度的长度   根据上面训练数据集的情况,   group矩阵第一维的长度是dataSetSize= 4   '''   dataSetSize = dataSet.shape[0]   '''   tile函数用于创造一个长度为4的一维矩阵,矩阵中的元素为inX,   然后与dataSet训练矩阵做减法   矩阵的减法是为了后面这两个矩阵中元素之间的距离计算做准备。   tile(inX,(dataSetSize,1))          array([[0, 0],   [0, 0],   [0, 0],   [0, 0]])    dataSet:             array([[ 1. ,  1.1],   [ 1. ,  1. ],   [ 0. ,  0. ],   [ 0. ,  0.1]])     两个矩阵做完减法diffMat结果如下:          array([[-1. ,-1.1],   [-1. , -1. ],   [ 0. ,  0. ],   [ 0. , -0.1]])   '''   diffMat = tile(inX,(dataSetSize,1)) -dataSet   '''   sqDiffMat          array([[ 1.  , 1.21],   [ 1.  , 1.  ],   [ 0.  , 0.  ],   [ 0.  , 0.01]])   '''   sqDiffMat = diffMat**2 #对矩阵减法结果做幂次运算     '''          计算矩阵中每个元素幂次运算之后的和          sqDistances =array([ 2.21,  2.  , 0.  ,  0.01])   '''   sqDistances = sqDiffMat.sum(axis=1)   '''   做开平方运算出inX未知点与训练数据四个点之间的距离   array([ 1.48660687,  1.41421356,  0. , 0.1])   '''   distances = sqDistances**0.5   '''          按着inX距离训练数据点距离的从小到大顺序的索引值进行排序          也及时argsort()函数的返回结果          排序后从小到大距离的索引值被放到list中[2,3,1,0]   '''   sortedDistIndecies = distances.argsort()     #选择距离最小的k个点     '''   定义一个空字典,字典中元素key的值为标签分类名,即是A或者B   value的值为根据距离计算出标签类别出现的频率数。   '''   classCount = {}   '''   这里k的值是根据训练数据量来设置的,本次训练数据为4个   所以k的值可以设置为3就是计算 inX 数据与训练数据4个的距离   中取前3个距离来进行统计,看前3个当中inX 得出的标签分类出现   的频率数   k取3,则在循环中i的值一次为 0 1 2   '''   for i in range(k):          '''          sortedDistIndecies[i]的值 当i=0是获得其中第一个元素的值2          那么第一个元素2 就是inX距离四个点之间前k个距离的第一个距离的索引值          训练数据如下:   group =array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])   labels =['A','A','B','B'] # x坐标和y坐标在1附近的归类到A,在0附近的点归类到B                 sortedDistIndecies= [2,3,1,0]                            [1.0,1.1] [1.0,1.0]    [0,0]  [0,0.1]                                  A(0)      A(1)       B(2)   B(3)                 array([1.48660687,  1.41421356,  0. , 0.1])

在这里我们计算出了inX数据点与训练数据四个点的距离,计算顺序也是按着矩阵的元素

排练的顺序计算出的,那么这里的顺序很重要,因为在训练数据集中顺序和它的标签分类

是一一对应的,所以计算出的距离索引的值和标签是一对一对应的。当我们把距离索引的值

按着距离的从小到大排列,然后放到一个list当中,此时索引的值就是标签的分类了。

这就是 voteIabel = labels[sortedDistIndecies[i]]这一句话的含义。sortedDistIndecies = [2,3,1,0]

'''

voteIlabel =labels[sortedDistIndecies[i]] #获取第k个距离最小的标签分类                   '''          这里的+1 是为了计算classCount字典中key对应的value的值,key其实就是标签的分类          value的值,就是某一分类出现的次数,如果相同的key出现了,那么这个key的分类对应          的值就加了两次1 变为2. 那么最后我们就可以统计出标签出现频率最高的一个做为陌生          inX数据点的分类,最为准确了。          '''          classCount[voteIlabel] =classCount.get(voteIlabel,0)+1          '''          循环结束后classCount字典的元素结果为          classCount ={['A',1],['B',2]}          '''          '''          classCount.iteritems()方法返回一个iterator对象          key=operator.itemgetter(1)按着key进行排序,此时的key的值对应的是字典中          value的值,即按每个字典元素value的值进行排序          reverse=True 指的是反转按着倒序排列。          sortedClassCount= [('B',2),('A',1)]          '''          sortedClassCount =sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)          #返回统计分类频率最好的类别元素,作为陌生数据分类的最终结果。          return sortedClassCount[0][0]
  1. 测试:classify0([0,0], dataset,label,3);结果分类为:B