Kd Tree演算法詳解

忽逢桃林發表於2020-05-24

kd樹(k-dimensional樹的簡稱),是一種分割k維資料空間的資料結構,主要應用於多維空間關鍵資料的近鄰查詢(Nearest Neighbor)和近似最近鄰查詢(Approximate Nearest Neighbor)。

一、Kd-tree

其實KDTree就是二叉查詢樹(Binary Search Tree,BST)的變種。二叉查詢樹的性質如下:
1)若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結點的值;
2)若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結點的值;
3)它的左、右子樹也分別為二叉排序樹;
例如:
在這裡插入圖片描述

如果我們要處理的物件集合是一個K維空間中的資料集,我們首先需要確定是:怎樣將一個K維資料劃分到左子樹或右子樹?

在構造1維BST樹類似,只不過對於Kd樹,在當前節點的比較並不是通過對K維資料進行整體的比較,而是選擇某一個維度d,然後比較兩個K維資料在該維度 d上的大小關係,即每次選擇一個維度d來對K維資料進行劃分,相當於用一個垂直於該維度d的超平面將K維資料空間一分為二,平面一邊的所有K維資料 在d維度上的值小於平面另一邊的所有K維資料對應維度上的值。也就是說,我們每選擇一個維度進行如上的劃分,就會將K維資料空間劃分為兩個部分,如果我 們繼續分別對這兩個子K維空間進行如上的劃分,又會得到新的子空間,對新的子空間又繼續劃分,重複以上過程直到每個子空間都不能再劃分為止。以上就是構造 Kd-Tree的過程,上述過程中涉及到兩個重要的問題:

  1. 每次對子空間的劃分時,怎樣確定在哪個維度上進行劃分;
  2. 在某個維度上進行劃分時,怎樣確保建立的樹儘量地平衡,樹越平衡代表著分割得越平均,搜尋的時間也就是越少。

1、在哪個維度上進行劃分?
一種選取軸點的策略是median of the most spread dimension pivoting strategy,統計樣本在每個維度上的資料方差,挑選出對應方差最大值的那個維度。資料方差大說明沿該座標軸方向上資料點分散的比較開。這個方向上,進行資料分割可以獲得最好的平衡。
2、怎樣確保建立的樹儘量地平衡?
給定一個陣列,怎樣才能得到兩個子陣列,這兩個陣列包含的元素 個數差不多且其中一個子陣列中的元素值都小於另一個子陣列呢?方法很簡單,找到陣列中的中值(即中位數,median),然後將陣列中所有元素與中值進行 比較,就可以得到上述兩個子陣列。同樣,在維度d上進行劃分時,劃分點(pivot)就選擇該維度d上所有資料的中值,這樣得到的兩個子集合資料個數就基本相同了。

二、Kd-Tree的構建

1)、在K維資料集合中選擇具有最大方差的維度k,然後在該維度上選擇中值m為pivot對該資料集合進行劃分,得到兩個子集合;同時建立一個樹結點node,用於儲存;
2)、對兩個子集合重複(1)步驟的過程,直至所有子集合都不能再劃分為止;


舉個例子:
假設有6個二維資料點{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)},資料點位於二維空間內(如下圖中黑點所示)。kd樹演算法就是要確定圖1中這些分割空間的分割線(多維空間即為分割平面,一般為超平面)。下面就要通過一步步展示kd樹是如何確定這些分割線的。
在這裡插入圖片描述

  1. 分別計算x,y方向上資料的方差,得知x方向上的方差最大;
  2. 根據x軸方向的值2,5,9,4,8,7排序選出中值為7,所以該node中的data = (7,2)。這樣,該節點的分割超平面就是通過(7,2)並垂直於x軸的直線x = 7;
  3. 確定左子空間和右子空間。分割超平面x = 7將整個空間分為兩部分,如下圖所示。x < = 7的部分為左子空間,包含3個節點{(2,3),(5,4),(4,7)};另一部分為右子空間,包含2個節點{(9,6),(8,1)}。
    在這裡插入圖片描述
    k-d樹的構建是一個遞迴的過程。然後對左子空間和右子空間內的資料重複根節點的過程就可以得到下一級子節點(5,4)和(9,6)(也就是左右子空間的'根'節點),同時將空間和資料集進一步細分。如此反覆直到空間中只包含一個資料點,如下圖所示:
    在這裡插入圖片描述

三、Kd-Tree的最近鄰查詢

  • (1)將查詢資料Q從根結點開始,按照Q與各個結點的比較結果向下訪問Kd-Tree,直至達到葉子結點。
    其中Q與結點的比較指的是將Q對應於結點中的k維度上的值與中值m進行比較,若Q(k) < m,則訪問左子樹,否則訪問右子樹。達到葉子結點時,計算Q與葉子結點上儲存的資料之間的距離,記錄下最小距離對應的資料點,記為當前最近鄰點nearest和最小距離dis。
  • (2)進行回溯操作,該操作是為了找到離Q更近的“最近鄰點”。即判斷未被訪問過的分支裡是否還有離Q更近的點,它們之間的距離小於dis。
    如果Q與其父結點下的未被訪問過的分支之間的距離小於dis,則認為該分支中存在離P更近的資料,進入該結點,進行(1)步驟一樣的查詢過程,如果找到更近的資料點,則更新為當前的最近鄰點nearest,並更新dis。
    如果Q與其父結點下的未被訪問過的分支之間的距離大於dis,則說明該分支內不存在與Q更近的點。
    回溯的判斷過程是從下往上進行的,直到回溯到根結點時已經不存在與P更近的分支為止。
    注:判斷未被訪問過的樹分支中是否還有離Q更近的點,就是判斷"Q與未被訪問的樹分支的距離|Q(k) - m|"是否小於"Q到當前的最近鄰點nearest的距離dis"。從幾何空間上來看,就是判斷以Q為中心,以dis為半徑超球面是否與未被訪問的樹分支代表的超矩形相交。
    下面舉兩個例子來演示一下最近鄰查詢的過程。
    假設我們的kd樹就是上面通過樣本集{(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)}建立的。

例1:查詢點Q(2.1,3.1)
如下圖所示,紅色的點即為要查詢的點。通過圖4二叉搜尋,順著搜尋路徑很快就能找到當前的最鄰近點(2,3)。
在這裡插入圖片描述
在上述搜尋過程中,產生的搜尋路徑節點有<(7,2),(5,4),(2,3)>。為了找到真正的最近鄰,還需要進行'回溯'操作,首先以(2,3)作為當前最近鄰點nearest,計算其到查詢點Q(2.1,3.1)的距離dis為0.1414,然後回溯到其父節點(5,4),並判斷在該父節點的其他子節點空間中是否有距離查詢點Q更近的資料點。以(2.1,3.1)為圓心,以0.1414為半徑畫圓,如圖6所示。發現該圓並不和超平面y = 4交割,即這裡:|Q(k) - m|=|3.1 - 4|=0.9 > 0.1414,因此不用進入(5,4)節點右子空間中去搜尋。
在這裡插入圖片描述
再回溯到(7,2),以(2.1,3.1)為圓心,以0.1414為半徑的圓更不會與x = 7超平面交割,因此不用進入(7,2)右子空間進行查詢。至此,搜尋路徑中的節點已經全部回溯完,結束整個搜尋,返回最近鄰點(2,3),最近距離為0.1414。


例2:查詢點Q(2,4.5)
如下圖所示,同樣經過圖4的二叉搜尋,可得當前的最鄰近點(4,7),產生的搜尋路徑節點有<(7,2),(5,4),(4,7)>。首先以(4,7)作為當前最近鄰點nearest,計算其到查詢點Q(2,4.5)的距離dis為3.202,然後回溯到其父節點(5,4),並判斷在該父節點的其他子節點空間中是否有距離查詢點Q更近的資料點。以(2,4.5)為圓心,以為3.202為半徑畫圓,如圖7所示。發現該圓和超平面y = 4交割,即這裡:|Q(k) - m|=|4.5 - 4|=0.5 < 3.202,因此進入(5,4)節點右子空間中去搜尋。所以,將(2,3)加入到搜尋路徑中,現在搜尋路徑節點有<(7,2), (2, 3)>。同時,注意:點Q(2,4.5)與父節點(5,4)的距離也要考慮,由於這兩點間的距離3.04 < 3.202,所以將(5,4)賦給nearest,並且dist=3.04。
在這裡插入圖片描述
接下來,回溯至(2,3)葉子節點,點Q(2,4.5)和(2,3)的距離為1.5,比距離(5,4)要近,所以最近鄰點nearest更新為(2,3),最近距離dis更新為1.5。回溯至(7,2),如圖8所示,以(2,4.5)為圓心1.5為半徑作圓,並不和x = 7分割超平面交割,即這裡:|Q(k) - m|=|2 - 7|=5 > 1.5。至此,搜尋路徑回溯完。返回最近鄰點(2,3),最近距離1.5。
在這裡插入圖片描述

四、總結

Kd樹在維度較小時(比如20、30),演算法的查詢效率很高,然而當資料維度增大(例如:K≥100),查詢效率會隨著維度的增加而迅速下降。假設資料集的維數為D,一般來說要求資料的規模N滿足N>>2的D次方,才能達到高效的搜尋。

為了能夠讓Kd樹滿足對高維資料的索引,Jeffrey S. Beis和David G. Lowe提出了一種改進演算法——Kd-tree with BBF(Best Bin First),該演算法能夠實現近似K近鄰的快速搜尋,在保證一定查詢精度的前提下使得查詢速度較快。

相關文章