Faiss入门篇假定Faiss已经被安装,若未安装可参考小编安装编译篇https://zhuanlan.zhihu.com/p/78689463。本篇小编基于Faiss的官方wiki实例展开,旨在让大家快速入门Faiss。Faiss底层用c++实现并为用户提供python接口,本篇我们以python示例Faiss用法,使用python接口前,需要下载numpy包用于faiss向量加载。
Faiss处理大规模d维向量近邻检索的问题,Faiss中所有向量以行矩阵的形式储存和使用,实例中我们用xb表示所有待索引的向量集合 ,xq表示查询向量集合,nb和nq分别表示xb、xq集合中向量数量。
构建待检索向量和查询向量
import numpy as np
d = 64
# 向量维度
nb = 100000
# 待索引向量size
nq = 10000
# 查询向量size
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.
建立索引并添加向量
Faiss通过Index对象进行向量的封装与预处理,Faiss提供了很多种索引类型,我们首先test暴力搜索精准L2距离搜索,对应的索引对象为IndexFlatL2。
所有向量在建立前需要明确向量的维度d,大多数的索引还需要训练阶段来分析向量的分布。但是,对于L2暴力搜索来说没有训练的必要。
Index对象训练好之后,对于Index有两个操作供调用,分别为add和search。add方法用于向Index中添加xb向量,search方法用于在add向量后的索引中检索xq的若干近邻。Index还有两个状态变量is_trained(bool类型,用于指示index是否已被训练)和ntotal(指示索引的数量)。此外,index还有IDs添加的方法。
import faiss
index = faiss.IndexFlatL2(d)
# 建立索引
print(index.is_trained)
# 输出true
index.add(xb)
# 索引中添加向量
print(index.ntotal)
# 输出100000
近邻搜索
通过Index检索xq中的数据,faiss支持批量数据检索,通过search方法返回的检索结果包括两个矩阵,分别为近邻向量的索引序号和xq中元素与近邻的距离大小。
k = 4
# 返回每个查询向量的近邻个数
D, I = index.search(xb[:5], k)
# 检索check
print(I)
print(D)
D, I = index.search(xq, k)
#xq检索结果
print(I[:5])
# 前五个检索结果展示
print(I[-5:])
# 最后五个检索结果展示
检索结果
check检索结果
[[ 0 393 363 78]
[ 1 555 277 364]
[ 2 304 101 13]
[ 3 173 18 182]
[ 4 288 370 531]]
[[ 0. 7.17517328 7.2076292 7.25116253]
[ 0. 6.32356453 6.6845808 6.79994535]
[ 0. 5.79640865 6.39173603 7.28151226]
[ 0. 7.27790546 7.52798653 7.66284657]
[ 0. 6.76380348 7.29512024 7.36881447]]
xq检索结果
[[ 381 207 210 477]
[ 526 911 142 72]
[ 838 527 1290 425]
[ 196 184 164 359]
[ 526 377 120 425]]
[[ 9900 10500 9309 9831]
[11055 10895 10812 11321]
[11353 11103 10164 9787]
[10571 10664 10632 9638]
[ 9628 9554 10036 9582]]
上面所示,IndexFlatL2是暴力检索的索引,为了加速检索speed,可以使用faiss的IndexIVFFlat索引对象。IndexIVFFlat的使用需要进行训练阶段,并需要指定其他索引作为量化器,与检索相关的参数
为nlist和nprobe。IndexIVFFlat索引先利用粗量化器将检索向量划分到Voronoi单元中并建立倒排索引,检索阶段将根据输入向量和probe参数定位到对应的Voronoi cell中进行近邻搜素。
IndexIVFFlat检索
nlist = 100
k = 4
quantizer = faiss.IndexFlatL2(d)
# 量化器索引
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
# 指定用L2距离进行搜索,若不指定默认为內积
assert not index.is_trained
index.train(xb)
# 索引训练
assert index.is_trained
index.add(xb)
# 向量添加
D, I = index.search(xq, k)
# 检索
print(I[-5:])
# 最后五个检索结果
index.nprobe = 10
# 多探针检索
D, I = index.search(xq, k)
print(I[-5:])
#最后五个检索结果
检索结果显示
1probe
[[ 9900 10500 9831 10808]
[11055 10812 11321 10260]
[11353 10164 10719 11013]
[10571 10203 10793 10952]
[ 9582 10304 9622 9229]]
10probe
[[ 9900 10500 9309 9831]
[11055 10895 10812 11321]
[11353 11103 10164 9787]
[10571 10664 10632 9638]
[ 9628 9554 10036 9582]]
读者可自行观察测试各参数对检索结果的影响。
本篇代码可以在faiss中的 tutorial/ 目录下查找。
Reference
https://github.com/facebookresearch/faiss/wiki
本篇内容在本人个人公众号上也已发布,欢迎关注本人微信公众号“勤菜鸟”。