轉自https://www.cnblogs.com/futurehau/p/6524396.html
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設置為盡可能大。