Faiss教程:索引(1)

索引是faiss的關鍵知識,咱們重點介紹下。app

索引方法彙總

有些索引名,我就不翻譯了,根據英文名去學習更準確。dom

索引名 類名 index_factory 主要參數 字節數/向量 精準檢索 備註
精準的L2搜索 IndexFlatL2 "Flat" d 4*d yes brute-force
精準的內積搜索 IndexFlatIP "Flat" d 4*d yes 歸一化向量計算cos
Hierarchical Navigable Small World graph exploration IndexHNSWFlat "HNSWx,Flat" d, M 4*d + 8 * M no -
倒排文件 IndexIVFFlat "IVFx,Flat" quantizer, d, nlists, metric 4*d no 須要另外一個量化器來創建倒排
Locality-Sensitive Hashing (binary flat index) IndexLSH - d, nbits nbits/8 yes optimized by using random rotation instead of random projections
Scalar quantizer (SQ) in flat mode IndexScalarQuantizer "SQ8" d d yes 每一個維度項能夠用4 bit表示,可是精度會受到必定影響
Product quantizer (PQ) in flat mode IndexPQ "PQx" d, M, nbits M (if nbits=8) yes -
IVF and scalar quantizer IndexIVFScalarQuantizer "IVFx,SQ4" "IVFx,SQ8" quantizer, d, nlists, qtype d or d/2 no 有兩種編碼方式:每一個維度項4bit或8bit
IVFADC (coarse quantizer+PQ on residuals) IndexIVFPQ "IVFx,PQy" quantizer, d, nlists, M, nbits M+4 or M+8 no 內存和數據id(int、long)相關,目前只支持 nbits <= 8
IVFADC+R (same as IVFADC with re-ranking based on codes) IndexIVFPQR "IVFx,PQy+z" quantizer, d, nlists, M, nbits, M_refine, nbits_refine M+M_refine+4 or M+M_refine+8 no -

Cell-probe方法

加速查找的典型方法是對數據集進行劃分,咱們採用了基於Multi-probing(best-bin KD樹變體)的分塊方法。函數

  • 特徵空間被切分爲ncells個塊
  • 數據被劃分到這些塊中(k-means可根據最近歐式距離),歸屬關係存儲在ncells個節點的倒排列表中
  • 搜索時,檢索離目標距離最近的nprobe個塊
  • 根據倒排列表檢索nprobe個塊中的全部數據。

這即是IndexIVFFlat,它須要另外一個索引來記錄倒排列表。學習

IndexIVFKmeans 和 IndexIVFSphericalKmeans 不是對象而是方法,它們能夠返回IndexIVFFlat對象。this

注意:對於高維的數據,要達到較好的召回,須要的nprobes可能很大編碼

和LSH的關係

最流行的cell-probe方法多是原生的LSH方法,可參考E2LSH。然而,這個方法及其變體有兩大弊端:spa

  • 須要大量的哈希函數(=分塊數),來達到能夠接受的結果
  • 哈希函數很難基於輸入動態調整,實際應用中容易返回次優結果

LSH的示例scala

n_bits = 2 * d
lsh = faiss.IndexLSH (d, n_bits)
lsh.train (x_train)
lsh.add (x_base)
D, I = lsh.search (x_query, k)

d是輸入數據的維度,nbits是存儲向量的bits數目。翻譯

PQ的示例code

m = 16                                   # number of subquantizers
n_bits = 8                               # bits allocated per subquantizer
pq = faiss.IndexPQ (d, m, n_bits)        # Create the index
pq.train (x_train)                       # Training
pq.add (x_base)                          # Populate the index
D, I = pq.search (x_query, k)            # Perform a search

帶倒排的PQ:IndexIVFPQ

coarse_quantizer = faiss.IndexFlatL2 (d)
index = faiss.IndexIVFPQ (coarse_quantizer, d,
                          ncentroids, m, 8)
index.nprobe = 5

複合索引

使用PQ做粗粒度量化器的Cell Probe方法

相應的文章見:The inverted multi-index, Babenko & Lempitsky, CVPR'12。在Faiss中可以使用MultiIndexQuantizer,它不須要add任何向量,所以將它應用在IndexIVF時須要設置quantizer_trains_alone。

nbits_mi = 12  # c
M_mi = 2       # m
coarse_quantizer_mi = faiss.MultiIndexQuantizer(d, M_mi, nbits_mi)
ncentroids_mi = 2 ** (M_mi * nbits_mi)

index = faiss.IndexIVFFlat(coarse_quantizer_mi, d, ncentroids_mi)
index.nprobe = 2048
index.quantizer_trains_alone = True

預過濾PQ編碼,漢明距離的計算比PQ距離計算快6倍,經過對PQ中心的合理重排序,漢明距離能夠正確地替代PQ編碼距離。在搜索時設置漢明距離的閾值,能夠避免PQ比較的大量運算。

# IndexPQ
index = faiss.IndexPQ (d, 16, 8)
# before training
index.do_polysemous_training = true
index.train (...)

# before searching
index.search_type = faiss.IndexPQ.ST_polysemous
index.polysemous_ht = 54    # the Hamming threshold
index.search (...)
# IndexIVFPQ
index = faiss.IndexIVFPQ (coarse_quantizer, d, 16, 8)
# before training
index. do_polysemous_training = true
index.train (...)

# before searching
index.polysemous_ht = 54 # the Hamming threshold
index.search (...)

閾值設定是注意兩點:

  • 閾值在0到編碼bit數(16*8)之間
  • 閾值越小,留下的須要計算的PQ中心數越少,推薦<1/2*bits

複合索引中也能夠創建多級PQ量化索引。

預處理和後處理

爲了得到更好的索引,能夠remap向量ids,對數據集進行變換,re-rank檢索結果等。

Faiss id mapping

默認狀況下,Faiss爲每一個向量設置id。有些Index實現了add_with_ids方法,爲向量添加64bit的ids,檢索時返回ids而不需返回原始向量。

index = faiss.IndexFlatL2(xb.shape[1]) 
ids = np.arange(xb.shape[0])
index.add_with_ids(xb, ids)  # this will crash, because IndexFlatL2 does not support add_with_ids
index2 = faiss.IndexIDMap(index)
index2.add_with_ids(xb, ids) # works, the vectors are stored in the underlying index

IndexIVF原生提供了ass_with_ids方法,就不須要IndexIDMap了。

預變換

變換方法 類名 備註
random rotation RandomRotationMatrix useful to re-balance components of a vector before indexing in an IndexPQ or IndexLSH
remapping of dimensions RemapDimensionsTransform 爲適應索引推薦的維度,經過重排列減小或增長向量維度d
PCA PCAMatrix 降維
OPQ rotation OPQMatrix OPQ經過旋轉輸入向量更利於PQ編碼,見 Optimized product quantization, Ge et al., CVPR'13

換行能夠經過train進行訓練,經過apply應用到數據上。這些變化能夠經過IndexPreTransform方法應用到索引上。

# the IndexIVFPQ will be in 256D not 2048
coarse_quantizer = faiss.IndexFlatL2 (256)
sub_index = faiss.IndexIVFPQ (coarse_quantizer, 256, ncoarse, 16, 8)
# PCA 2048->256
# also does a random rotation after the reduction (the 4th argument)
pca_matrix = faiss.PCAMatrix (2048, 256, 0, True) 

#- the wrapping index
index = faiss.IndexPreTransform (pca_matrix, sub_index)

# will also train the PCA
index.train(...)
# PCA will be applied prior to addition
index.add(...)

IndexRefineFlat

對搜索結果進行精準重排序

q = faiss.IndexPQ (d, M, nbits_per_index)
rq = faiss.IndexRefineFlat (q)
rq.train (xt)
rq.add (xb)
rq.k_factor = 4
D, I = rq:search (xq, 10)

從IndexPQ的最近4*10個鄰域中,計算真實距離,返回最好的10個結果。注意IndexRefineFlat須要積累全向量,佔用內存較高。

IndexShards

若是數據分開爲多個索引,查詢時須要合併結果集。這在多GPU以及平行查詢中是必需的。

相關文章
相關標籤/搜索