KNN算法/HNSW算法


高維稀疏數據進行快速相似查找,可以采用learning to hash,但高維稠密數據查找則采用annoy

learning to hash 參考:

https://blog.csdn.net/hero_fantao/article/details/70245284
《海量數據相似查找系列1 -- Minhashing & LSH & Simhash 技術匯總》

 

Annoy以及其他KNN算法可以看下面:

 

參考這篇文章:

https://zhuanlan.zhihu.com/p/152522906

《K近鄰算法哪家強?KDTree、Annoy、HNSW原理和使用方法介紹》

 

關於KNN算法,一個核心問題是:如何快速從數據集中找到和目標樣本最接近的K個樣本?

本文將從這個角度切入,介紹常用的K近鄰算法的實現方法。具體將從原理、使用方法、時間開銷和准確率對比等方面進行分析和實驗。

 

KNN算法的三要素:距離度量、k值的選擇和分類決策規則。

其中機器學習領域常用的距離度量方法,有歐式距離、余弦距離、曼哈頓距離、dot內積等

主流的近鄰算法都支持上述不同的距離度量。其中n維特征空間的a、b向量的歐式距離 [公式] 體現數值上的絕對差異,而余弦距離基於余弦相似度(兩個向量間夾角的余弦值),體現方向上的相對差異。如果對向量做歸一化處理,二者的結果基本是等價的。

 

首先最直觀的想法(暴力法),是線性掃描法。將待預測樣本和候選樣本逐一比對,最終挑選出距離最接近的k個樣本即可,時間復雜度O(n)。

 

常用的存儲結構可以分為樹和圖兩大類。樹結構的代表是KDTree,以及改進版BallTree和Annoy等;基於圖結構的搜索算法有HNSW等。

 

KDTree

KD樹的構造:

一顆構建好的KD-Tree中,任何一個非葉節點都有一個特征空間切割維度,在這個維度上,所有左子樹的節點都小於它,所有右子樹的節點都大於等於它;

構建的步驟如下:

  1. 計算數據集在特征空間每個維度上的方差,將方差最大的維度作為當前的分割維度;
  2. 將數據集在分割維度上排序后,找到中位數,假設在排序的數據集中位置為m;
  3. 構建節點,保存中位數和當前分割維度;
  4. 將數據集中在當前維度上小於中位數(從0到m-1)作為左子樹的數據集;大於等於中位數(從m+1到最后)的數據作為右子樹的數據集;重復上面的步驟,繼續構建左右子樹;直到整個KD-Tree構建完成;

 

KD樹的查找:

從root節點開始,DFS搜索直到葉子節點,同時在stack中順序存儲已經訪問的節點。
如果搜索到葉子節點,當前的葉子節點被設為最近鄰節點。
然后通過stack回溯:
如果當前點的距離比最近鄰點距離近,更新最近鄰節點.
然后檢查以最近距離為半徑的圓是否和父節點的超平面相交.
如果相交,則必須到父節點的另外一側,用同樣的DFS搜索法,開始檢查最近鄰節點。
如果不相交,則繼續往上回溯,而父節點的另一側子節點都被淘汰,不再考慮的范圍中.
當搜索回到root節點時,搜索完成,得到最近鄰節點。

 

 

kd樹在維數小於20時效率最高,一般適用於訓練實例數遠大於空間維數時的k近鄰搜索;當空間維數接近訓練實例數時,它的效率會迅速下降,幾乎接近線形掃描。

 

Annoy

annoy全稱“Approximate Nearest Neighbors Oh Yeah”

 

是一種適合實際應用的快速相似查找算法。Annoy 同樣通過建立一個二叉樹來使得每個點查找時間復雜度是O(log n),和kd樹不同的是,annoy沒有對k維特征進行切分。

annoy的每一次空間划分,可以看作聚類數為2的KMeans過程。收斂后在產生的兩個聚類中心連線之間建立一條垂線(圖中的黑線),把數據空間划分為兩部分。

 

直到每個子空間最多只剩下K個數據節點,划分結束。

 

最終生成的二叉樹具有如下類似結構,二叉樹底層是葉子節點記錄原始數據節點,其他中間節點記錄的是分割超平面的信息。

 

 

 

在 ANN 領域,最常見的兩個問題是:

  1. 如果我們想要 Top K 的點,但是該區域的點集數量不足 K,該怎么辦?
  2. 如果真實的 Top K 中部分點不在這個區域,該怎么辦?

作者用了兩個技巧來解決這個問題:

    1. 使用優先隊列(priority queue):將多棵樹放入優先隊列,逐一處理;並且通過閾值設定的方式,如果查詢的點與二叉樹中某個節點比較相似,那么就同時走兩個分支,而不是只走一個分支;
    2. 使用森林(forest of trees):構建多棵樹,采用多個樹同時搜索的方式,得到候選集 Top M(M > K),然后對這 M 個候選集計算其相似度或者距離,最終進行排序就可以得到近似 Top K 的結果。

 

最后提一點,annoy接口中一般需要調整的參數有兩個:查找返回的topk近鄰和樹的個數。一般樹越多,精准率越高但是對內存的開銷也越大,需要權衡取舍(tradeoff)。

 

HNSW

和前幾種算法不同,HNSW(Hierarchcal Navigable Small World graphs)是基於圖存儲的數據結構。

 

 

假設我們現在有13個2維數據向量,我們把這些向量放在了一個平面直角坐標系內,隱去坐標系刻度,它們的位置關系如上圖所示。
朴素查找法:不少人腦子里都冒出過這樣的朴素想法,把某些點和點之間連上線,構成一個查找圖,存儲備用;當我想查找與粉色點最近的一點時,我從任意一個黑色點出發,計算它和粉色點的距離,與這個任意黑色點有連接關系的點我們稱之為“友點”(直譯),然后我要計算這個黑色點的所有“友點”與粉色點的距離,從所有“友點”中選出與粉色點最近的一個點,把這個點作為下一個進入點,繼續按照上面的步驟查找下去。如果當前黑色點對粉色點的距離比所有“友點”都近,終止查找,這個黑色點就是我們要找的離粉色點最近的點。

HNSW算法就是對上述朴素思想的改進和優化。為了達到快速搜索的目標,hnsw算法在構建圖時還至少要滿足如下要求:1)圖中每個點都有“友點”;2)相近的點都互為“友點”;3)圖中所有連線的數量最少;4)配有高速公路機制的構圖法。

 HNSW低配版NSW論文中配了這樣一張圖,短黑線是近鄰點連線,長紅線是“高速公路機制”,如此可以大幅減少平均搜索的路徑長度。

 

 

 在NSW基礎之上,HNSW加入了跳表結構做了進一步優化。最底層是所有數據點,每一個點都有50%概率進入上一層的有序鏈表。這樣可以保證表層是“高速通道”,底層是精細查找。通過層狀結構,將邊按特征半徑進行分層,使每個頂點在所有層中平均度數變為常數,從而將NSW的計算復雜度由多重對數復雜度降到了對數復雜度。

 

 

小結

本文介紹了幾種常用的k近鄰查找算法,kdtree是KNN的一種基本實現算法;考慮到並發、延時等要素,annoy、hnsw是可以在實際業務中落地的算法,其中bert/sentence-bert+hnsw的組合會有不錯的召回效果。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM