ANN的方法分為三大類:基於樹的方法、哈希方法、矢量量化方法。brute-force搜索的方式是在全空間進行搜索,為了加快查找的速度,幾乎所有的ANN方法都是通過對全空間分割,將其分割成很多小的子空間,在搜索的時候,通過某種方式,快速鎖定在某一(幾)子空間,然后在該(幾個)子空間里做遍歷。
1、基於樹的方法
1.1 kd樹
1.2 Annoy
Annoy是一個以樹為數據結構的近似最近鄰搜索庫,並用在Spotify的推薦系統中。Annoy的核心是不斷用選取的兩個質心的法平面對空間進行分割,最終將每一個區分的子空間里面的樣本數據限制在K以內。對於待插入的樣本$x_i$,從根節點依次使用法向量跟$x_i$做內積運算,從而判斷使用法平面的哪一邊(左子樹or右子樹)。對於查詢向量$q_i$,采用同樣的方式(在樹結構上體現為從根節點向葉子節點遞歸遍歷),即可定位到跟$q_i$在同一個子空間或者鄰近的子空間的樣本,這些樣本即為$q_i$近鄰。為了提高查詢的召回,Annoy采用建立多棵樹的方式。
2、哈希方法
3、矢量量化方法
矢量量化方法,即vector quantization,其具體定義為:將一個向量空間中的點用其中的一個有限子集來進行編碼的過程。在矢量量化編碼中,關鍵是碼本的建立和碼字搜索算法。比如常見的聚類算法,就是一種矢量量化方法。而在ANN近似最近鄰搜索中,向量量化方法又以乘積量化(PQ, Product Quantization)最為典型。
PQ乘積量化的核心思想還是聚類,或者說具體應用到ANN近似最近鄰搜索上,K-Means是PQ乘積量化子空間數目為1的特例。PQ乘積量化生成碼本和量化的過程可以用如下圖示來說明:
在訓練階段,針對N個訓練樣本,假設樣本維度為128維,我們將其切分為4個子空間,則每一個子空間的維度為32維,然后我們在每一個子空間中,對子向量采用K-Means對其進行聚類(圖中示意聚成256類),這樣每一個子空間都能得到一個碼本。這樣訓練樣本的每個子段,都可以用子空間的聚類中心來近似,對應的編碼即為類中心的ID。如圖所示,通過這樣一種編碼方式,訓練樣本僅使用的很短的一個編碼得以表示,從而達到量化的目的。對於待編碼的樣本,將它進行相同的切分,然后在各個子空間里逐一找到距離它們最近的類中心,然后用類中心的id來表示它們,即完成了待編碼樣本的編碼。
在查詢階段,PQ同樣在計算查詢樣本與dataset中各個樣本的距離,只不過這種距離的計算轉化為間接近似的方法而獲得。PQ乘積量化方法在計算距離的時候,有兩種距離計算方式,一種是對稱距離,另外一種是非對稱距離。非對稱距離的損失小(也就是更接近真實距離),實際中也經常采用這種距離計算方式。下面過程示意的是查詢樣本來到時,以非對稱距離的方式(紅框標識出來的部分)計算到dataset樣本間的計算示意:
具體地,查詢向量來到時,按訓練樣本生成碼本的過程,將其同樣分成相同的子段,然后在每個子空間中,計算子段到該子空間中所有聚類中心得距離,如圖中所示,可以得到4*256個距離,這里為便於后面的理解說明,小白菜就把這些算好的距離稱作距離池。在計算庫中某個樣本到查詢向量的距離時,比如編碼為(124, 56, 132, 222)這個樣本到查詢向量的距離時,我們分別到距離池中取各個子段對應的距離即可,比如編碼為124這個子段,在第1個算出的256個距離里面把編號為124的那個距離取出來就可,所有子段對應的距離取出來后,將這些子段的距離求和相加,即得到該樣本到查詢樣本間的非對稱距離。所有距離算好后,排序后即得到我們最終想要的結果。
從上面這個過程可以很清楚地看出PQ乘積量化能夠加速索引的原理:即將全樣本的距離計算,轉化為到子空間類中心的距離計算。比如上面所舉的例子,原本brute-force search的方式計算距離的次數隨樣本數目N成線性增長,但是經過PQ編碼后,對於耗時的距離計算,只要計算4*256次,幾乎可以忽略此時間的消耗。另外,從上圖也可以看出,對特征進行編碼后,可以用一個相對比較短的編碼來表示樣本,自然對於內存的消耗要大大小於brute-force search的方式。
在某些特殊的場合,我們總是希望獲得精確的距離,而不是近似的距離,並且我們總是喜歡獲取向量間的余弦相似度(余弦相似度距離范圍在[-1,1]之間,便於設置固定的閾值),針對這種場景,可以針對PQ乘積量化得到的前top@K做一個brute-force search的排序。
倒排乘積量化
倒排PQ乘積量化(IVFPQ)是PQ乘積量化的更進一步加速版。其加速的本質逃不開小白菜在最前面強調的是加速原理:brute-force搜索的方式是在全空間進行搜索,為了加快查找的速度,幾乎所有的ANN方法都是通過對全空間分割,將其分割成很多小的子空間,在搜索的時候,通過某種方式,快速鎖定在某一(幾)子空間,然后在該(幾個)子空間里做遍歷。在上一小節可以看出,PQ乘積量化計算距離的時候,距離雖然已經預先算好了,但是對於每個樣本到查詢樣本的距離,還是得老老實實挨個去求和相加計算距離。但是,實際上我們感興趣的是那些跟查詢樣本相近的樣本(小白菜稱這樣的區域為感興趣區域),也就是說老老實實挨個相加其實做了很多的無用功,如果能夠通過某種手段快速將全局遍歷鎖定為感興趣區域,則可以舍去不必要的全局計算以及排序。倒排PQ乘積量化的”倒排“,正是這樣一種思想的體現,在具體實施手段上,采用的是通過聚類的方式實現感興趣區域的快速定位,在倒排PQ乘積量化中,聚類可以說應用得淋漓盡致。
倒排PQ乘積量化整個過程如下圖所示:
在PQ乘積量化之前,增加了一個粗量化過程。具體地,先對N個訓練樣本采用K-Means進行聚類,這里聚類的數目一般設置得不應過大,一般設置為1024差不多,這種可以以比較快的速度完成聚類過程。得到了聚類中心后,針對每一個樣本x_i,找到其距離最近的類中心c_i后,兩者相減得到樣本x_i的殘差向量(x_i-c_i),后面剩下的過程,就是針對(x_i-c_i)的PQ乘積量化過程,此過程不再贅述。
在查詢的時候,通過相同的粗量化,可以快速定位到查詢向量屬於哪個c_i(即在哪一個感興趣區域),然后在該感興趣區域按上面所述的PQ乘積量化距離計算方式計算距離。
參考:
https://yongyuan.name/blog/ann-search.html