【转】【量化课堂】kd 树算法之思路篇

导语:kd 树是一种二叉树数据结构,可以用来进行高效的 kNN 计算。kd 树算法偏于复杂,本篇将先介绍以二叉树的形式来记录和索引空间的思路,以便读者更轻松地理解 kd 树。

作者:肖睿
编辑:宏观经济算命师

本文由JoinQuant量化课堂退出,本文的难度属于进阶(上),深度为level-1。

阅读本文之前请掌握 kNN(level-1)的知识。

前言

kd 树(k-dimensional tree)是一个包含空间信息的二项树数据结构,它是用来计算 kNN 的一个非常常用的工具。如果特征的维度是 DD,样本的数量是 NN,那么一般来讲 kd 树算法的复杂度是 O(DlogN)O(Dlog?N),相比于穷算的 O(DN)O(DN) 省去了非常多的计算量。

因为 kd 树的概念和算法较为复杂,固将本教程分为“思路篇”和“详细篇”。两篇的内容在一定程度上是重叠的,但是本篇注重于讲解 kd 树背后的思想和直觉,告诉读者一颗二项树是如何承载空间概念的,我们又该如何从树中读取这些信息;而之后的详细篇则详细讲解 kd 树的定义,如何构造它并且如何计算 kNN。出于教学起见,本文讲的例子和算法与严格的 kd 树有一些差异。有算法经验或者想尝试挑战的读者可以直接跳过本篇去读详细篇

关于在学习编程和算法时有没有必要自己制作轮子的问题,一直存在着很多的争议。作者认为,做不做轮子暂且不论,但是有必要去了解轮子是怎么做出来的。Python 的 scikit-learn 机器学习包提供了蛮算、kd 树和 ball 树三种 kNN 算法,学完本篇的读者若无兴趣自撰算法,可以非常轻松地使用该包,详细可见 scikit-learn 之 kNN 分类

直觉

给定一堆已有的样本数据,和一个被询问的数据点(红色五角星),我们如何找到离五角星最近的15个点?

先忽略在编程上的实现,想一想一个人如何主观地执行。嗯,他一定会把在五角附近的一些点中,分别计算每一个的距离,然后选最近的15个。这样可能只需要进行二三十次距离计算,而不是300次。

如图,只对紫圈里的点进行计算。

啊哈!问题来了。我们讲到的“附近”已经包含了距离的概念,如果不经过计算我们怎么知道哪个点是在五角星的“附近”?为什么我们一下就认出了“附近”而计算机做不到?那是因为我们在观看这张图片时,得到的输入是已经带有距离概念的影像,然而计算机在进行计算时得到的则是没有距离概念的坐标数据。如果要让一个人人为地从300组坐标里选出最近的15个,而不给他图像,那么他也省不了功夫,必须要把300个全部计算一遍才行。

这样来说,我们要做的就是在干巴巴的坐标数据上进行加工,将空间分割成小块,并以合理地方法将信息进行储存,这样方便我们读取“附近”的点。

切割

这只危险的兔子,它又回来了!它今天上了四个纹身,爱心、月牙、星星和眼泪,下面是它的照片。

我们来回答一个简单的问题:在这幅照片上,距离爱心最近的纹身是什么?记得上一篇文章中,我们选用的特征是每一只兔子的身高和体重;这次就不一样了,在这个问题中,每个纹身的特征是照片平面上的横轴和竖轴的坐标。

对于这个问题,如果进行蛮算的办法我们需要计算 3 次距离(分别和月亮、眼泪和星星算一次)。下面我们要做的是把整个空间按照左右和上下进行等分,并且把分割后的小空间以二叉树形式进行记录,这样可以很快地读取邻近的点而省去计算量。

好,我们先竖向沿中间把这个兔子切成两半

再沿横向从中间切成四份

再沿着竖向平分八份

最后再沿横向切一次。这次有些区域是完全空白的,我们就把它舍弃不要了,得到 14 份:

我们再按照上下左右的关系把切开的图片做成一个二叉树,树的每一个节点是一幅图,它的两个枝是这幅图平分出来的子图。

可以看出这个树状结构包含了很多局部性的信息,因为它的每一个节点都是按照上下或者左右进行平分的,因此如果两个点在树中的距离较近,那么它们的实际距离就是比较近的。

搜寻

接下来我们要通过这棵二叉树找到离爱心最近的纹身。

首先从树的最顶端开始,向下搜寻找到最底部包含爱心的节点。这个操作非常简单,因为每一次分割要么是沿着某纵线 x=ax=a 要么是沿着横线 y=ay=a,因此只需要判断爱心的 xx 或 yy 轴坐标是大于 aa 还是小于 aa,便知道是向左还是右边选择树枝。

在找到了爱心之后,我们沿着相同的路径向上攀爬。只爬了一节就发现了屁股上的两个纹身

这里看出,在8平分的情况下,爱心和月亮是在同一个区域的。在某种意义上来讲它们是“近”的,但是我们还不能确定它们是最近的,因此还要继续向上攀爬寻找。再继续向上爬两个节点,都没有出现爱心和月亮以外的纹身。在下面这个节点中

我们发现爱心和月亮之间的距离(红线)要小于爱心和分割线的距离(蓝线),也就是说,不论分割线的右边是什么情况,那边的纹身都不可能离爱心更近。因此可以判断,离爱心最近的图形是月亮。

这样,我们只计算了一次爱心和月亮之间的距离和一次爱心和分割线之间的距离,而不是分别计算爱心和其他三个纹身的距离。并且,要知道,爱心和分割线之间距离的计算非常简单,就是爱心的 xx 坐标和分割线的 xx 坐标的差(的绝对值),相比于计算两点之间的距离

((x1?y1)2+(x2?y2)2)????????????????????√((x1?y1)2+(x2?y2)2)

要省下很多计算量。

麻烦

啊,但也有可能这个搜寻最近点的过程没那么顺利。在上面的计算中,在找到了离爱心比较近的月亮之后,我们发现爱心距离分割线的距离比较远,因此确定月亮的确就是最近的。但是,在分割线的另一边有一个更近的纹身,那么情况就稍微复杂了。

就说这个兔子啊,又去加了两个纹身,一片叶子和一个圆圈。

二叉树分割上也相应地多出这两个纹身。我们想找到离爱心最近的纹身,所以依旧向下搜寻先找到爱心。

我们找来一张纸,记下在已访问节点中距离爱心最近的纹身和所对应的距离。现在这张纸还是空的。

向上爬了一节,发现那一节的另一个枝里有月亮,于是跑下去查看月亮的坐标,计算爱心和月亮的距离,并在纸上记录 (图形=月亮,距离=d1)(图形=月亮,距离=d1)。

再回到蓝圈的节点向上爬,继续向上爬。我们发现,d1d1(红线)大于爱心和分割线的距离(蓝线)。

也就是说分割线的另一边可能有更近的点,所以从另一个分枝开始向下搜,找到…

在另一个分枝中我们追溯到圆圈,并计算它与爱心的距离 d2d2,发现 d2>d1d2>d1,比月亮远,所以丢弃不要。

再向上爬一个节,我们发现 d1d1(红线)大于爱心和切分线之间的距离(蓝线)

因此,切分线的另一端可能有更近的纹身,因此我们从另一个树枝向下搜索…

找到了叶子。(所幸在这个分枝里只搜索到了叶子,如果有更多的图形的话,还需要进行多层的递归。具体的过程会在后面的详细篇中讲解。)计算叶子和爱心之间的距离,得 d3d3,并发现 d3<d1d3<d1,比月亮更近,于是更新纸上的记录为 (纹身=叶子,距离=d3)(纹身=叶子,距离=d3)。

再向上攀登一节,我们发现 d3d3 小于爱心和切分线的距离,因此另一边的数据就不用考虑了。

这次我们已经爬到了树的最顶端,完成了搜索,纸上记载的 (叶子,d3)(叶子,d3) 就是最近的纹身和对应的距离。

结语

在以上的算法中,当我们已经找到了比切分线更近的点时,就不需要继续搜索切分线另一边的点了,因为那些只会更远。于是,通过把整个空间进行分割并以树状结构进行记录,我们只需要在问题点附近的一些区域进行搜寻便可以找到最近的数据点,节省了大量的计算。

到此为止,本篇文章友好地介绍了如何使用二叉树的形式记录距离信息并快速地进行搜索,但文中所讲的还不是 kd 树。下一篇文章,kd 树算法之详细篇,将系统性地介绍 kd 树的定义和在 kd 树上的 kNN 算法。

本文由JoinQuant量化课堂退出,版权归JoinQuant所有,商业转载清联系我们获得授权,非商业转载请注明出处。

文章迭代记录
v1.0,2016-09-01,文章上线
时间: 2024-07-30 03:00:28

【转】【量化课堂】kd 树算法之思路篇的相关文章

【转】【量化课堂】一只兔子帮你理解 kNN

导语:商业哲学家 Jim Rohn 说过一句话,"你,就是你最常接触的五个人的平均."那么,在分析一个人时,我们不妨观察和他最亲密的几个人.同理的,在判定一个未知事物时,可以观察离它最近的几个样本,这就是 kNN(k最近邻)的方法. 作者:肖睿 编辑:宏观经济算命师 本文由JoinQuant量化课堂推出,本文的难度属于进阶(上),深度为 level-1 简介 kNN(k-Nearest Neighbours)是机器学习中最简单易懂的算法,它的适用面很广,并且在样本量足够大的情况下准确度

JavaWeb快速入门Session&amp;Cookie代码思路篇(七)

Cookie练习 简介 尝试了n种分享代码,最终没有找到好办法.我觉得贴代码,很难看下去.还不如写思路,今后代码篇改成思路篇 Session代码练习 1. 购物车思路 1. ListBookServlet类 1.1 显示该网站所有商品,每个商品加上购买链接(BuyServlet) 用Map集合存储Book对象 map.put("1", new Book("1","JavaWeb开发","老张","一本好书"

四则运算2之设计思路篇

四则运算2之设计思路篇 对于四则运算程序的新要求: 1.题目避免重复: 2.可定制(数量/打印方式): 3.可以控制一下参数: 是否有乘除法.数值范围.加减有无负数.除法有无余数.是否支持分数(真分数,假分数,...) 要求一. 首先解决第一个要求:题目避免重复.第一件事就是优化得到随机数的算法,在每次生成算式的循环中,首先排除被除数为0的除法算式,将得到的算式分别赋给四个字符串数组,根据本次随机得到的运算符分别在四个字符串数组中进行比较,默认交换律得到的两个式子满足要求,可以作为交换律的考察.

政府采购电子办公系统总结-思路篇

前言 政府采购电子办公系统是实现招标,投标,评标等过程的电子化管理.在这个系统中我所负责的是招标文件制作和档案管理.这篇文章里我主要介绍一下对于这两部分业务的实现思路. 招标文件制作 实现思路 提炼出招标文件中相对来说容易变化的内容,这些内容因不同的招标项目而异.如:招标项目名称,采购单位,招标开始日期等.这些信息做成表单由用户来填写. 制作一个招标文件模板,在出现上述可变内容的地方写成"{}". 在生成招标文件时获得用户的信息,利用Aspose.Word替换上述模板中的"{

【解决问题.思路篇】StringIndexOutOfBoundsException:String index out of range: -1

看到题目,就应该能想到应该是字符串过长引起的问题.下面咱们分析一下. 报错: 严重: Servlet.service()for servlet jsp threw exception java.lang.StringIndexOutOfBoundsException:String index out of range: -1 根据代码跟踪,发现是首页数据加载完之后就会报错,所以继续跟踪,发现了问题. 当我们输入了访问地址:localhost:8080页面以及数据加载完就会报错,抛异常.但是不影响

入侵指定网站的一些方法(思路篇)

如何入侵指定网站! 首先,观察指定网站. 入侵指定网站是需要条件的: 要先观察这个网站是动态还是静态的. 首先介绍下什么样站点可以入侵:我认为必须是动态的网站 如ASP.PHP. JSP等代码编写的站点 如果是静态的(.htm或html),一般是不会成功的. 如果要入侵的目标网站是动态的,就可以利用动态网站的漏洞进行入侵. Quote:以下是入侵网站常用方法: 1.上传漏洞 如果看到:选择你要上传的文件 [重新上传]或者出现“请登陆后使用”,80%就有漏洞了! 有时上传不一定会成功,这是因为Co

课堂作业-四则运算-设计思路

要求: 1.题目避免重复 2.可定制(数量/打印方式) 3.可以控制下列参数:是否有除法,是否有括号,数值范围,加减有无负数,除法有无余数,是否支持分数,是否支持小数,精确到多少位,打印中每行的间隔 设计思路 根据第一次的程序实现,已经实现了打印数量上的控制,直接确定了打印列数,其他实现基本功能 第一步:避免题目重复:将打印的结果保存,此后每次生成的结果与之前的结果比较. 第二步:实现可定制:通过用户输入数量数和打印的列数,实现可定制,将间隔参数也由用户来输入.参数由用户输入,将参数传递到所需函

非常好理解的KNN算法示例

参考链接:https://www.joinquant.com/post/2227?f=study&m=math:一只兔子帮你理解KNN https://www.joinquant.com/post/2627?f=study&m=math:kd 树算法之思路篇 https://www.joinquant.com/post/2843?f=study&m=math;kd 树算法之详细篇

从K近邻算法、距离度量谈到KD树、SIFT+BBF算法

从K近邻算法.距离度量谈到KD树.SIFT+BBF算法 从K近邻算法.距离度量谈到KD树.SIFT+BBF算法 前言 前两日,在微博上说:“到今天为止,我至少亏欠了3篇文章待写:1.KD树:2.神经网络:3.编程艺术第28章.你看到,blog内的文章与你于别处所见的任何都不同.于是,等啊等,等一台电脑,只好等待..”.得益于田,借了我一台电脑(借他电脑的时候,我连表示感谢,他说“能找到工作全靠你的博客,这点儿小忙还说,不地道”,有的时候,稍许感受到受人信任也是一种压力,愿我不辜负大家对我的信任)