写程序学ML:K近邻(KNN)算法原理及实现(一)

来源:互联网 发布:家里有网络怎么看电视 编辑:程序博客网 时间:2024/05/23 15:40

[题外话]近期申请了一个微信公众号:平凡程式人生。有兴趣的朋友可以关注,那里将会涉及更多更新机器学习、OpenCL+OpenCV以及图像处理方面的文章。


K近邻(k-NearestNeighbor,KNN)算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。

1、K近邻算法的原理

简单地说,K近邻算法采用测量不同特征值之间距离的方法进行分类。它的核心思想是如果一个样本在特征空间中的k个最相邻的样本(距离最近的样本)中的大多数属于某一个类别,则该样本也属于这个类别,并具有这个类别上样本的特性。该方法在确定分类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

K近邻算法的优缺点

优点:精度高、对异常值不敏感、无数据输入假定。

缺点:计算复杂度高、空间复杂度高。

适用数据范围:数值型和标称型。

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

K近邻算法的一般流程

1>    收集数据:可以使用任何方法。

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

3>    分析数据:可以使用任何方法。

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

5>    测试算法:计算错误率。

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

在K近邻算法中计算样本之间的距离,一般采用欧式距离。

欧几里得度量(euclidean metric)(也称欧氏距离)是一个通常采用的距离定义,指在m维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离。

二维欧式距离的公式,计算A(x1,y1)和B(x2,y2)两点之间的距离:

  ρ = sqrt((x1-x2)^2+(y1-y2)^2 )

三维欧式距离的公式,计算A(x1,y1,z1)和B(x2,y2,z2)两点之间的距离:

  ρ = sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2 )

2、K近邻算法的实现

为了深入理解K近邻算法,实现了三个K近邻算法的例子。

2.1   K近邻算法的实现

根据第一节中介绍的K近邻算法原理,使用python语言实现了K近邻算法。

为了后续各个机器学习算法的学习,我建立了文件夹module用来存储所有机器学习算法的module实现。

在module目录下,创建了文件kNN.py,将在这个文件中完成K近邻算法的python实现。具体的实现过程如下:

1>    为了方便交流,我在code中添加了尽可能多的注释,且用中文注释。如果python文件中要使用中文,需要在文件开始声明文件的编码方式为utf-8。在第一行写入:

# encoding: utf-8

2>    实现KNN算法过程中,需要用到外部模块numpy和operator。使用下面code导入:

from numpy import *import operator

3>    定义函数knnClassify(inX, dataSet, labels, k)实现K近邻算法。

该函数定义了4个形参,它们的含义如下:

inX: 1行M列的向量,为测试样本,用来跟输入变量dataSet中的数据进行比较(1xM)

dataSet: 已知的N行M列的数组,为训练样本集,一行为一个训练样本,即N个训练样本,每个训练样本M个元素(NxM)

labels: 1行N列的向量,存储各个训练样本对应分类的标签信息 (1xN)

k: 用来比较的近邻元素的个数,选择奇数,且小于20

函数knnClassify()实现思路:使用测试样本创建一个与训练样本dataSet个数一样NxM的二维数组,其每个样本都和测试样本的内容一致。然后求得该二维数组与训练样本dataSet中每个元素的差的平方和,再按行,即按照样本求得每个样本所有元素之和,对该和进行平方运算,最终求得测试样本与训练样本dataSet中每一个样本的欧式距离。调用函数argsort()对欧式距离进行从小到大的排序,从中选取前k个距离对应的训练样本信息。对k个最近邻样本进行排序,选择样本个数最多的那个分类信息,作为测试样本的分类信息。

函数knnClassify()的具体实现如下:
def knnClassify(inX, dataSet, labels, k):dataSetSize = dataSet.shape[0] #获取训练样本集dataSet的个数,即矩阵dataSet的行数N#tile(inX, (dataSetSize, 1))是建立一个N行1列的数组,但其元素是一个1行M列的向量,最后返回一个N行M列的数组#N行M列的数组中每一行都是输入的测试样本,它与训练样本集相减,则得到NxM的数组值之差diffMat = tile(inX, (dataSetSize, 1)) - dataSetsqDiffMat = diffMat**2sqDistances = sqDiffMat.sum(axis = 1) #axis=1是对存储NxM个元素对应的平方差的数组,将每一行的值累加起来,返回一个Nx1的数组distances = sqDistances**0.5 #求得测试样本与各个训练样本的欧式距离    #对distances中N个元素进行从小到大排序,之后返回一个N个元素的一维数组,存储distances排序后各个元素在原来数组中的index#eg. distances=[2,1,3,0], argsort的返回值为[3,1,0,2]sortedDistIndicies = distances.argsort()    classCount={} #定义一个空的字典变量for i in range(k): #i的取值为[0,k - 1]voteLabel = labels[sortedDistIndicies[i]] #返回测试样本与训练样本欧式距离第i小的训练样本所对应的类的标签#classCount.get(voteLabel, 0)获取classCount中voteLabel为index的元素的值,找不到则返回0classCount[voteLabel] = classCount.get(voteLabel, 0) + 1        #classCount.iteritems()返回字典classCount中所有项,按照迭代器的方式#operator.itemgetter()用于获取对象的哪些维的数据,参数为一些序号;此处参数为1,即按照字典变量classCount各项的第二个元素进行排序#reverse = True表示按照逆序排序,即从大到小的次序;False表示按照从小到大的次序排序sortedClassCount = sorted(classCount.iteritems(), key = operator.itemgetter(1), reverse = True)    #返回k个近邻元素中所对应类最多那个类的标签,即测试样本所属那个类return sortedClassCount[0][0]

(未完待续)





阅读全文
0 0
原创粉丝点击