近似最近鄰演算法 annoy解析

2022-07-16 10:48:09 字數 1878 閱讀 3267

**

annoy是高維空間求近似最近鄰的乙個開源庫。

annoy構建一棵二叉樹,查詢時間為o(logn)。

annoy通過隨機挑選兩個點,並使用垂直於這個點的等距離超平面將集合劃分為兩部分。

如圖所示,圖中灰色線是連線兩個點,超平面是加粗的黑線。按照這個方法在每個子集上迭代進行劃分。

依此類推,直到每個集合最多剩餘k個點,下圖是乙個k = 10 的情況。

相應的完整二叉樹結構:

隨機投影森林。

乙個思想依據是:在原空間中相鄰的點,在樹結構上也表現出相互靠近的特點,也就是說,如果兩個點在空間上相互靠近,那麼他們很可能被樹結構劃分到一起。

如果要在空間中查詢臨近點,我們可以在這個二叉樹中搜尋。上圖中每個節點用超平面來定義,所以我們可以計算出該節點往哪個方向遍歷,搜尋時間 log n

如上圖,我們找到了七個最近鄰,但是假如我們想找到更多的最近鄰怎麼辦?有些最近鄰是在我們遍歷的葉子節點的外邊的。

技巧1:使用優先佇列

如果乙個劃分的兩邊「靠得足夠近」(量化方式在後面介紹),我們就兩邊都遍歷。這樣就不只是遍歷乙個節點的一邊,我們將遍歷更多的點

我們可以設定乙個閾值,用來表示是否願意搜尋劃分「錯」的一遍。如果設定為0,我們將總是遍歷「對」的一片。但是如果設定成0.5,就按照上面的搜尋路徑。

這個技巧實際上是利用優先順序佇列,依據兩邊的最大距離。好處是我們能夠設定比0大的閾值,逐漸增加搜尋範圍。

技巧2:構建乙個森林

我們能夠用乙個優先順序佇列,同時搜尋所有的樹。這樣有另外乙個好處,搜尋會聚焦到那些與已知點靠得最近的那些樹——能夠把距離最遠的空間劃分出去

每棵樹都包含所有的點,所以當我們搜尋多棵樹的時候,將找到多棵樹上的多個點。如果我們把所有的搜尋結果的葉子節點都合在一起,那麼得到的最近鄰就非常符合要求。

依照上述方法,我們找到乙個近鄰的集合,接下來就是計算所有的距離和對這些點進行排序,找到最近的k個點。

很明顯,我們會丟掉一些最近的點,這也是為什麼叫近似最近鄰的原因。

annoy在實際使用的時候,提供了一種機制可以調整(搜尋

k),你能夠根據它來權衡效能(時間)和準確度(質量)。

tips:

1.距離計算,採用歸一化的歐氏距離:vectors = sqrt(2-2*cos(u, v)) 

2.向量維度較小(<100),即使維度到達1000變現也不錯

3.記憶體占用小

4.索引建立與查詢分離(特別是一旦樹已經建立,就不能新增更多項)

5.有兩個引數可以用來調節annoy 樹的數量n_trees和搜尋期間檢查的節點數量search_k

n_trees在構建時提供,並影響構建時間和索引大小。 較大的值將給出更準確的結果,但更大的索引。

search_k在執行時提供,並影響搜尋效能。 較大的值將給出更準確的結果,但將需要更長的時間返回。

如果不提供search_k,它將預設為n *

n_trees,其中n是近似最近鄰的數目。

否則,search_k和n_tree大致是獨立的,即如果search_k保持不變,n_tree的值不會影響搜尋時間,反之亦然。

基本上,建議在可用負載量的情況下盡可能大地設定n_trees,並且考慮到查詢的時間限制,建議將search_k設定為盡可能大。

最近鄰搜尋和近似最近鄰搜尋(NN和ANN)和庫

這樣查詢返回的前k個向量並不一定是最相似的k個向量,衡量ann演算法好不好的乙個依據是召回,每次ann請求返回的k個結果與使用暴力查詢的k個結果去比較,如果完全一致,說明是最好的。因為省了搜尋時間卻沒有影響效果。目前的ann演算法有基於圖 hnswlib 的,基於樹 pysparnn 的,基於雜湊 ...

K最近鄰演算法

參考 所謂k最近鄰,就是k個最近的鄰居的意思,說的是每個樣本都可以用它最接近的k個鄰居來代表。cover和hart在1968年提出了最初的鄰近演算法。knn是一種分類 classification 演算法,它輸入基於例項的學習 instance based learning 屬於懶惰學習 lazy ...

K最近鄰演算法

一 原理 k最近鄰演算法 k nearest neighbor,knn 是最基本的分類演算法,其基本原理是 從最近的k個鄰居 樣本 中,選擇出現次數最多的類別作為判定類別。k最近鄰演算法可以理解為是乙個分類演算法,常用於標籤的 如性別。實現knn演算法核心的一般思路 相似度計算 計算未知樣本和每個訓...