1. 程式人生 > >最近鄰算法

最近鄰算法

int 們的 無需 選擇 遞歸 樣本 數據結構 樹結構 嵌入

最近鄰算法

簡介

NearestNeighbors (最近鄰)實現了 unsupervised nearest neighbors learning(無監督的最近鄰學習)。 它為三種不同的最近鄰算法提供統一的接口:BallTree, KDTree, 還有基於 sklearn.metrics.pairwise 的 brute-force 算法。算法的選擇可通過關鍵字 ‘algorithm‘ 來控制, 並必須是 [‘auto‘, ‘ball_tree‘, ‘kd_tree‘, ‘brute‘] 其中的一個。當默認值設置為 ‘auto‘ 時,算法會嘗試從訓練數據中確定最佳方法。

暴力計算

最簡單的近鄰搜索的實現涉及數據集中所有成對點之間距離的暴力計算: 對於 D 維度中的 N 個樣本來說, 這個方法的復雜度是 O[D N^2]。 對於小數據樣本,高效的暴力近鄰搜索是非常有競爭力的。 然而,隨著樣本數 N 的增長,暴力方法很快變得不切實際了。

在 sklearn.neighbors 類中, 暴力近鄰搜索通過關鍵字 algorithm = ‘brute‘ 來指定,並通過 sklearn.metrics.pairwise 中的例程來進行計算。

K-D 樹

為了解決效率低下的暴力計算方法,已經發明了大量的基於樹的數據結構。總的來說, 這些結構試圖通過有效地編碼樣本的 aggregate distance (聚合距離) 信息來減少所需的距離計算量。 基本思想是,若 A 點距離 B 點非常遠,B 點距離 C 點非常近, 可知 A 點與 C 點很遙遠,不需要明確計算它們的距離。 通過這樣的方式,近鄰搜索的計算成本可以降低為 O[D N log(N)] 或更低。 這是對於暴力搜索在大樣本數 N 中表現的顯著改善。

利用這種聚合信息的早期方法是 KD tree 數據結構(* K-dimensional tree* 的簡寫), 它將二維 Quad-trees 和三維 Oct-trees 推廣到任意數量的維度. KD 樹是一個二叉樹結構,它沿著數據軸遞歸地劃分參數空間,將其劃分為嵌入數據點的嵌套的各向異性區域。 KD 樹的構造非常快:因為只需沿數據軸執行分區, 無需計算 D-dimensional 距離。 一旦構建完成, 查詢點的最近鄰距離計算復雜度僅為 O[log(N)] 。 雖然 KD 樹的方法對於低維度 (D < 20) 近鄰搜索非常快, 當 D 增長到很大時, 效率變低: 這就是所謂的 “維度災難” 的一種體現。 在 scikit-learn 中, KD 樹近鄰搜索可以使用關鍵字 algorithm = ‘kd_tree‘ 來指定, 並且使用類 KDTree 來計算。

Ball 樹

為了解決 KD 樹在高維上效率低下的問題, ball 樹 數據結構就被研發出來了. 其中 KD 樹沿卡迪爾軸(即坐標軸)分割數據, ball 樹在沿著一系列的 hyper-spheres 來分割數據. 通過這種方法構建的樹要比 KD 樹消耗更多的時間, 但是這種數據結構對於高結構化的數據是非常有效的, 即使在高維度上也是一樣.

ball 樹將數據遞歸地劃分為由質心 C 和半徑 r 定義的節點,

使得節點中的每個點位於由 r 和 C 定義的 hyper-sphere 內. 通過使用 triangle inequality(三角不等式) 減少近鄰搜索的候選點數:

|x+y| <= |x| + |y|

通過這種設置, 測試點和質心之間的單一距離計算足以確定距節點內所有點的距離的下限和上限. 由於 ball 樹節點的球形幾何, 它在高維度上的性能超出 KD-tree, 盡管實際的性能高度依賴於訓練數據的結構. 在 scikit-learn 中, 基於 ball 樹的近鄰搜索可以使用關鍵字 algorithm = ‘ball_tree‘ 來指定, 並且使用類 sklearn.neighbors.BallTree 來計算. 或者, 用戶可以直接使用 BallTree 類.

使用

from sklearn.neighbors import NearestNeighbors

import numpy as np

X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])

# n_neighbors默認5,algorithm默認‘auto’

nbrs = NearestNeighbors(n_neighbors=3, algorithm=‘ball_tree‘).fit(X)

# indices得到k個最近鄰的索引,distances得到與k個最近鄰的距離

distances, indices = nbrs.kneighbors(X)

indices

>>> array([[0, 1, 2],

[1, 0, 2],

[2, 1, 0],

[3, 4, 5],

[4, 3, 5],

[5, 4, 3]], dtype=int64)

Distances

>>> array([[ 0. , 1. , 2.23606798],

[ 0. , 1. , 1.41421356],

[ 0. , 1.41421356, 2.23606798],

[ 0. , 1. , 2.23606798],

[ 0. , 1. , 1.41421356],

[ 0. , 1.41421356, 2.23606798]])

也可以直接使用:

from sklearn.neighbors import KDTree

import numpy as np

X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])

kdt = KDTree(X, leaf_size=30, metric=‘euclidean‘)

#得到k個最近鄰的索引

kdt.query(X, k=2, return_distance=False)

最近鄰算法