在之前关于knn算法的文章里曾提到,对特征空间进行划分的方法为计算新的输入实例与训练实例之间的距离,因为在特征空间中2个特征实例的相似程度可以用距离来表示。一般我们采用的是欧式距离,也就是说每个新的输入实例都需要与所有的训练实例计算一次距离并排序。当训练集非常大的时候,计算就非常耗时、耗内存,导致算法的效率降低。
为了提高Knn的搜索效率,这里介绍一种可以减少计算距离次数的方法———kd树方法。
kd树
kd树(k-dimensional树的简称),是一种对k维空间中的实例点进行存储以便对其进行快速搜索的二叉树结构。利用kd树可以省去对大部分数据点的搜索,从而减少搜索的计算量。
kd 树是每个节点均为k维数值点的二叉树,其上的每个节点代表一个超平面,该超平面垂直于当前划分维度的坐标轴,并在该维度上将空间划分为两部分,一部分在其左子树,另一部分在其右子树。即若当前节点的划分维度为d,其左子树上所有点在d维的坐标值均小于当前值,右子树上所有点在d维的坐标值均大于等于当前值,本定义对其任意子节点均成立。
kd树的构造
上面对kd树的描述貌似说的又不是人话,哈哈,我一开始看的时候也是,不要慌,看看实例就知道是怎么一回事啦。这里举一简单的例子,以我们熟悉的二维空间里的数据为例:
集合(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)。
这就是利用kd树方法将这6个数据在特征空间中进行划分的二叉树。到底怎么划分的呢?看客莫慌,听我娓娓道来…
构建根节点时,此时的切分维度为x,如上点集合在x维从小到大排序为(2,3),(4,7),(5,4),(7,2),(8,1),(9,6);其中值为(7,2)。(注:2,4,5,7,8,9在数学中的中值为(5 + 7)/2=6,但因该算法的中值需在点集合之内,所以中值计算用的是 len(points)//2=3, points[3]=(7,2) )
(2,3),(4,7),(5,4)挂在(7,2)节点的左子树,(8,1),(9,6)挂在(7,2)节点的右子树。
构建(7,2)节点的左子树时,点集合(2,3),(4,7),(5,4)此时的切分维度为y,中值为(5,4)作为分割平面,(2,3)挂在其左子树,(4,7)挂在其右子树。
构建(7,2)节点的右子树时,点集合(8,1),(9,6)此时的切分维度也为y,中值为(9,6)作为分割平面,(8,1)挂在其左子树。至此k-d tree构建完成。
根据上面的步骤,kd树将二维空间的划分展示出来:
再从三维空间来看一下kd tree的构建及空间划分过程:首先,边框为红色的竖直平面将整个空间划分为两部分,此两部分又分别被边框为绿色的水平平面划分为上下两部分。最后此4个子空间又分别被边框为蓝色的竖直平面分割为两部分,变为8个子空间,此8个子空间即为叶子节点。
对于kd树划分特征空间的原理是不是越来越清晰了呢?
搜索kd树
上面我们将kd树构建好了,那如何利用kd树进行k近邻搜索呢?
看看书上对于算法的描述:
哇,反正一开始看,就是云里雾里,说的啥玩意。别慌,依旧是看实例(智商不够,看实例):
实例1
结论是:通过二叉搜索,顺着搜索路径很快就能找到最邻近的叶子节(2,3)。
首先假设(2,3)为“当前最近邻点”。最邻近点肯定位于以查询点为圆心且通过叶子节点的圆域内。为了找到真正的最近邻,还需要进行“回溯”操作:算法沿搜索路径反向查找是否有距离查询点更近的数据点。此例中是由点(2,3)回溯到其父节点(5,4),并判断在该父节点的其他子节点空间中是否有距离查询点更近的数据点,发现该圆并不和超平面y = 4交割,因此不用进入(5,4)节点右子空间中去搜索。
再回溯到(7,2),以(2.1,3.1)为圆心,以0.1414为半径的圆更不会与x = 7超平面交割,因此不用进入(7,2)右子空间进行查找。
至此,搜索路径中的节点已经全部回溯完,结束整个搜索,返回最近邻点(2,3),最近距离为0.1414。
So,easy!!!
实例2
结论:通过二叉搜索,顺着搜索路径很快就能找到最邻近的叶子节点(4,7)。
首先假设(4,7)为当前最近邻点,计算其与目标查找点的距离为3.202。回溯到(5,4),计算其与查找点之间的距离为3.041,小于3.202,所以“当前最近邻点”变成(5,4)。
以目标点(2,4.5)为圆心,以目标点(2,4.5)到“当前最近邻点”(5,4)的距离(即3.041)为半径作圆,如上图所示。可见该圆和y = 4超平面相交,所以需要进入(5,4)左子空间进行查找,即回溯至(2,3)叶子节点
(2,3)距离(2,4.5)比(5,4)要近,所以“当前最近邻点”更新为(2,3),最近距离更新为1.5。
回溯至(7,2),以(2,4.5)为圆心1.5为半径作圆,并不和x = 7分割超平面交割,如下图所示。至此,搜索路径回溯完。返回最近邻点(2,3),最近距离1.5。
对于kd树的具体内容基础介绍完了,看到这里是不是觉得还是挺简单的呀,哈哈。在实际应用中,我们不需要自己造轮子,直接在sklearn中调用即可:
这篇文章的主要目的是为了了解kd树到底是怎么优化Knn算法的计算效率的。虽然轮子都给我们造好了,但是我们得了解其中的原理。
对于想学习python编程以及数据分析的小伙伴可以免费领取相关学习资料,包括电子书和视频,这里并不像其他公众号一样,推荐的书籍和视频估计一辈子都看不完,什么500G学习资料视频免费送的那种,资料不在多,在精!同意的小伙伴点个赞。
还有,对机器学习感兴趣的朋友,可以在B站上搜索 吴恩达 机器学习 相关关键字。至于书籍,个人也只推荐 西瓜书 和 统计学习方法 作为入门教材(这里就不给电子版了,这2本书应该人手一本的,没事多翻翻)。其实,我也入门不久,视频和教材都是在边实战中边看,查缺补漏式的,所以都还没看完。
希望大家多关注,如果可以的话多多推送给身边对该领域感兴趣的小伙伴,谢谢!
领取专属 10元无门槛券
私享最新 技术干货