问题导入:

实现k近邻法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索。

这在特征空间的维数大及训练数据容量大时尤其必要。

k近邻法最简单的实现是线性扫描(穷举搜索),即要计算输入实例与每一个训练实例的距离。计算并存储好以后,再查找K近邻。当训练集很大时,计算非常耗时。

为了提高kNN搜索的效率,可以考虑使用特殊的结构存储训练数据,以减小计算距离的次数。


1 kd树简介

1.1 什么是kd树

根据KNN每次需要预测一个点时,我们都需要计算训练数据集里每个点到这个点的距离,然后选出距离最近的k个点进行投票。当数据集很大时,这个计算成本非常高,针对N个样本,D个特征的数据集,其算法复杂度为O(DN^2)

kd树:为了避免每次都重新计算一遍距离,算法会把距离信息保存在一棵树里,这样在计算之前从树里查询距离信息,尽量避免重新计算。其基本原理是,如果A和B距离很远,B和C距离很近,那么A和C的距离也很远。有了这个信息,就可以在合适的时候跳过距离远的点。

这样优化后的算法复杂度可降低到O(DNlog(N))。感兴趣的读者可参阅论文:Bentley,J.L.,Communications of the ACM(1975)。

1989年,另外一种称为Ball Tree的算法,在kd Tree的基础上对性能进一步进行了优化。感兴趣的读者可以搜索Five balltree construction algorithms来了解详细的算法信息。

1.2 原理

黄色的点作为根节点,上面的点归左子树,下面的点归右子树,接下来再不断地划分,分割的那条线叫做分割超平面(splitting hyperplane),在一维中是一个点,二维中是线,三维的是面。

黄色节点就是Root节点,下一层是红色,再下一层是绿色,再下一层是蓝色。

1.树的建立;

2.最近邻域搜索(Nearest-Neighbor Lookup)

kd树(K-dimension tree)是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是一种二叉树,表示对k维空间的一个划分,构造kd树相当于不断地用垂直于坐标轴的超平面将K维空间切分,构成一系列的K维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。

类比“二分查找”:给出一组数据:[9 1 4 7 2 5 0 3 8],要查找8。如果挨个查找(线性扫描),那么将会把数据集都遍历一遍。而如果排一下序那数据集就变成了:[0 1 2 3 4 5 6 7 8 9],按前一种方式我们进行了很多没有必要的查找,现在如果我们以5为分界点,那么数据集就被划分为了左右两个“簇” [0 1 2 3 4]和[6 7 8 9]。

因此,根本就没有必要进入第一个簇,可以直接进入第二个簇进行查找。把二分查找中的数据点换成k维数据点,这样的划分就变成了用超平面对k维空间的划分。空间划分就是对数据点进行分类,“挨得近”的数据点就在一个空间里面。

2 构造方法

(1)构造根结点,使根结点对应于K维空间中包含所有实例点的超矩形区域;

(2)通过递归的方法,不断地对k维空间进行切分,生成子结点。在超矩形区域上选择一个坐标轴和在此坐标轴上的一个切分点,确定一个超平面,这个超平面通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个子区域(子结点);这时,实例被分到两个子区域。

(3)上述过程直到子区域内没有实例时终止(终止时的结点为叶结点)。在此过程中,将实例保存在相应的结点上。

(4)通常,循环的选择坐标轴对空间切分,选择训练实例点在坐标轴上的中位数为切分点,这样得到的kd树是平衡的(平衡二叉树:它是一棵空树,或其左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是平衡二叉树)。

KD树中每个节点是一个向量,和二叉树按照数的大小划分不同的是,KD树每层需要选定向量中的某一维,然后根据这一维按左小右大的方式划分数据。在构建KD树时,关键需要解决2个问题:

(1)选择向量的哪一维进行划分;

(2)如何划分数据;

第一个问题简单的解决方法可以是随机选择某一维或按顺序选择,但是更好的方法应该是在数据比较分散的那一维进行划分(分散的程度可以根据方差来衡量)。好的划分方法可以使构建的树比较平衡,可以每次选择中位数来进行划分,这样问题2也得到了解决。

3 案例分析

3.1 树的建立

给定一个二维空间数据集:T={(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},构造一个平衡kd树。

(1)思路引导:

根结点对应包含数据集T的矩形,选择x(1)轴,6个数据点的x(1)坐标中位数是6,这里选最接近的(7,2)点,以平面x(1)=7将空间分为左、右两个子矩形(子结点);接着左矩形以x(2)=4分为两个子矩形(左矩形中{(2,3),(5,4),(4,7)}点的x(2)坐标中位数正好为4),右矩形以x(2)=6分为两个子矩形,如此递归,最后得到如下图所示的特征空间划分和kd树。

X轴方差相比Y大,先选x轴切割。 找中间点发现是(7,2);选5和7都可以,这里选7.

构建树
        第一次:
        x轴-- 2,5,9,4,8,7 --> 2,4,5,7,8,9
        y轴-- 3,4,6,7,1,2 --> 1,2,3,4,6,7

首先选择x轴, 找中间点,发现是(7,2)

第二次:
        左面: (2,3), [4,7], [5,4] --> 3,4,7
        右面: (8,1), (9,6) --> 1,6

从y轴开始选择, 左边选择点是(5,4),右边选择点(9,6)

第三次:
        从x轴开始选择

3.2 最近领域的搜索

假设标记为星星的点是 test point, 绿色的点是找到的近似点,在回溯过程中,需要用到一个队列,存储需要回溯的点,在判断其他子节点空间中是否有可能有距离查询点更近的数据点时,做法是以查询点为圆心,以当前的最近距离为半径画圆,这个圆称为候选超球(candidate hypersphere),如果圆与回溯点的轴相交,则需要将轴另一边的节点都放到回溯队列里面来。

样本集{(2,3),(5,4), (9,6), (4,7), (8,1), (7,2)}

3.2.1 查找点(2.1,3.1)

在(7,2)点测试到达(5,4),在(5,4)点测试到达(2,3),然后search_path中的结点为<(7,2),(5,4), (2,3)>,从search_path中取出(2,3)作为当前最佳结点nearest, dist为0.141;

然后回溯至(5,4),以(2.1,3.1)为圆心,以dist=0.141为半径画一个圆,并不和超平面y=4相交,如上图,所以不必跳到结点(5,4)的右子空间去搜索,因为右子空间中不可能有更近样本点了。

于是再回溯至(7,2),同理,以(2.1,3.1)为圆心,以dist=0.141为半径画一个圆并不和超平面x=7相交,所以也不用跳到结点(7,2)的右子空间去搜索。

至此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2.1,3.1)的最近邻点,最近距离为0.141。

3.2.2 查找点(2,4.5)

在(7,2)处测试到达(5,4),在(5,4)处测试到达(4,7)【优先选择在本域搜索】,然后search_path中的结点为<(7,2),(5,4), (4,7)>,从search_path中取出(4,7)作为当前最佳结点nearest, dist为3.202;

然后回溯至(5,4),以(2,4.5)为圆心,以dist=3.202为半径画一个圆与超平面y=4相交,所以需要跳到(5,4)的左子空间去搜索。所以要将(2,3)加入到search_path中,现在search_path中的结点为<(7,2),(2, 3)>;另外,(5,4)与(2,4.5)的距离为3.04 < dist = 3.202,所以将(5,4)赋给nearest,并且dist=3.04。

回溯至(2,3),(2,3)是叶子节点,直接平判断(2,3)是否离(2,4.5)更近,计算得到距离为1.5,所以nearest更新为(2,3),dist更新为(1.5)

回溯至(7,2),同理,以(2,4.5)为圆心,以dist=1.5为半径画一个圆并不和超平面x=7相交, 所以不用跳到结点(7,2)的右子空间去搜索。

至此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2,4.5)的最近邻点,最近距离为1.5。

4 总结

首先通过二叉树搜索(比较待查询节点和分裂节点的分裂维的值,小于等于就进入左子树分支,大于就进入右子树分支直到叶子结点),顺着“搜索路径”很快能找到最近邻的近似点,也就是与待查询点处于同一个子空间的叶子结点;

然后再回溯搜索路径,并判断搜索路径上的结点的其他子结点空间中是否可能有距离查询点更近的数据点,如果有可能,则需要跳到其他子结点空间中去搜索(将其他子结点加入到搜索路径)。

重复这个过程直到搜索路径为空。

kd树实现搜索的过程相关推荐

  1. 构建kd树和kd树的搜索

    问题引入:如何对训练数据进行快速k近邻搜索? 方法1.k近邻法最简单的实现是线性扫描(穷举搜索),即要计算输入实例与每一个训练实例的距离.计算并存储好以后,再查找K近邻.当训练集很大时,计算非常耗时. ...

  2. 利用kd树进行平面内最近点搜索

    要求: 对平面内每一个点都找到离它最近的点.通常想法是knn,那么由于是对n个点求,直接算的话就是n平方. 我们利用kd树来搜索,那么复杂度就变为了nlogn,甚至说由于a与b距离最小等价于b与a最小 ...

  3. KNN算法与Kd树(转载+代码详细解释)

    最近邻法和k-近邻法 下面图片中只有三种豆,有三个豆是未知的种类,如何判定他们的种类? 提供一种思路,即:未知的豆离哪种豆最近就认为未知豆和该豆是同一种类.由此,我们引出最近邻算法的定义:为了判定未知 ...

  4. python机器学习案例系列教程——K最近邻算法(KNN)、kd树

    全栈工程师开发手册 (作者:栾鹏) python数据挖掘系列教程 K最近邻简介 K最近邻属于一种估值或分类算法,他的解释很容易. 我们假设一个人的优秀成为设定为1.2.3.4.5.6.7.8.9.10 ...

  5. 机器学习算法之二KD树

    KD树 实现k近邻算法时,主要考虑的问题是如何对训练数据进行快速k近邻搜索. 这在特征空间的维数大及训练数据容量大时尤其必要. k近邻法最简单的实现是线性扫描(穷举搜索),即要计算输入实例与每一个训练 ...

  6. 使用k-d树进行无序点云去噪

    离散点云的点之间是没有拓扑结构的,因此为了找到它的几何属性,可以找到各点的邻域结构.对于采样点pi∈Pp_i \in P定义其邻域NkpiN_{p_i}^k为采样点pip_i的最近的kk个采样点组成的 ...

  7. 多维空间分割树--KD树

    算法介绍 KD树的全称为k-Dimension Tree的简称,是一种分割K维空间的数据结构,主要应用于关键信息的搜索.为什么说是K维的呢,因为这时候的空间不仅仅是2维度的,他可能是3维,4维度的或者 ...

  8. 高级数据结构-KD树

    高级数据结构-KD树 一.基本概念 KD树是一种对K维空间中的实例点进行存储以便对其进行快速检索的树形结构,表示对K维空间的一个划分 用于KNN算法的实现.给定一个目标点,利用KD树可以快速地查找出距 ...

  9. 【KD-Tree】基于k-d树的KNN算法实现

    文章目录 一.什么是KD-Tree? 二.k-d树的结构 三.k-d树的创建 四.k-d树的应用 五.KD-Tree的优缺点 例题 JZPFAR 一.什么是KD-Tree? KD-Tree,又称(k- ...

最新文章

  1. 你哪来这么多事(四):职工信息排序
  2. Snmp在Windows下的实现----WinSNMP编程原理
  3. 户籍改革更进一步!我国将推动户籍准入年限同城化累计互认
  4. SQL SERVER最大用户连接数
  5. dos和linux有关系吗,DOS和Linux近年来的发展比较
  6. is_file()和file_exists()
  7. 你在微信漂流瓶里遇到过哪些有意思的人和事?
  8. windows Server 2008各版本区别详解
  9. c# 批量mqtt_Paho-MQTT C#接入示例
  10. 开始新的学习之旅--PHP开发学习--基础部分笔记
  11. VISIO使用FPGA时序图、状态机等所需的模具
  12. 个人信息安全规范----2、个人信息安全基本原则
  13. 子网划分,掩码转换计算
  14. 电脑如何安装无线网卡?
  15. 深度学习分析--TextCNN算法原理及分类实现
  16. Spring Boot-1 (IntelliJ IDEA + gradle)
  17. 在unik中,写一个Go HTTP服务器
  18. 西门子plc与oracle报文,西门子PLC以太网 通讯协议 解析
  19. 力扣—— 19. 删除链表的倒数第 N 个结点(java)、剑指 Offer 22. 链表中倒数第k个节点(java)
  20. html垂直居中vertical,利用vertical-align:middle垂直居中

热门文章

  1. python常用画图颜色
  2. java画图板之平面山水画(二)
  3. SSM框架体会和小结(短学期实训)
  4. 个人博客园样式、背景及细节美化过程
  5. 怎么把图片文件压缩到最小?
  6. linux python 路径获取
  7. 入侵JVM?Java Agent原理浅析和实践(上)
  8. POJ3666 线性dp+维度优化
  9. android顶部按钮图片,安卓动态改变button顶部图片即drawableTop属性
  10. Spark --入门简介