对Faiss中IndexFlatL2、IndexIVFFlat、IndexIVFPQ三种索引的总结和选择

本文通过实例对比了Faiss中的IndexFlatL2、IndexIVFFlat和IndexIVFPQ三种索引方法在存储和检索大量embedding时的性能和空间占用。IndexIVFPQ利用有损压缩实现高效存储,尽管精度有所下降,但空间节省显著。通过调整参数m,可以在精度和空间之间找到平衡。

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

由于项目和研究的需要,想要存储并检索大量的embedding,在之前的博客里,我尝试了一种方案:https://blog.csdn.net/qysh123/article/details/113754991

但是感觉太傻瓜了,听从建议,试了一下Faiss。不得不说,虽然Faiss感觉挺强大的,但是文档和说明依然是很垃圾。像这里给出了我们到底应该怎么选index:https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index

但是也有网友吐槽,按照你们的说明,还是内存爆了:https://github.com/facebookresearch/faiss/issues/1239

所以说很多项目最大的问题,是对初学者和想要迅速使用的人太不友好了,要程序员写文档,感觉脑子里都是浆糊。

还是参考一些博客才真正理解了:https://www.cnblogs.com/yhzhou/p/10569311.html

我以上面这篇博客为基础,尝试分析一下IndexFlatL2、IndexIVFFlat、IndexIVFPQ这三种索引。我把上面博客中的例子稍微修改了一下,首先我们生成1,000,000条假的数据:

import numpy as np

# 构造数据
import time
d = 50                           # dimension
nb = 1000000                     # database size
np.random.seed(1234)             # make reproducible
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.

print(xb[:1])

# 写入文件中
np.savetxt('data.txt', xb)

在这个基础上我们来看看,用这三种索引,分别会占用多少空间(用pickle导出,然后查看文件大小):

import numpy as np
import faiss
import pickle

# 读取文件形成numpy矩阵
data = []
with open('data.txt', 'rb') as f:
    for line in f:
        temp = line.split()
        data.append(temp)
print(data[0])
# 训练与需要计算的数据
dataArray = np.array(data).astype('float32')

# print(dataArray[0])
print(dataArray.shape)
# 获取数据的维度
d = dataArray.shape[1]

# IndexFlatL2索引方式
# # 为向量集构建IndexFlatL2索引,它是最简单的索引类型,只执行强力L2距离搜索
index = faiss.IndexFlatL2(d)
index.add(dataArray)

# # we want to see 11 nearest neighbors
k = 11
D, I = index.search(dataArray[:5], k)
# neighbors of the 5 first queries
print(I[:5])

f_Index=open('IndexFlatL2.pkl','wb')
pickle.dump(index, f_Index, protocol = 4)

# IndexIVFFlat索引方式
nlist = 100 # 单元格数
k = 11
quantizer = faiss.IndexFlatL2(d)  # the other index  d是向量维度
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2)
# here we specify METRIC_L2, by default it performs inner-product search

assert not index.is_trained
index.train(dataArray)
assert index.is_trained
index.add(dataArray)

index.nprobe = 10 # 执行搜索访问的单元格数(nlist以外)
D, I = index.search(dataArray[:5], k)
# neighbors of the 5 first queries
print(I[:5])

f_Index=open('IndexIVFFlat.pkl','wb')
pickle.dump(index, f_Index, protocol = 4)

nlist = 100
m = 10 #这里m需要是原维度d的整数商
k = 11
quantizer = faiss.IndexFlatL2(d)  # this remains the same
# 为了扩展到非常大的数据集,Faiss提供了基于产品量化器的有损压缩来压缩存储的向量的变体。压缩的方法基于乘积量化。
# 损失了一定精度为代价, 自身距离也不为0, 这是由于有损压缩。
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)
# 8 specifies that each sub-vector is encoded as 8 bits
index.train(dataArray)
index.add(dataArray)

f_Index=open('IndexIVFPQ.pkl','wb')
pickle.dump(index, f_Index, protocol = 4)

index.nprobe = 10              # make comparable with experiment above
D, I = index.search(dataArray[:5], k)     # search
print(I[:5])

我稍微把别人的代码改了一下,这段代码的运行结果如下:

[[   0  220  201  120  116   24   77  974  147  303  346]
 [   1  322  844    5  107  327  346  524  230   99  390]
 [   2  831   61  857  438   75 1059  320  466  923  611]
 [   3  747 1144  408 1354 1136  831 1162  494  942  968]
 [   4  386  461  281  419  782  466  531 1083 1128  285]]
[[   0  220  201  120  116   24   77  974  147  303  346]
 [   1  322  844    5  107  327  346  524  230   99  390]
 [   2  831   61  857  438   75 1059  320  466  923  611]
 [   3  747 1144  408 1354 1136  831 1162  494  942  968]
 [   4  386  461  281  419  782  466  531 1083 1128  285]]
[[   0  974  120   77   24  584   95  201  147  220   98]
 [   1  844  322  202 1143   98  531  234  689  309  629]
 [   2  320  278  342  293  466  121  344  857  348  831]
 [   3  408 1337  747 1240  921 1354  968 1562  288  493]
 [   4  281  151  793  219  341  461  212  285  531  386]]

稍微解释一下,每一行表示的是和查询向量相似度最高的向量的序号,当然每一个第一个结果都和自己最相似。我们看看标红的这几行,可以看出,IndexIVFFlat(第二组结果)和IndexFlatL2(第一组结果)是相同的,但是速度要快很多,IndexIVFPQ(第三组结果)由于是有损压缩,所以结果和前两组并不相同,但是:我们可以从标红的可以看到,前10名结果里还是有很多重复的。

从占据内存的大小来看,存储1,000,000个维度为50的embedding,前两个的空间分别为200和208MB,最后一个为18.1MB,所以IndexIVFPQ还是压缩得挺利害的。估算一下,如果embedding维度是512,那么存储5,000,000个embedding大概内存空间是:10GB,而IndexIVFPQ大概需要900MB。

从信息压缩的角度也可以理解,m = 10 #这里m需要是原维度d的整数商,这里决定了最后结果的精度,如果把m改成25,那么结果是这样的:

[[   0  220  201  120  116   24   77  974  147  303  346]
 [   1  322  844    5  107  327  346  524  230   99  390]
 [   2  831   61  857  438   75 1059  320  466  923  611]
 [   3  747 1144  408 1354 1136  831 1162  494  942  968]
 [   4  386  461  281  419  782  466  531 1083 1128  285]]
[[   0  220  201  120  116   24   77  974  147  303  346]
 [   1  322  844    5  107  327  346  524  230   99  390]
 [   2  831   61  857  438   75 1059  320  466  923  611]
 [   3  747 1144  408 1354 1136  831 1162  494  942  968]
 [   4  386  461  281  419  782  466  531 1083 1128  285]]
[[   0  201  220  120   24  147   77  303  116  467  346]
 [   1  322  327    5  346  524  510  390   99  107  333]
 [   2  831   61   75  466  320 1059  857   23  611 1144]
 [   3  747 1144  408 1136 1325 1354  831 1162  494   19]
 [   4  281  386  461  419 1083  466  531 1128  889  782]]

可以看到,此时和ground truth的差距已经不大了, 此时IndexIVFPQ的大小是33.1MB,不得不说,这个结果还挺利害的。

<think>好的,用户想了解Faiss的工作原理,特别是它在深度学习索引库中的应用。首先,我需要回忆一下Faiss的基本信息。Faiss是由Facebook AI Research开发的库,主要用于高效相似性搜索稠密向量聚类。它能够快速处理大规模向量数据,这在深度学习中非常常见,比如图像或文本的嵌入向量。 用户提到了IndexFlatL2IndexIVFFlatIndexIVFPQ这些索引类型,我需要解释这些索引的不同之处。比如,IndexFlatL2是暴力搜索,计算所有向量的L2距离,虽然准确但效率低。而IndexIVFFlat通过倒排索引结构,将向量分到不同的聚类中心,减少搜索范围,提高效率。IndexIVFPQ结合了乘积量化倒排索引,进一步压缩向量,节省内存同时保持搜索速度。 然后,用户可能想知道Faiss如何与深度学习结合。通常,深度学习模型生成的特征向量需要高效的检索,Faiss作为后端支持快速查找,比如在推荐系统或图像检索中应用。需要说明Faiss如何优化计算,比如使用GPU加速、量化技术等。 还要注意用户提供的引用内容,引用1提到了IVFADC索引,即基于倒排文件乘积量化的方法,这可能涉及分阶段处理,先粗粒度搜索再细粒度量化。引用2中的三种索引需要详细对比,说明各自的优缺点适用场景。 可能用户的实际需求是处理大规模数据时的性能问题,需要解释Faiss索引选择策略,如何根据数据规模、精度要求内存限制选择合适的索引类型。此外,需要覆盖Faiss的整体架构,如预处理、索引构建、搜索过程,以及底层优化技术如SIMD指令并行计算。 最后,生成相关问题时要围绕用户可能进一步询问的点,比如索引选择、性能优化、与其他库的对比,以及具体应用案例。确保回答结构清晰,分点说明,帮助用户逐步理解Faiss的工作原理应用方法。</think>### Faiss 工作原理 Faiss(Facebook AI Similarity Search)是专为稠密向量设计的相似性搜索库,其核心原理通过**索引结构优化****量化技术**实现高效搜索。以下从三个层面解析其工作原理: --- #### 一、索引结构设计 1. **暴力搜索(IndexFlatL2/IndexFlatIP)** 直接计算查询向量与全量向量的距离(如L2距离或内积),时间复杂度为$O(Nd)$,适合小规模数据集。例如: $$ \text{distance}(q, x) = \| q - x \|^2 $$ 2. **倒排索引IndexIVFFlat)** - **聚类预处理**:使用k-means将向量划分为$nlist$个聚类中心 - **倒排列表存储**:每个向量分配到最近的聚类中心,搜索时仅扫描目标聚类及其邻近区域[^1] - **时间复杂度**降低到$O(nlist + k \cdot d \cdot (N/nlist))$,其中$k$为搜索的聚类数 3. **乘积量化(IndexIVFPQ)** - **向量分段量化**:将向量划分为$m$个子空间,每个子空间用$k$个质心编码 - **压缩存储**:原始向量被替换为质心编号组合,存储空间降低至$m \cdot \log_2k$ bits - **非对称距离计算(ADC)**:查询时直接计算与量化质心的距离[^1] --- #### 二、深度学习整合 1. **特征向量生成** 通过CNN、Transformer等模型提取图像/文本特征,例如ResNet生成2048维特征向量。 2. **端到端优化** Faiss支持与PyTorch/TensorFlow集成,实现**索引训练与模型微调联合优化**。例如: ```python # PyTorch与Faiss联合使用示例 features = model(input_data) # 深度学习模型提取特征 index.add(features.detach().cpu().numpy()) # 将特征载入Faiss索引 ``` --- #### 三、性能优化技术 1. **分层搜索策略** 先通过倒排索引筛选候选集(粗搜),再使用乘积量化精确排序(精搜) 2. **硬件加速** - **SIMD指令集**:使用AVX2/AVX512加速向量运算 - **GPU并行化**:支持多GPU并行搜索,吞吐量提升10倍以上 3. **内存优化对比** | 索引类型 | 内存占用 | 搜索速度 | 精度 | |----------------|----------|----------|----------| | IndexFlatL2 | 100% | 慢 | 精确 | | IndexIVFFlat | 100% | 快 | 较高 | | IndexIVFPQ | 5-25% | 最快 | 可控损失 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值