1. 程式人生 > >KNN(k-nearest neighbors) K近鄰演算法深入

KNN(k-nearest neighbors) K近鄰演算法深入

K近鄰法是機器學習中一種很基本的演算法,可以用做分類和迴歸,主要的思想跟我們生活中近墨者黑,近朱者赤的思想雷同。例如我們看一個人人品的好壞只需要看與之相交的人的品格好壞。KNN演算法分類和迴歸主要區別在於最後做預測時候的決策方式不同:分類時我們選取與之相近的K個樣本採取多數表決法來判斷類別,迴歸時我們選取K個樣本的平均值來作為預測值。

1.KNN演算法的三個要素

  • K值     對於K值的選取可根據我們的經驗,然而有時候並不是那麼的好,我們需要通過交叉驗證來不斷地調節K值,直到選擇到滿意的值。K選擇較小的值,就會使用訓練例項中較小領域的訓練例項進行預測,訓練的誤差會減小,那也就意味這隻有與之相近的樣本才對預測結果影響,這樣會使模型變得複雜,容易發生過擬合。K選擇較小的值,就會使用訓練例項中較大的訓練例項進行預測,這樣會使模型變得簡單,模型的泛化能力增強,但同時也意味著與之相距較遠的例項會對其預測結果產生影響,會使訓練誤差增大。
  • 距離度量     我們有很多種距離度量的方式,可推薦參考   距離度量 。standard Euclidean distance(標準歐式距離)是最常見的選擇。
  • 決策規則     分類我們一般使用的是對數表決法,迴歸採用K個樣本標籤均值

2. KNN的實現

  • 暴力實現
  • 最簡單的近鄰搜尋的實現涉及資料集中所有成對點之間距離的暴力計算: 對於 D 維度中的 N 個樣本來說, 這個方法的複雜度是 O[D N^2]。 對於小資料樣本,高效的暴力近鄰搜尋是非常有競爭力的。 然而,隨著樣本數 N
     的增長,暴力方法很快變得不切實際了。
  •  
  •  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 增長到很大時, 效率變低: 這就是所謂的 “維度災難” 的一種體現。

  • ball 樹

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

    ball 樹將資料遞迴地劃分為由質心 C 和半徑 r 定義的節點, 使得節點中的每個點位於由 r 和 C 定義的 hyper-sphere 內. 通過使用 triangle inequality(三角不等式) 減少近鄰搜尋的候選點數:

    |x+y| \leq |x| + |y|

    通過這種設定, 測試點和質心之間的單一距離計算足以確定距節點內所有點的距離的下限和上限.

3.KNN幾種實現的效能及時間複雜度

對於給定資料集的最優演算法是一個複雜的選擇, 並且取決於多個因素:

  • 樣本數量 N (i.e. n_samples) 和維度 D (例如. n_features).

    • Brute force 查詢時間以 O[D N] 增長

    • Ball tree 查詢時間大約以 O[D \log(N)] 增長

    • KD tree 的查詢時間 D 的變化是很難精確描述的.

      對於較小的 D (小於20) 的成本大約是 O[D\log(N)], 並且 KD 樹更加有效.

      對於較大的 D 成本的增加接近 O[DN], 由於樹結構引起的開銷會導致查詢效率比暴力還要低.

    對於小資料集 (N 小於30), \log(N) 相當於 N, 暴力演算法比基於樹的演算法更加有效.

4.KNN應用的一點建議

最近鄰迴歸是用在資料標籤為連續變數,而不是離散變數的情況下。分配給查詢點的標籤是由它的最近鄰標籤的均值計算而來的。

scikit-learn 實現了兩種不同的最近鄰迴歸:KNeighborsRegressor 基於每個查詢點的 k 個最近鄰實現, 其中 k 是使用者指定的整數值。RadiusNeighborsRegressor 基於每個查詢點的固定半徑 r 內的鄰點數量實現, 其中 r 是使用者指定的浮點數值。

基本的最近鄰迴歸使用統一的權重:即,本地鄰域內的每個鄰點對查詢點的分類貢獻一致。 在某些環境下,對鄰點加權可能是有利的,使得附近點對於迴歸所作出的貢獻多於遠處點。 這可以通過 weights 關鍵字來實現。預設值 weights = 'uniform' 為所有點分配同等權重。 而 weights = 'distance' 分配的權重與查詢點距離呈反比。 或者,使用者可以自定義一個距離函式用來計算權重。