从精确度、内存、数据集大小等几个方面考虑选择合适的索引
如果需要精确的结果,就只能选择IndexFlatL2或者IndexFlatIP,这是保证精确搜索的唯一索引,可以作为其他索引结果的参考基线。它不压缩向量,但也不会增加额外的开销,顺序添加id,不支持add_with_ids,除非使用"IDMap Flat"。该索引不需要训练,没有参数。
支持GPU
所有的Faiss索引都是存储在RAM中的,如果RAM容量是一个限制因素,在选择索引时应该对精确度-速度进行权衡。
如果RAM很大或者数据集比较小的话,既快又准的HNSW是最佳选择。M(4~64之间)是每个节点连接的最大节点数,M越大搜索越精确,但需要的RAM越大。通过参数efSearch进行精确度-速度的权衡。
HNSW同样不支持add_with_ids,需要使用IDMap。不需要训练,不支持从索引中移除向量。
第二个索引比HNSW更快,但是需要一个re-ranking阶段,要调整两个参数:reranking的k_factor和IVF的nprobe。
不支持GPU
"..."表示对数据库进行聚类的预处理操作。聚类之后,依然通过"Flat"的方式进行检索,如前所说,并不对向量进行压缩,所以存储容量和原始数据库一样。精确度-速度权衡参数的nprobe。
支持GPU
如果存储整个向量内存消耗太大,可以通过降维和压缩减少存储:OPQ将原始向量降到D维,PQ将D维向量量化成M个4bits的编码。
支持GPU
PQ M通过乘积量化将向量压缩编码成M(通常M<=64)字节,对于较大的编码,SQ更准确更快。D应该是M的整数倍,最好D=4M。
支持GPU
数据集的大小涉及聚类的选择(上面提到的"...")。数据集被聚类成nlist个子数据集,搜索时只有部分子数据集(nprobe个)被访问。聚类是在数据集向量的代表性样本上执行的,通常是数据集的一个样本。
其中4*sqrt(N) <= K <=16*sqrt(N),N是数据集的向量个数。聚类方法是k-means,需要30*K到256*K个向量进行训练,当然越多越好。
IVF与HNSW结合,使用HNSW进行聚类。需要30*65536到256*65536个向量来进行训练。
和上面一种情况一样,只是将65536换成了262144(2^18)。
这个时候训练过程会很慢,可以采取一些措施加快训练:
训练在GPU上进行,其他的过程在CPU上;
示例代码:
index = faiss.index_factory(128, "PCA64,IVF16384_HNSW32,Flat") # 建立索引
index_ivf = faiss.extract_index_ivf(index)
clustering_index = faiss.index_cpu_to_all_gpus(faiss.IndexFlatL2(index_ivf.d))
index_ivf.clustering_index = clustering_index
# training with GPU
index.train(xt) # xt是训练数据
采用两级聚类;
示例代码见demo_two_level_clustering.ipynb · GitHub
将65536换成1048576(2^20),训练过程会更慢。
参考链接:https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index