赞
踩
因为最近要使用到faiss来做检索和查询,所以这里只好抽出点时间来学习下,本文主要是自己最近学习的记录,来源于网络资料查询总结,仅用作个人学习总结记录。
Faiss的全称是Facebook AI Similarity Search,是FaceBook的AI团队针对大规模相似度检索问题开发的一个工具,使用C++编写,有python接口,对10亿量级的索引可以做到毫秒级检索的性能。
简单来说,Faiss的工作,就是把我们自己的候选向量集封装成一个index数据库,它可以加速我们检索相似向量TopK的过程,其中有些索引还支持GPU构建,可谓是强上加强。
faiss官方项目地址在这里,如下所示:
安装的话有cpu和gpu两个版本,根据自己需要选择即可,我直接使用的是cpu版本的:
pip install faiss-cpu==1.7.3
faiss整体流程图如下所示:
Faiss检索相似向量TopK的工程基本都能分为三步:
1、得到向量库;
2、用faiss 构建index,并将向量添加到index中;
3、用faiss index 检索。
faiss库的运行是基于索引的,这个索引与传统数据库中的Index不同,它是包含向量集,训练和查询方法等的类。
Index类信息汇总如下表所示:
Method | Class name | index_factory | Main parameters | Bytes/vector | Exhaustive | Comments |
Exact Search for L2 | IndexFlatL2 | Flat | d | 4*d | yes | brute-force |
Exact Search for Inner Product | IndexFlatIP | Flat | d | 4*d | yes | also for cosine (normalize vectors beforehand) |
Hierarchical Navigable Small World graph exploration | IndexHNSWFlat | NSWx,Flat | d, M | 4*d + 8 * M | no | |
Inverted file with exact post-verification | IndexIVFFlat | IVFx,Flat | quantizer, d, nlists, metric | 4*d | no | Take another index to assign vectors to inverted lists |
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 per component is also implemented, but the impact on accuracy may be inacceptable |
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 | SQfp16: 2 * d, SQ8: d or SQ4: d/2 | no | there are 2 encodings: 4 bit per dimension and 8 bit per dimension |
IVFADC (coarse quantizer+PQ on residuals) | IndexIVFPQ | IVFx,PQy | quantizer, d, nlists, M, nbits | M+4 or M+8 | no | the memory cost depends on the data type used to represent ids (int or long), currently supports only 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 |
faiss 三个最基础的 index. 分别是 IndexFlatL2, IndexIVFFlat, IndexIVFPQ
搜索时,可以以查询向量为中心,返回距离在一定范围内的结果,如返回数据库中与查询向量距离小于0.3的结果。不是所有的Index都支持按距离检索,但是下面三种Index都支持,只支持在CPU使用。
一、IndexFlatL2 - 最基础的Index
Flat索引只是将向量简单的编码为固定大小的代码,并将它们存储在 ntotal * code_size的数组中,不对向量数据进行压缩或折叠等操作。在搜索时,对所有的索引向量依次与查询向量比较。
优点:该方法是Faiss所有index中最准确的,召回率最高的方法,没有之一;
缺点:速度慢,占内存大。
使用情况:向量候选集很少,在50万以内,并且内存不紧张。
- import numpy as np
- import faiss
- import time
-
- def demo_IndexFlatL2():
- d = 2048 # 2048维的数据
- nb = 2 # database size
- nq = 1 # nb of queries
- np.random.seed(1234)
- index = faiss.IndexFlatL2(d)
- print(index.is_trained)
- # 随机nb个数据插入索引
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000.
- index.add(xb)
- # 再随机nb个数据插入索引
- xb1 = np.random.random((nb, d)).astype('float32')
- xb1[:, 0] += np.arange(nb) / 1000.
- index.add(xb1)
- print('index.ntotal:', index.ntotal)
- # 随机nq个数据用于查询
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000.
- k = 4 # 查询距离最近的4个
- D, I = index.search(xq, k) # 返回值D是距离 I是索引
- print("I: ", I)
- # 按照距离查询
- dist = 1000 # 定义一个半径/阈值
- _, D, I = index.range_search(xb[[2], :], dist) # 用第2个向量查询
- print('range_search res:', I)
- # 删除元素,dtype=np.int64非常重要
- print('remove之前ntotal:', index.ntotal)
- index.remove_ids(np.asarray((2,3), dtype=np.int64))
- print('remove之后ntotal:', index.ntotal)
-
- if __name__ == '__main__':
- demo_IndexFlatL2()
二、更快的搜索 - IndexIVFFlat
为了加快搜索速度,可以将数据集分割成几部分。我们在d维空间中定义Voronoi单元格,并且每个数据库矢量都落入其中一个单元格中。在搜索时,只有查询x所在单元中包含的数据库向量y与少数几个相邻查询向量进行比较。(划分搜索空间)
这种类型的索引需要一个训练的过程,可以在与数据库向量具有相同分布的任何向量集合上执行。
这IndexIVFFlat还需要另一个索引,即量化器(quantizer),它将矢量分配给Voronoi单元。每个单元由一个质心定义,找到一个矢量所在的Voronoi单元包括在质心集中找到该矢量的最近邻居。这是另一个索引的任务,通常是索引IndexFlatL2。
搜索方法有两个参数:
nlist 划分单元的数量
nprobe 执行搜索访问的单元格数(不包括nlist)
nprobe 参数始终是调整结果速度和准确度之间折中的一种方式 。设置 nprobe = nlist 将给出与蛮力搜索(但会更慢)相同的结果。
优点:IVF主要利用倒排的思想,在文档检索场景下的倒排技术是指,一个kw后面挂上很多个包含该词的doc,由于kw数量远远小于doc,因此会大大减少了检索的时间。在向量中如何使用倒排呢?可以拿出每个聚类中心下的向量ID,每个中心ID后面挂上一堆非中心向量,每次查询向量的时候找到最近的几个中心ID,分别搜索这几个中心下的非中心向量。通过减小搜索范围,提升搜索效率。
缺点:速度也还不是很快。
使用情况:相比Flat会大大增加检索的速度,建议百万级别向量可以使用。
参数:IVFx中的x是k-means聚类中心的个数
- import numpy as np
- import faiss
- import time
-
- def demo_IndexIVFFlat():
- d = 64 # 向量维度
- nb = 200 # 向量集大小
- nq = 10000 # 查询次数
- np.random.seed(1234) # 随机种子,使结果可复现
-
- nlist = 100
- k = 4
- quantizer = faiss.IndexFlatL2(d) # the other index
- index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
- # here we specify METRIC_L2, by default it performs inner-product search
-
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000.
- index.train(xb)
- index.add(xb) # 添加索引可能会有一点慢
- print('index.ntotal:', index.ntotal)
- xb1 = np.random.random((nb, d)).astype('float32')
- xb1[:, 0] += np.arange(nb) / 1000.
- index.train(xb1)
- index.add(xb1) # 添加索引可能会有一点慢
- print('index.ntotal:', index.ntotal)
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000.
- D, I = index.search(xq, k) # 搜索
- print(I[-5:]) # 最初五次查询的结果
- index.nprobe = 10 # 执行搜索访问的单元格数 默认 nprobe 是1 ,可以设置的大一些试试
- D, I = index.search(xq, k)
- print(I[-5:]) # 最后五次查询的结果
- # 按照距离查询
- dist = 1000 # 定义一个半径/阈值
- _, D, I = index.range_search(xb[[2], :], dist) # 用第2个向量查询
- print('range_search res:', I)
- # 删除元素,dtype=np.int64非常重要
- print('remove之前ntotal:', index.ntotal)
- index.remove_ids(np.asarray((2,3), dtype=np.int64))
- print('remove之后ntotal:', index.ntotal)
-
- if __name__ == '__main__':
- demo_IndexIVFFlat()
三 、IVFx Flat :倒排暴力检索
优点:IVF主要利用倒排的思想,在文档检索场景下的倒排技术是指,一个kw后面挂上很多个包含该词的doc,由于kw数量远远小于doc,因此会大大减少了检索的时间。在向量中如何使用倒排呢?可以拿出每个聚类中心下的向量ID,每个中心ID后面挂上一堆非中心向量,每次查询向量的时候找到最近的几个中心ID,分别搜索这几个中心下的非中心向量。通过减小搜索范围,提升搜索效率。
缺点:速度也还不是很快。
使用情况:相比Flat会大大增加检索的速度,建议百万级别向量可以使用。
参数:IVFx中的x是k-means聚类中心的个数
- import numpy as np
- d = 64 # 向量维度
- nb = 100000 # index向量库的数据量
- nq = 10000 # 待检索query的数目
- np.random.seed(1234)
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000. # index向量库的向量
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000. # 待检索的query向量
-
-
- dim, measure = 64, faiss.METRIC_L2
- param = 'IVF100,Flat' # 代表k-means聚类中心为100,
- index = faiss.index_factory(dim, param, measure)
- print(index.is_trained) # 此时输出为False,因为倒排索引需要训练k-means,
- index.train(xb) # 因此需要先训练index,再add向量
- index.add(xb)
四 、PQx :乘积量化
乘积量化的核心思想还是聚类。其主要分为两个步骤,Cluster 和 Assign,也即聚类和量化。
乘积量化有个重要的参数m_split ,这个参数控制着向量被切分的段数,如图所示,假设每个向量的维度为128,每个向量被切分为4段,这样就得到了4个小的向量,对每段小向量分别进行聚类,聚类个数为256个,这就完成了Cluster。然后做Assign操作,先每个聚类进行编码,然后分别对每个向量来说,先切分成四段小的向量,对每一段小向量,分别计算其对应的最近的簇心,然后使用这个簇心的ID当做该向量的第一个量化编码,依次类推,每个向量都可以由4个ID进行编码。
每个ID可以由一个字节保存,每个向量只需要用4个字节就可以编码,这样就完成的向量的压缩,节省了大量的内存,压缩比率2000+。
这一步其实就是Faiss训练的部分,目的是为了获取索引Index。在完成向量压缩,获取索引之后,就要考虑如何进行向量查询,下图表示了某个查询向量Query进行查询时的操作流程:
优点:利用乘积量化的方法,改进了普通检索,将一个向量的维度切成x段,每段分别进行检索,每段向量的检索结果取交集后得出最后的TopK。因此速度很快,而且占用内存较小,召回率也相对较高。
缺点:召回率相较于暴力检索,下降较多。
使用情况:内存及其稀缺,并且需要较快的检索速度,不那么在意召回率
参数:PQx中的x为将向量切分的段数,因此,x需要能被向量维度整除,且x越大,切分越细致,时间复杂度越高
- import numpy as np
- d = 64 # 向量维度
- nb = 100000 # index向量库的数据量
- nq = 10000 # 待检索query的数目
- np.random.seed(1234)
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000. # index向量库的向量
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000. # 待检索的query向量
-
- dim, measure = 64, faiss.METRIC_L2
- param = 'PQ16'
- index = faiss.index_factory(dim, param, measure)
- print(index.is_trained) # 此时输出为False,因为倒排索引需要训练k-means,
- index.train(xb) # 因此需要先训练index,再add向量
- index.add(xb)
五、更低的内存占用 - IndexIVFPQ
索引IndexFlatL2和IndexIVFFlat都存储完整的向量。 为了扩展到非常大的数据集,Faiss提供了基于产品量化器的有损压缩来压缩存储的向量的变体。压缩的方法基于乘积量化(Product Quantizer)。
在这种情况下,由于矢量没有精确存储,搜索方法返回的距离也是近似值。
- import numpy as np
- import faiss
- import time
-
- def demo_IndexIVFPQ():
- d = 64 # 向量维度
- nb = 100000 # 向量集大小
- nq = 10000 # 查询次数
- np.random.seed(1234) # 随机种子,使结果可复现
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000.
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000.
-
- nlist = 100
- m = 8
- k = 4
- quantizer = faiss.IndexFlatL2(d) # 内部的索引方式依然不变
- index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)
- # 每个向量都被编码为8个字节大小
- index.train(xb)
- index.add(xb)
- D, I = index.search(xb[:5], k) # 测试
- print(I)
- print(D)
- index.nprobe = 10 # 与以前的方法相比
- D, I = index.search(xq, k) # 检索
- print(I[-5:])
- # 按照距离查询
- dist = 1000 # 定义一个半径/阈值
- _, D, I = index.range_search(xb[[2], :], dist) # 用第2个向量查询
- print('range_search res:', I)
- # 删除元素,dtype=np.int64非常重要
- print('remove之前ntotal:', index.ntotal)
- index.remove_ids(np.asarray((2,3), dtype=np.int64))
- print('remove之后ntotal:', index.ntotal)
-
- if __name__ == '__main__':
- demo_IndexIVFPQ()
六、IVFxPQy 倒排乘积量化
优点:工业界大量使用此方法,各项指标都均可以接受,利用乘积量化的方法,改进了IVF的k-means,将一个向量的维度切成x段,每段分别进行k-means再检索。
缺点:集百家之长,自然也集百家之短
使用情况:一般来说,各方面没啥特殊的极端要求的话,最推荐使用该方法!
参数:IVFx,PQy,其中的x和y同上
- import numpy as np
- d = 64 # 向量维度
- nb = 100000 # index向量库的数据量
- nq = 10000 # 待检索query的数目
- np.random.seed(1234)
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000. # index向量库的向量
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000. # 待检索的query向量
-
- dim, measure = 64, faiss.METRIC_L2
- param = 'IVF100,PQ16'
- index = faiss.index_factory(dim, param, measure)
- print(index.is_trained) # 此时输出为False,因为倒排索引需要训练k-means,
- index.train(xb) # 因此需要先训练index,再add向量 index.add(xb)
七、LSH 局部敏感哈希
原理:哈希对大家再熟悉不过,向量也可以采用哈希来加速查找,我们这里说的哈希指的是局部敏感哈希(Locality Sensitive Hashing,LSH),不同于传统哈希尽量不产生碰撞,局部敏感哈希依赖碰撞来查找近邻。高维空间的两点若距离很近,那么设计一种哈希函数对这两点进行哈希计算后分桶,使得他们哈希分桶值有很大的概率是一样的,若两点之间的距离较远,则他们哈希分桶值相同的概率会很小。
优点:训练非常快,支持分批导入,index占内存很小,检索也比较快
缺点:召回率非常拉垮。
使用情况:候选向量库非常大,离线检索,内存资源比较稀缺的情况
- import numpy as np
- d = 64 # 向量维度
- nb = 100000 # index向量库的数据量
- nq = 10000 # 待检索query的数目
- np.random.seed(1234)
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000. # index向量库的向量
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000. # 待检索的query向量
-
- dim, measure = 64, faiss.METRIC_L2
- param = 'LSH'
- index = faiss.index_factory(dim, param, measure)
- print(index.is_trained) # 此时输出为True
- index.add(xb)
八、HNSWx (最重要的放在最后说)
优点:该方法为基于图检索的改进方法,检索速度极快,10亿级别秒出检索结果,而且召回率几乎可以媲美Flat,最高能达到惊人的97%。检索的时间复杂度为loglogn,几乎可以无视候选向量的量级了。并且支持分批导入,极其适合线上任务,毫秒级别体验。
缺点:构建索引极慢,占用内存极大(是Faiss中最大的,大于原向量占用的内存大小)
参数:HNSWx中的x为构建图时每个点最多连接多少个节点,x越大,构图越复杂,查询越精确,当然构建index时间也就越慢,x取4~64中的任何一个整数。
使用情况:不在乎内存,并且有充裕的时间来构建index
- import numpy as np
- d = 64 # 向量维度
- nb = 100000 # index向量库的数据量
- nq = 10000 # 待检索query的数目
- np.random.seed(1234)
- xb = np.random.random((nb, d)).astype('float32')
- xb[:, 0] += np.arange(nb) / 1000. # index向量库的向量
- xq = np.random.random((nq, d)).astype('float32')
- xq[:, 0] += np.arange(nq) / 1000. # 待检索的query向量
-
- dim, measure = 64, faiss.METRIC_L2
- param = 'HNSW64'
- index = faiss.index_factory(dim, param, measure)
- print(index.is_trained) # 此时输出为True
- index.add(xb)
IndexBinary是二进制索引,其表示的向量集中每个维度只有 0 和 1 两种数值,查询时计算待查向量与DataBase的汉明距离。向量在内存中按字节存储,每个维度占用1bit,总的内存空间为(dimension / 8)bytes,所以这类向量只支持维度为8的整数倍的向量集,否则需要对向量进行扩展或压缩。
九、IndexBinaryFlat
穷举搜索,在查询时会计算索引集中所有向量,该索引针对256维向量进行了特殊优化。
- import faiss
-
- # 向量维度
- d = 256
-
- # 建立索引的向量,可视为DataBase
- db = ...
-
- # 需从Index中查询的目标向量
- queries = ...
-
- # 初始化Index
- index = faiss.IndexBinaryFlat(d)
-
- # 添加db到index中
- index.add(db)
-
- # 每个查询向量要检索的最近邻居数
- k = ...;
-
- # 查询索引, D是存放最近K个距离的容器, I是D中每个距离的向量在db中的下标
- D, I = index.search(queries, k)
十、IndexBinaryIVF
该索引会对向量进行聚类来加快查询速度。聚类搜索需要先进行训练,相当于无监督学习。训练过程主要是量化器学习对数据进行聚类,划分为nlist个簇。这里采用的聚类方法是按距离划分。IndexBinaryIVF索引查询与IndexBinaryFlat的方式不同,前者是基于聚类的。
- import faiss
-
- # 向量维度
- d = 256
-
- # 建立索引的向量,可视为DataBase
- db = ...
-
- # 训练用的向量集
- training = ...
-
- # 需从Index中查询的目标向量
- queries = ...
-
- # 初始化量化器
- quantizer = faiss.IndexBinaryFlat(d)
-
- # 设置向量集中簇的个数
- nlist = ...
-
- # 初始化索引
- index = faiss.IndexBinaryIVF(quantizer, d, nlist)
- # 设置每次查询的簇的数量
- index.nprobe = 4
-
- # 训练
- index.train(training)
-
- # 将向量集添加进索引
- index.add(db)
-
- # 每个查询向量要检索的最近邻居数
- k = ...
-
- # 查询索引, D是存放最近K个距离的容器, I是D中每个距离的向量在db中的下标
- D, I = index.search(queries, k)
混合索引表示使用了上述多种索引方法以增强查询性能。
十一、IndexPQ
- 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
十二、IndexIVFPQ
- coarse_quantizer = faiss.IndexFlatL2 (d)
- index = faiss.IndexIVFPQ (coarse_quantizer, d,
- ncentroids, code_size, 8)
- index.nprobe = 5
十三、粗略PQ索引
乘积量化器(PQ)也可以作为初始索引,对应于多索引[The inverted multi-index, Babenko & Lempitsky, CVPR'12],对于具有m个段(每个段编码为c个质心)的PQ,反向列表的数量为c ^ m。因此,m = 2是唯一可行的选择。在FAISS中,相应的粗略量化器索引是MultiIndexQuantizer。该索引没有添加向量。因此,必须在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代码比较。
设置阈值的原则:
阈值应在0和每个代码的位数之间(在这种情况下为128 = 16 * 8),并且代码遵循二项式分布;
阈值设置应小于代码位数的1/2。
- #
- #For an 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 (...)
-
- #
- #For an 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 (...)
对于Index相关的内容整体总结如下所示:
Faiss提供的索引多种多样,虽然它们都能完成既定的任务,但是对于不同的应用场景,表现的性能差异是非常大的。针对不同的应用场景,如何选择最好的索引,可以遵循下列因素:
【搜索量】
如果搜索量比较小(比如1000 - 10000),那么建立索引的时间在整个搜索过程中的占比会比较大,所以可以直接计算。
【结果精确度】
如果对结果要求绝对的准备,那么选择带有"Flat"关键字的索引。只有IndexFlatL2 或 IndexFlatIP能保证绝对的准确。它们为其他索引的结果提供了基准值。
【内存敏感性】
如果对内存没有要求,选择"HNSWx",其中x(4 - 64)表示每个向量的link数,x值越大,结果越精确,占用内存也越大;
要求不高,选择"... , Flat",其中"..."表示先对数据进行聚类。
要求比较高,选择"PCARx,...,SQ8"
对内存要求非常高,选择"OPQx_y,...,PQx"
【dataset大小】
小于1M, 选择"...,IVFx,..."
1M - 10M之间,选择 "...,IVF65536_HNSW32,..."
10M - 100M:之间, 选择"...,IVF262144_HNSW32,..."
100M - 1B:之间, 选择"...,IVF1048576_HNSW32,..."
Faiss的前后处理主要进行重新映射向量ID,对数据进行转换,并使用更好的索引对搜索结果重新排序等操作。
默认情况下Faiss按顺序将向量添加到索引,并设置ID。一些Index类实现了add_with_ids方法,其中除了向量之外,还可以提供64位向量ID。在搜索时,该类将返回存储的ID,而不是初始向量。映射ID可以使用IndexIDMap方法,该方法封装了另一个索引,并在添加和搜索时转换ID。它维护带有映射的表。
- 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
由于输入数据量过大等原因,在对数据进行索引前,往往需要进行数据转换,如压缩,降维等。所有的数据预转换方法都继承自VectorTransform类。该类对维度为d_in的输入向量进行转换,输出维度为d_out的输出向量。
数据预转换方法如下表:
【 PCAMatrix : 使用PCA降维】
将向量维度从2048D减到16字节
- # 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(...)
【RemapDimensionsTransform:增加维度】
可以通过添加零值的方式给向量增加维度
- # input is in dimension d, but we want a multiple of M
- d2 = int((d + M - 1) / M) * M
- remapper = faiss.RemapDimensionsTransform (d, d2, true)
- # the index in d2 dimensions
- index_pq = faiss.IndexPQ(d2, M, 8)
-
- # the index that will be used for add and search
- index = faiss.IndexPreTransform (remapper, index_pq)
【IndexRefineFlat:重新排列搜索结果】
查询向量时,使用实际距离计算对搜索结果重新排序可能会很有用。以下示例使用IndexPQ搜索索引,然后通过计算实际距离对第一个结果进行排名:
- 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)
最后放上在总结学习过程中参考的博文【对所有作者表示感谢】:
https://blog.csdn.net/rangfei/category_10080429.html
Home · facebookresearch/faiss Wiki · GitHub
faiss 三种基础索引方式_faiss.indexflatl2_小殊小殊的博客-CSDN博客
https://blog.csdn.net/weixin_43791511/article/details/122513786
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。