一、kd树概念
kd树(K-Dimensional Tree)是一种对K维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。
kd树是二叉树,表示对K维空间的一个划分(partition);构造Kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域;Kd树的每个结点对应于一个k维超矩形区域。
KD树算法包括三步:第一步是建树,第二部是搜索最近邻,最后一步是预测。
注:kd树中的k指的是k维空间,knn中的k指的是距离目标点最近的k个实例样本点。
二、构建kd树
我们首先来看建树的方法。KD树建树采用的是从m个样本的k维特征中,分别计算k个特征的取值的方差,用方差最大的第i维特征
k
i
k_i
ki来作为根节点。对于这个特征,我们选择特征
k
i
k_i
ki的取值的中位数
k
v
k_v
kv对应的样本作为划分点,对于所有第n维特征的取值小于
k
v
k_v
kv的样本,我们划入左子树;对于第n维特征的取值大于等于
k
v
k_v
kv的样本,我们划入右子树。对于左子树和右子树,我们采用和刚才同样的办法来找方差最大的特征来做跟节点,递归的生成KD树。
我们用6个二维的样本{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}来构建kd树,具体步骤如下:
1)、寻找划分样本集的特征,即选取最大方差的维度特征。6个数据点在x,y维度上的数据方差分别为6.97,5.37,所以在x轴上方差更大,确定用第1维特征建树。
2)、寻找划分点,即确定划分样本集的特征上的分割点。根据x维上的值将数据排序,6个数据的中值(所谓中值,即中间大小的值)为7,所以划分点的数据是(7,2)。这样,该节点的分割超平面就是通过(7,2)并垂直于:划分点维度的直线x=7;
3)、确定左子空间和右子空间,即构建左、右子树。 分割超平面x=7将整个空间分为两部分:x<=7的部分为左子空间,包含3个节点={(2,3),(5,4),(4,7)};另一部分为右子空间,包含2个节点={(9,6),(8,1)}。
4)、循环1)2)3)步骤,直至左右子树无法分割而得到最终的kd树。用同样的办法划分左子树的节点{(2,3),(5,4),(4,7)}和右子树的节点{(9,6),(8,1)},最终得到KD树。
最终的kd树和构建kd树寻找的节点,如下所示:
三、KD树搜索最近邻
1、搜索原理
当我们生成KD树以后,就可以去预测测试集里面的样本目标点了。对于一个目标点,我们首先在KD树里面找到包含目标点的叶子节点。以目标点为圆心,以目标点到叶子节点样本实例的距离为半径,得到一个超球体,最近邻的点一定在这个超球体内部。然后返回叶子节点的父节点,检查另一个子节点包含的超矩形体是否和超球体相交,如果相交就到这个子节点寻找是否有更加近的近邻,有的话就更新最近邻。如果不相交那就简单了,我们直接返回父节点的父节点,在另一个子树继续搜索最近邻。当回溯到根节点时,算法结束,此时保存的最近邻节点就是最终的最近邻。
从上面的描述可以看出,KD树划分后可以大大减少无效的最近邻搜索,很多样本点由于所在的超矩形体和超球体不相交,根本不需要计算距离。大大节省了计算时间。
2、搜索实例
我们用刚建立的KD树,来看对点(2,4.5)找最近邻的过程。
先进行二叉查找,先从(7,2)查找到(5,4)节点,在进行查找时是由y = 4为分割超平面的,由于查找点为y值为4.5,因此进入右子空间查找到(4,7),形成搜索路径<(7,2),(5,4),(4,7)>,但 (4,7)与目标查找点的距离为3.202,而(5,4)与查找点之间的距离为3.041,所以(5,4)为查询点的最近点; 以(2,4.5)为圆心,以3.041为半径作圆,如下图所示。可见该圆和y = 4超平面交割,所以需要进入(5,4)左子空间进行查找,也就是将(2,3)节点加入搜索路径中得<(7,2),(2,3)>;于是接着搜索至(2,3)叶子节点,(2,3)距离(2,4.5)比(5,4)要近,所以最近邻点更新为(2,3),最近距离更新为1.5;回溯查找至(5,4),直到最后回溯到根结点(7,2)的时候,以(2,4.5)为圆心1.5为半径作圆,并不和x = 7分割超平面交割,如下图所示。至此,搜索路径回溯完,返回最近邻点(2,3),最近距离1.5。
对应的图如下:
三、KD树预测
有了KD树搜索最近邻的办法,KD树的预测就很简单了,在KD树搜索最近邻的基础上,我们选择到了第一个最近邻样本,就把它置为已选。在第二轮中,我们忽略置为已选的样本,重新选择最近邻,这样跑k次,就得到了目标的K个最近邻,然后根据多数表决法,如果是KNN分类,预测为K个最近邻里面有最多类别数的类别。如果是KNN回归,用K个最近邻样本输出的平均值作为回归预测值。