共计 1535 个字符,预计需要花费 4 分钟才能阅读完成。
引言
关于faiss的使用也是在使用embedding相似度召回的时候接触到,的确是一个高效的计算相似度的方案,由FB推出,业界使用的也是比较多。
对于faiss的使用我日常使用就是调接口,因为特征平台组都帮我封装好了,更新索引/实时添加索引/查询/监控等都做的比较完善了,去年也去听过一场PPT汇报,大概还是有点印象。
如今有个小业务要交界处去,也牵扯到这个faiss,自己也来了解一下
Faiss
简单点应该就分为两个部分吧
- 构建索引
- 查询
构建索引
这里也是有两种方式
- 全量更新重建索引
- 实时增量构建索引
准备
N*D矩阵数据
N表示数据条数,D表示每条数据的维度
数据类型是float32
使用
import numpy as np
d = 64 # dimension
nb = 100000 # database size
nq = 10000 # nb of queries
np.random.seed(1234) # make reproducible
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.
#至此随机构建数据完成
#-------------------------------------------------
#开始准备构建索引
import faiss # make faiss available
index = faiss.IndexFlatL2(d) # build the index
print(index.is_trained)
index.add(xb) # add vectors to the index
print(index.ntotal)
Faiss围绕Index对象构建。索引的类型很多,我们将使用对它们执行暴力L2距离搜索的最简单版本:IndexFlatL2。
在我们的例子中,所有索引都需要知道何时建立索引,即它们操作的向量的维数。然后,大多数索引还需要训练阶段,以分析向量的分布。对于IndexFlatL2,我们可以跳过此操作。
要将元素添加到索引,我们在xb上调用add。我们还可以显示索引的两个状态变量:is_trained,一个布尔值,指示是否需要训练,以及ntotal,即索引向量的数量。
在上面的例子中我们成功的构建了索引
查询
可以对索引执行的基本搜索操作是k近邻搜索,即k。 对于每个查询向量,在索引中找到它的k个最近邻居。
此操作的结果可以方便地存储在大小为nq-k的整数矩阵中,其中第i行包含查询向量i邻居的 ID(按距离递增排序)。 除了此矩阵之外,搜索操作还返回具有相应平方距离的nq-k浮点矩阵。
k = 4 # 指定需要查询最近邻的个数
D, I = index.search(xb[:5], k) # sanity check
print(I)
print(D)
D, I = index.search(xq, k) # actual search
print(I[:5]) # neighbors of the 5 first queries
print(I[-5:]) # neighbors of the 5 last queries
小结
到这里基本上简单的faiss服务就完成了,但是faiss还有很多需要关注的地方
- 数据预处理 比如降维构建索引
- 更加高效的查询方式 精度与速度之间衡量
- 衡量距离的计算方式
- 多线程异步服务
- GPU部署
注意事项
Faiss服务主要是为了提供高效的查询服务,那么存在一个问题,你如果插入相同的数据和id信息,数据索引里面是不会进行去重的,索引你查询会返回相同的结果,这点还是需要注意下,可以在实时添加索引或者离线构建索引的时候注意下。