K近邻算法,简称KNN算法,通俗地说:就是寻找K个最近的邻居;当K=1时,算法便成了最近邻算法,即寻找最近的那个邻居。
用官方的话来说,所谓K近邻算法,即是给定一个训练集,对新的输入实例,在训练集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居),这K个实例的多数属于某个类,就把该输入实例分类到这个类中。
根据这个说法,咱们来看下引自维基百科上的一幅图:
如上图所示,有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的圆所标示的数据则是待分类的数据。
下面,我们就要解决的问题是:给这个绿色的圆分类。
如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
如果K=5,绿色圆点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
衡量待分类点周围邻居的权重,而把它归到权重更大的那一类。这就是K近邻算法的核心思想。
K值的选择对K近邻算法的结果会产生很大影响。
如果选择较小的K值,预测结果会对近邻的实例点非常敏感。如果近邻的实例点恰巧是噪声,预测就会出错。
如果选择较大的K值,与输入实例不相似的训练实例会使预测发生错误。
在实际应用中,K值一般取一个比较小的数值。可以采用交叉验证法(简单来说,就是一部分样本做训练集,一部分做测试集)来选择最优的K值。
针对特征点匹配有两种方法:
最容易的办法就是线性扫描,也就是我们常说的穷举搜索:
依次计算样本集E中每个样本到输入实例点的距离,然后抽取出计算出来的最小距离的点即为最近邻点。
当样本集或训练集很大时,这种方法的缺点就暴露出来了,举个例子,在物体识别的问题中,可能有数千个甚至数万个特征点,去一一计算这成千上万的特征点与输入实例点的距离,效率会非常低。
另外一种,就是构建数据索引,因为实际数据一般都会呈现簇状的聚类形态,因此我们可以建立数据索引,然后再进行快速匹配。索引树是一种树结构索引方法,其基本思想是对搜索空间进行层次划分。
k-d树就是其中一种索引树。
Kd-树:是对数据点在k维空间(如二维(x,y),三维(x,y,z),k维(x1,y,z..))中划分的一种数据结构,主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)。本质上说,Kd-树就是一种平衡二叉树。
k-d树是一种空间划分树,就是把整个空间划分为特定的几个部分,然后在特定空间的部分内进行相关搜索操作。
想像一个三维空间,kd树按照一定的划分规则把这个三维空间划分了多个空间,如下图所示:
一个简单直观的实例:
假设有6个二维数据点{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},数据点位于二维空间内,根据这些数据点构造一个kd树。如下图所示。
具体步骤:
①6个数据点在x,y维度上的数据方差分别为39,28.63,所以在x轴上方差更大,故先选x轴为坐标轴来切分矩形;
②根据x维上的值将数据排序:
{(2,3),(4,7),(5,4),(7,2),(8,1),(9,6)}
6个数据的中值为7,所以以数据点(7,2)为节点,通过(7,2)并垂直于x轴的直线来把平面切分成两个矩形;
③确定左子空间和右子空间。x=7将整个空间分为两部分:x<=7的部分为左子空间,包含3个节点={(2,3),(5,4),(4,7)};另一部分为右子空间,包含2个节点={(8,1),(9,6)};
kd树的构建是一个递归过程,我们对左子空间和右子空间内的数据重复上述过程将空间和数据集进一步细分,如此往复直到空间中任意两个子区域没有实例存在时,停止划分。
如此,便形成了下面这样一棵k-d树:
欧氏距离:最常见的两点之间或多点之间的距离表示法。
点 x = (x1,…,xn) 和 y = (y1,…,yn) 之间的距离为:
目的是检索在k-d树中与查询点距离最近的数据点。
步骤:
假设查询点(2.1,3.1)。通过二叉搜索,顺着搜索路径很快就能找到最邻近的近似点,也就是叶子节点(2,3)。
而找到的叶子节点并不一定就是最邻近的,最邻近肯定距离查询点更近,应该位于以查询点为圆心且通过叶子节点的圆域内。
为了找到真正的最近邻,还需要进行相关的‘回溯’操作。也就是说,算法首先沿搜索路径反向查找是否有距离查询点更近的数据点。
一个简单的栗子:
以查询(2.1,3.1)为例:
1.二叉树搜索:先从(7,2)点开始进行二叉查找,然后到达(5,4),最后到达(2,3),此时搜索路径中的节点为<(7,2),(5,4),(2,3)>,首先以(2,3)作为当前最近邻点,计算其到查询点(2.1,3.1)的距离为0.1414
2.回溯查找:在得到(2,3)为查询点的最近点之后,回溯到其父节点(5,4),并判断在该父节点的其他子节点空间中是否有距离查询点更近的数据点。
以(2.1,3.1)为圆心,以0.1414为半径画圆,如下图所示。发现该圆并不和超平面y = 4交割,因此不用进入(5,4)节点右子空间中(图中灰色区域)去搜索;
3.最后,再回溯到(7,2),以(2.1,3.1)为圆心,以0.1414为半径的圆更不会与x = 7超平面交割,因此不用进入(7,2)右子空间进行查找。至此,搜索路径中的节点已经全部回溯完,结束整个搜索,返回最近邻点(2,3),最近距离为0.1414。
参考资料:从K近邻算法、距离度量谈到KD树、SIFT+BBF算法
代码参考:KNN最近邻基于欧几里德距离的JAVA算法实现