Faiss学习笔记

Faiss是一个用于稠密向量相似度搜索和聚类的库,支持多种索引类型如IndexFlatL2、IndexIVFFlat和IndexIVFPQ。使用步骤包括构建索引、添加数据和查询数据。IndexIVFFlat通过预聚类提高效率,IndexIVFPQ利用产品量化压缩存储。文章还介绍了分布式检索的实现和性能优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Faiss概念

Faiss是一个向量检索库。它的GitHub上描述是这样的

Faiss is a library for efficient similarity search and clustering of dense vectors. It contains algorithms that search in sets of vectors of any size, up to ones that possibly do not fit in RAM. It also contains supporting code for evaluation and parameter tuning. Faiss is written in C++ with complete wrappers for Python/numpy. Some of the most useful algorithms are implemented on the GPU. It is developed primarily at Meta’s Fundamental AI Research group.

通过以上描述,可以发现有如下关键点:

  • 支持稠密向量相似度搜索和聚类
  • 包含了可以搜索任意维度向量集合的算法
  • 使用C++编写,支持Python/numpy接口
  • 部分最有用的算法支持GPU

本质上是解决基于向量相似度的knn问题

使用步骤

简单来说就是构建索引、添加数据、查询数据。具体步骤为:

  1. 构建索引index
  2. 根据不同索引的特性,对索引进行训练(train)。对于有些算法来说,训练的本质是聚类;对于IndexFlatL2等可以直接搜索的场景可省略本步骤。
  3. add 添加完整数据集到索引
  4. 针对query数据进行搜索search操作

常见索引类型及示例

根据特点,索引类别主要分为几类。完整版参见文档

  • Flat indexes(暴力检索类,搜索时逐个比对)
  • IndexIVF* indexes(IVF,Inverted File System基于分区的方法,例如聚类。此方法在检索时只会与一部分向量做对比,提升了效率。但可能得到的结果落入局部最优而非全局最优。在构建索引时还可以采用乘积量化、降维等方式进行内存压缩和提速)
  • IndexHNSW及相关变体(HNSW,Hierarchical Navigable Small World,基于索引数据构建图,检索时根据生成图进行最邻近检索。HNSW算法介绍可以参见这篇
  • IndexLSH(LSH,局部敏感哈希)

以下为几个常见方法简介

# test_datasets.py
# 数据集
import numpy as np
import time

print("generate data ..")
a = time.time()
d = 64                           # dimension
nb = 1000000                      # 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.

print("generate data done, time cost:{}".format(time.time() - a))

IndexFlatL2

基于L2距离的暴力全量搜索,速度较慢

这里多介绍一下L1距离和L2距离
L1距离:即曼哈顿距离。每一维度坐标相减后的绝对值求和。公式为
d 1 ( v 1 , v 2 ) = ∑ i = 1 n ∣ x i − y i ∣ d_1(v_1,v_2)= \sum_{i=1}^n|x_i-y_i| d1(v1,v2)=i=1nxiyi
L2距离:即欧氏距离。
d 2 ( v 1 , v 2 ) = ∑ i = 1 n ( x i − y i ) 2 d_2(v_1,v_2)=\sqrt{\sum_{i=1}^{n}{\left( x_{i}-y_{i} \right)^{2}}} d2(v1,v2)=i=1n(xiyi)2

代码如下

from test_dataset import *
import faiss
import numpy as np
import time


print("start to build index..")
a = time.time()
index = faiss.IndexFlatL2(d)   # build the index
print("build index done, time cost:{}".format(time.time() - a ))
print("is trained: {}".format(index.is_trained))        # IndexFlatL2不需要训练过程
print("start to add query..")
a = time.time()
index.add(xb)                  # add vectors to the index
print("add data done, time cost:{}".format(time.time() - a ))
print(index.ntotal)


print("start to search..")
k = 4                          # we want to see 4 nearest neighbors
D, I = index.search(xq, k)     # search
print("search done, time cost:{}".format(time.time() - a ))
print(I[:5])                   # neighbors of the 5 first queries
print(D[:5])                   # distance of the return results

结果

generate data ..
generate data done, time cost:1.2140648365020752
start to build index..
build index done, time cost:1.621246337890625e-05
is trained: True
start to add query..
add data done, time cost:0.19340801239013672
1000000
start to search..
search done, time cost:8.8518226146698
[[ 774  175  917  413]
 [  98   66  357  596]
 [ 504  263  740  738]
 [ 756 1102  454  353]
 [ 352  381  545  264]]
[[6.8088226 6.8297997 6.8576965 6.8842087]
 [7.1985703 7.3808327 7.4176826 7.491226 ]
 [7.2444    7.460396  7.4658546 7.6690903]
 [6.293293  6.414982  6.531719  6.7257767]
 [7.6404266 7.77285   7.8547783 8.039719 ]]

IndexIVFFlat

先聚类再索引。索引初始化后先在数据集上执行train方法(本质是在对数据集做kmeans聚类),再add数据。查询时先查询与各簇中心距离,找到最近的nprobe个簇后在相应的簇中遍历向量,找到最终结果。

from test_dataset import *
import faiss

"""
先聚类再搜索,可以加快检索速度
先将xb中的数据进行聚类(聚类的数目是超参),nlist: 聚类的数目
nprobe: 在多少个聚类中进行搜索,默认为1, nprobe越大,结果越精确,但是速度越慢
"""

nlist = 100 #聚类的数目
k = 4

a = time.time()
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFFlat(quantizer, d, nlist)
print("build index done, time cost:{}".format(time.time() - a ))

a = time.time()
index.train(xb) # IndexIVFFlat是需要训练的,这边是在聚类
print("train index done, time cost:{}".format(time.time() - a ))
print("is trained: {}".format(index.is_trained))

a = time.time()
index.add(xb)                  # add may be a bit slower as well
print("add data done, time cost:{}".format(time.time() - a ))

a = time.time()
D, I = index.search(xq, k)     # actual search
print("nprobe=1 search done, time cost:{}".format(time.time() - a ))
print(I[-5:])                  # neighbors of the 5 last queries

index.nprobe = 10              # default nprobe is 1, try a few more
a = time.time()
D, I = index.search(xq, k)
print("nprobe=10 search done, time cost:{}".format(time.time() - a ))
print(I[-5:])                  # neighbors of the 5 last queries

结果

generate data ..
generate data done, time cost:1.2480273246765137
build index done, time cost:5.1021575927734375e-05
train index done, time cost:0.19619107246398926
is trained: True
add data done, time cost:0.30707430839538574
nprobe=1 search done, time cost:0.18898820877075195
[[10179 10714  9864  8813]
 [ 9762 10013  9720 10405]
 [ 9229  9510 10039 10602]
 [10584  9725  9787  9816]
 [ 9464  9635 10199  9180]]
nprobe=10 search done, time cost:1.5632450580596924
[[10179 10714  9864  8813]
 [ 9762 10013  9720 10405]
 [ 9229  9510 10039 10602]
 [10584  9725  9787  9816]
 [ 9464  9635 10199  9180]]

可以看出建立索引后搜索速度显著提升,从1个类中搜索仅耗时不到0.19s,从10个类中搜索耗时1.56s,远小于暴力搜索。

IndexIVFPQ

基于Product Quantizer的压缩算法将向量压缩并存储。此方法可以在存储时压缩向量的大小,故搜索的时候也属于近似搜索。优点是可以减少存储空间。

from test_dataset import *
import faiss

"""
基于乘积量化(product quantizers)对存储向量进行压缩,节省存储空间
m:乘积量化中,将原来的向量维度平均分成多少份,d必须为m的整数倍
bits: 每个子向量用多少个bits表示

和之前输出的最优结果略有不同,牺牲精度,但是节约空间
"""

nlist = 100
m = 8                             # number of subquantizers
bits = 8
k = 4

a = time.time()
quantizer = faiss.IndexFlatL2(d)  # this remains the same
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, bits)                                    # 8 specifies that each sub-vector is encoded as 8 bits
print("build index done, time cost:{}".format(time.time() - a ))

a = time.time()
index.train(xb)
print("train index done, time cost:{}".format(time.time() - a ))
print("is trained: {}".format(index.is_trained))        # IndexFlatL2不需要训练过程

a = time.time()
index.add(xb)
print("add data done, time cost:{}".format(time.time() - a ))

index.nprobe = 10              # make comparable with experiment above
a = time.time()
D, I = index.search(xq, k)     # search
print("nprobe=10 search done, time cost:{}".format(time.time() - a ))
print(I[:5])

结果

generate data ..
generate data done, time cost:1.3283205032348633
build index done, time cost:0.005830287933349609
train index done, time cost:3.3282480239868164
is trained: True
add data done, time cost:0.8176021575927734
nprobe=10 search done, time cost:0.596820592880249
[[ 175  365  774   66]
 [  56  375    3  286]
 [ 740  738  394  519]
 [ 353  454 1350  233]
 [  87  262   47  642]]

索引选择指南

Faiss提供了多种索引,涵盖了各种侧重点,在准确度、内存容量、数据集大小等方面可以综合考虑并选择适合自己业务的index。官方对此提供了较为详细的教程,参见Guidelines-to-choose-an-index

分布式检索

Faiss本身集成了一个rpc库(实际为创建socket连接进行通信,实现参见代码),可以支持分布式检索,代码参加官方demo。其原理为创建多个线程(线程数与远程服务器数相同)分别创建连接请求远程计算服务,并收集计算结果。
由于我手头只有两台机器,所以参考官方demo进行了简单修改,分别将计算分布在两台机器上和一台机器上进行对比测试

# 分布式版本
dis_machine_ports = [
    ('172.16.160.21', 12010),
    ('localhost', 12011),
]
# 单机版本
single_machine_ports = [
    ('localhost', 12014),
]

两台机器配置均为32核64G,测试结果如下

running:distribution
index size: 1000000
recall@1: 0.990
time cost: 3.421029567718506

running:single
index size: 1000000
recall@1: 0.989
time cost: 5.748946666717529

可以看到分布式明显快于单机版,且召回率略有提升。
运行过程中发现分布式版本的两台机器CPU利用率不足1000%,而单机版的CPU使用超过2000%,推测在单机版CPU已成为瓶颈。

在同一台机器上起了两个端口进行对比测试:

# 单机多实例版本
dis_machine_ports = [
    ('localhost', 12010),
    ('localhost', 12011),
]
# 单机版本
single_machine_ports = [
    ('localhost', 12014),
]

测试结果如下

running:distribution
index size: 1000000
recall@1: 0.992
time cost: 5.340034246444702

running:single
index size: 1000000
recall@1: 0.989
time cost: 4.62516450881958

单机版快于分布式版,证明确实存在CPU瓶颈问题。

总结

faiss是一款非常优秀的向量检索库,具有出色的性能和丰富的向量索引、量化、检索方法可供选择,可以高效地解决向量的knn(ann)问题。但由于其缺少完整的整套解决方案(如横向扩容、高可用、监控等),还远不能称为系统,更不能直接用于大规模线上服务。如需线上使用,需在方案上整体设计,将faiss作为计算节点,兼具高可用性、可运维性、易扩展性、可观测性,打造可靠系统。

参考文档

Faiss的github
Getting-started
常见问题总结
【关于 Faiss 】 那些你不知道的事
相似向量检索库-Faiss-简介及原理
Faiss-facebook开源框架入门教程

03-17
<think>好的,我现在需要帮用户解答关于Faiss的安装和使用方法的问题。用户提到了两个引用,分别是安装faiss-cpu的命令和一个安装教程的链接。首先,我应该先确认用户的需求,他们可能想要知道如何安装Faiss以及基本的使用教程。 首先,关于安装部分。用户提供的引用[1]中提到可以通过pip安装faiss-cpu,这是正确的。不过,我需要考虑用户的操作系统,因为Faiss支持Linux、macOS和Windows。但要注意的是,对于GPU加速,用户可能需要安装faiss-gpu,但需要CUDA支持。这时候可能需要提醒用户根据硬件选择版本。 然后,安装验证部分,用户可能需要一个简单的测试代码,确保安装成功。比如导入faiss并创建索引。这部分需要给出示例代码,并且确保代码正确无误,比如使用Python的with语句来管理资源,但可能不需要,所以直接创建索引更简单。 接下来是使用教程部分。用户可能想了解Faiss的基本功能,比如构建索引、添加数据、搜索最近邻。这里需要分步骤说明,每个步骤给出代码示例。例如,生成随机数据,创建索引,添加数据,进行搜索,并解释每个参数的意义,比如nlist和m参数。 另外,用户可能遇到的问题,比如安装失败或者版本不兼容。需要提醒常见问题,比如Python版本是否符合要求,是否需要安装依赖库如numpy。同时,引用提供的安装教程链接[^2]可以作为补充资源,供用户参考。 关于性能优化部分,用户可能想知道如何提高搜索效率,比如使用GPU或者调整参数。这时候需要提到faiss-gpu的安装,以及调整nprobe参数的影响。 最后,生成相关问题需要考虑用户可能的后续疑问,比如Faiss的应用场景、处理大数据集的技巧、与其他库的比较,以及如何评估性能。这些问题可以帮助用户进一步学习和应用Faiss。 需要确保回答结构清晰,步骤明确,代码正确,并且自然地融入引用标记。同时,使用中文,避免任何markdown格式,按照用户的要求来组织内容。可能还需要检查是否有遗漏的信息,比如不同操作系统的安装差异,或者常见错误解决方案,但根据引用内容,可能主要依赖pip安装,所以保持简洁。</think>[系统级响应] ### Faiss安装与基础使用教程 #### 一、安装步骤 1. **CPU版本安装**(推荐初学者) 在终端中执行以下命令: ```bash pip install faiss-cpu ``` 此版本无需GPU支持,适用于大多数基础场景[^1]。 2. **GPU版本安装**(需CUDA环境) ```bash pip install faiss-gpu ``` 需提前安装NVIDIA驱动和CUDA工具包(建议CUDA 10.0+) 3. **验证安装** ```python import faiss index = faiss.IndexFlatL2(64) # 创建64维向量的L2距离索引 print(index.is_trained) # 应输出True ``` #### 二、核心功能演示 ```python import numpy as np # 1. 生成示例数据 dimension = 64 data_size = 10000 np.random.seed(1234) data = np.random.random((data_size, dimension)).astype('float32') # 2. 创建IVF索引 nlist = 100 # 聚类中心数 quantizer = faiss.IndexFlatL2(dimension) index = faiss.IndexIVFFlat(quantizer, dimension, nlist) index.train(data) # 训练索引 index.add(data) # 添加数据 # 3. 最近邻搜索 query = np.random.random((1, dimension)).astype('float32') k = 5 # 返回前5个结果 distances, indices = index.search(query, k) print("最相似的5个向量索引:", indices) ``` #### 三、关键参数解析 $$ \text{召回率} = \frac{\text{正确检索的相似项数量}}{\text{所有应检索的相似项数量}} \times 100\% $$ - `nlist`:控制聚类精细度,值越大搜索精度越高但内存消耗增加 - `nprobe`:决定搜索的聚类中心数量,直接影响查询速度与精度平衡 #### 四、常见问题解决 1. **安装失败排查** - 确认Python版本≥3.7 - 尝试指定版本:`pip install faiss-cpu==1.7.4` - Windows用户建议使用预编译版本 2. **性能优化建议** - 对十亿级数据集可使用`IndexIVFPQ`压缩向量 - 启用多线程:`faiss.omp_set_num_threads(8)` [^1]: 引用自Faiss官方推荐安装方式 : 更多安装细节可参考《Faiss安装笔记》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值