Facebook-Faiss原理


Faiss是一個Facebook AI團隊開源的庫,全稱為Facebook AI Similarity Search,該開源庫針對高維空間中的海量數據(稠密向量),提供了高效且可靠的相似性聚類和檢索方法,可支持十億級別向量的搜索,是目前最為成熟的近似近鄰搜索庫。

官方資源地址https://github.com/facebookresearch/faiss

Faiss本質上是一個向量(矢量)數據庫。進行搜索時,基礎是原始向量數據庫,基本單位是單個向量,默認輸入一個向量x,返回和x最相似的k個向量。

在使用Faiss進行query向量的相似性搜索之前,需要將原始的向量集構建封裝成一個索引文件(index file)並緩存在內存中,提供實時的查詢計算。Index繼承了一組向量庫,作用是對原始向量集進行預處理和封裝。所有向量在建立前需要明確向量的維度d,大多數的索引還需要訓練階段來分析向量的分布(除了IndexFlatL2)。在第一次構建索引文件的時候,需要經過TrainAdd兩個過程。后續如果有新的向量需要被添加到索引文件的話還可以有一個Add操作從而實現增量build索引。當索引被建立就可以進行后續的search操作了。

為1個或者多個向量找它的k個最近鄰的向量,可以認為是一個最近鄰問題。可以計算x和向量集合中所有向量的歐式距離(或者cos距離,點積等),然后從小到大排序,這看上去似乎並不需要很復雜的算法,但在現實場景中往往面臨海量的數據,當數據量在上億級別的時候,這種暴力搜索的方法就並不可取了。所以在進行大規模向量檢索時,通常采用的是近似最近鄰算法(Approximate Nearest Neighbor,ANN)。和暴力解法不同的是,ANN算法不需要和集合中的所有向量計算距離,而是有選擇地和部分向量計算距離,這樣得到的答案往往不是最精確的,但是在效率上卻大大提高了。所以向量檢索其實是一個精度和效率的權衡問題。

關於乘積量化

矢量量化方法,即vector quantization,其具體定義為: 將向量空間的點用一個有限子集來進行編碼的過程。常見的聚類算法,都是一種矢量量化方法。而在ANN(Approximate Nearest Neighbor,近似最近鄰) 搜索問題中,向量量化方法又以乘積量化(PQ, Product Quantization)最為典型。

PQ有一個Pre-train的過程,一般分為兩步操作,第一步「Cluster」,第二步「Assign」,這兩步合起來就是對應到前文提到Faiss數據流的「Train」階段,可以以一個128維的向量庫為例:

 

PQ乘積量化的核心思想還是聚類,K-Means就是PQ乘積量化子空間數目為1的特例。

在做PQ之前,首先需要指定一個參數M,這個M就是指定向量要被切分成多少段,在上圖中M=4,所以向量庫的每一個向量就被切分成了4段,然后把所有向量的第一段取出來做Clustering得到256個簇心(256是一個作者拍的經驗值);再把所有向量的第二段取出來做Clustering得到256個簇心,直至對所有向量的第N段做完聚類,從而最終得到了256*M個簇心。

做完Cluster,就開始對所有向量做Assign操作。這里的Assign就是把原來的N維的向量映射到M個數字,以N=128,M=4為例,首先把向量切成四段,然后對於每一段向量,都可以找到對應的最近的簇心 ID,4段向量就對應了4個簇心 ID,一個128維的向量就變成了一個由4個ID組成的向量,這樣就可以完成了Assign操作的過程 -- 現在,128維向量變成了4維,每個位置都只能取0~127,這就完成了向量的壓縮。

完成了PQ的Pre-train,就可以看看如何基於PQ做向量檢索了。查詢過程如下圖所示:

 

同樣是以N=128,M=4為例,對於每一個查詢向量,以相同的方法把128維分成4段32維向量,「然后計算每一段向量與之前預訓練好的簇心的距離」,得到一個4*256的表。然后就可以開始計算查詢向量與庫里面的向量的距離。此時,庫的向量已經被量化成M個簇心 ID,而查詢向量的M段子向量與各自的256個簇心距離已經預計算好了,所以在計算兩個向量的時候只用查M次表,比如的庫里的某個向量被量化成了[124, 56, 132, 222], 那么首先查表得到查詢向量第一段子向量與其ID為124的簇心的距離,然后再查表得到查詢向量第二段子向量與其ID為56的簇心的距離......最后就可以得到四個距離d1、d2、d3、d4,查詢向量跟庫里向量的距離d = d1+d2+d3+d4。

所以在提出的例子里面,使用PQ只用4×256次128/4維向量距離計算加上4xN次查表,而最原始的暴力計算則有N次128維向量距離計算,很顯然隨着向量個數N的增加,后者相較於前者會越來越耗時。

關於倒排索引
PQ優化了向量距離計算的過程,但是假如庫里面的向量特別多,依然逃不了一個遍歷整個庫的過程,效率依舊還是不夠高,所以這時就有了Faiss用到的另外一個關鍵技術——Inverted File System。倒排乘積量化(IVFPQ)是乘積量化(PQ)的更進一步加速版。其加速的本質就是在前面強調的是加速原理:暴力搜索的方式是在「全空間」進行搜索,為了加快查找的速度,幾乎所有的ANN方法都是通過「對全空間分割(聚類)」,將其分割成很多小的子空間,在搜索的時候,通過某種方式,快速鎖定在某一(幾)子空間,然后在該(幾個)子空間里做遍歷。

PQ乘積量化計算距離的時候,距離雖然已經預先算好了,但是對於每個樣本到查詢樣本的距離,還是得老老實實挨個去求和相加計算距離。但是,實際上我們感興趣的是那些跟查詢樣本相近的樣本(下面稱之為“感興趣區域”),也就是說老老實實挨個相加其實做了很多的無用功,如果能夠通過某種手段快速「將全局遍歷鎖定為感興趣區域」,則可以舍去不必要的全局計算以及排序。倒排PQ乘積量化的”倒排“,正是這樣一種思想的體現,在具體實施手段上,采用的是通過聚類的方式實現感興趣區域的快速定位,在倒排PQ乘積量化中,聚類可以說應用得淋漓盡致。

要想減少需要計算的目標向量的個數,做法就是直接對庫里所有向量做KMeans Clustering,假設簇心個數為1024。那么每來一個query向量,首先計算其與1024個粗聚類簇心的距離,然后選擇距離最近的top N個簇,只計算查詢向量與這幾個簇底下的向量的距離,計算距離的方法就是前面說的PQ。

Faiss具體實現有一個小細節,就是在計算查詢向量和一個簇底下的向量的距離的時候,所有向量都會被轉化成與簇心的殘差,這應該就是類似於歸一化的操作,使得后面用PQ計算距離更准確一點。使用了IVF過后,需要計算距離的向量個數就少了幾個數量級,最終向量檢索就變成一個很快的操作。

主要步驟

1.構造數據
Faiss處理固定維度d維的向量集合,通常從10維到100維均可。這些集合可以被存儲在矩陣中。假設按行存儲,向量i的第j維元素被存儲在矩陣的第i行第j列。Faiss使用的是32位的浮點數矩陣。需要兩個矩陣:

  • xb用於構建數據庫,包含我們要被構建索引的,即將被檢索的所有向量。它的維度是nb * d。
  • xq是我們需要尋找其最近鄰的查詢向量,它的維度是nq * d。如果只有單一query,nq=1。

2. 構建索引並向其中添加向量

Faiss通常構建一個index對象,它封裝了數據庫的向量集合,並且有選擇地對它們進行一些預處理,使得檢索更加高效。


所有的index在它們構建的時候都需要知道它們要操作的向量維度。然后,許多index需要一個訓練(training)階段,來分析向量的分布。對於IndexFlatL2索引來說,可以跳過這個步驟。
當索引被構建和訓練之后,就可以在index上執行兩個操作:add和search。
向index中添加元素稱作add。我們也可以查看index的兩個狀態變量:is_trained,一個boolean型的變量指示是否需要訓練;ntotal,表示被構建索引的向量的個數。
一些index可以給每個向量存儲它們對應的int型ID(但不是IndexFlatL2)。如果沒有提供ID,add只是用向量原始的作為id,比如:第1個向量的id是0,第2個向量的id是1,等等。

三種常用的索引:IndexFlatL2、IndexIVFFlat、IndexIVFPQ

1)IndexFlatL2 - 最基礎的索引

indexFlatL2是最簡單最常用的索引類型,對向量執行暴力的L2距離搜索,也是唯一可以保證精確結果的索引類型

2)IndexIVFFlat-更快的索引

為了加快搜索的速度,我們可以將數據集分割為幾部分,每個數據向量只能落在一個cell中。查詢時只需要查詢query向量落在cell中的數據了,降低了距離計算次數。這就是上文說的倒排索引方法。

IndexIVFFlat需要一個訓練的階段,其與另外一個索引quantizer有關,通過quantizer來判斷屬於哪個cell。IndexIVFFlat在搜索的時候,引入了nlist(cell的數量)與nprobe(執行搜索的cell數)參數。增大nprobe可以得到與brute-force更為接近的結果,nprobe就是速度與精度的調節器。

3)IndexIVFPQ-內存占用更小的索引

如果一個索引在處理很大規模向量數據時都往緩存中存儲完整的向量,那么對硬件的壓力會特別大, 為了擴展到非常大的數據集,Faiss提供了基於乘積量化(Product Quantizer)的方法來壓縮存儲的向量,減小數據量。除此之外,IndexIVFPQ會應用到K-means聚類中心算法,PCA降維等算法來對向量集進行壓縮、編碼。但是由於向量數據是有損壓縮的,所以搜索方法返回的距離也是近似值。

距離的計算,默認是L2(歐式距離)

也可以是

  • perform maximum inner product search argmax_i <x, x_i> instead of minimum Euclidean search. There is also limited support for other distances (L1, Linf, etc.).

內積/L1距離/無窮范數(切比雪夫距離)

 

先到這吧,頂不住了

 


免責聲明!

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



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