Faiss原理和使用

参考自https://github.com/facebookresearch/faiss/wiki,https://blog.csdn.net/Kaiyuan_sjtu/article/details/121551473

Faiss

Faiss是一个用于高效相似性搜索和密集向量聚类的库。它包含搜索任意大小的向量集(大小由RAM决定)的算法。它还包含用于评估和参数调整的支持代码。Faiss用C++编写,带有完整的Python包装器。一些最有用的算法是在GPU上实现的。

什么是相似性搜索?

给定一组维度为 d d d的向量 { x 1 , . . . , x n } \{x_1,...,x_n\} {x1,...,xn},Faiss会在RAM中构建一个数据结构。构建结构之后,当给定一个维度为 d d d的新向量 x x x时,它会有效地执行以下操作:
i = a r g m i n i ∣ ∣ x − x i ∣ ∣ i=argmin_i||x-x_i|| i=argmini∣∣xxi∣∣
其中, ∣ ∣ ⋅ ∣ ∣ ||\cdot|| ∣∣∣∣是欧几里得距离(L2)。

在Faiss术语中,数据结构是一个索引,一个具有add方法以添加x_i向量的对象。注意,x_i的维度被假定为固定的。

这就是Faiss的全部内容。它还可以:

  • 不仅返回最近邻居,还返回第二近邻、第三近邻、…、第k近邻
  • 一次搜索多个向量而不是一个(批处理)。对于许多索引类型,这比逐个搜索向量更快
  • 以精度换取速度,即使用速度快10倍或使用内存少10倍的方法,代价是10%的时间会给出错误结果
  • 执行最大内积搜索 a r g m a x i ( x , x i ) argmax_i(x,x_i) argmaxi(x,xi)而不是最小欧几里得搜索。对其他距离( L 1 L1 L1 L i n f Linf Linf等)也有有限的支持
  • 返回查询点给定半径内的所有元素(范围搜索)
  • 将索引存储在磁盘上而不是RAM中
  • 索引二进制向量而不是浮点向量
  • 根据向量ID上的谓词忽略索引向量的子集

Faiss的原理

对于m个向量和n个向量做相似度比较,用暴力搜索的时间复杂度就是O(mn),在向量个数非常多的情况下,这种资源消耗十分巨大的。Faiss使用索引,并且以一些精度换取速度,能够大大加快搜索速度。

Faiss的核心原理有两个部分:

Product Quantizer(PQ——乘积量化)

矢量量化方法,即vector quantization,其具体定义是将向量中的点用一个有限子集来进行编码的过程。常见的聚类算法都是一种矢量量化方法。而在ANN(Approximate Nearest Neighbor,近似最近邻)搜索问题中,向量量化方法又以乘积量化(PQ,Product Quantization)最为典型。

PQ的Pretrain

PQ的Pre-train过程分为两步操作,第一步Cluster,第二步Assign,这两步合起来就是Faiss数据流的Train阶段,以一个128维的向量库为例:
向量库向量个数为N,则共有 N ∗ 128 N*128 N128维,那么将其进行切分,比如将一个向量切成4个子段,那么就是 N ∗ 4 ∗ 32 N*4*32 N432维。这个切分的参数为 M M M M = 4 M=4 M=4代表每一个向量被切成了4段。

在切分后,按所有向量的每一段进行Clustering,比如所有向量的第一段取出来做Clustering得到256个簇心(经验值),这样总共得到 256 ∗ M 256*M 256M个簇心。

做完Cluser后,对所有向量进行Assign操作,Assign就是把原来的N维的向量映射到M维,比如上面的 128 , 4 128,4 1284,对于每一段向量,都可以找到对应的最近的簇心ID,这样一个128维的向量可以由4个簇心ID表示,这样就完成了向量的压缩,把 N ∗ 128 N*128 N128压缩成了 N ∗ 4 N*4 N4

PQ的查询

对于查询向量,以相同的方式把其分成 M M M段,计算每一段向量与之前预训练好的簇心的距离,相当于本来是128维的查询向量和 N ∗ 128 N*128 N128维的向量库进行查询,现在变成了4*32的查询向量和 N ∗ 4 N*4 N4维的向量库进行查询,当N变大时,速度差距越来越大。
注意:相当于将向量的原始表示转化成向量的簇心ID表示,即一个向量[x1,x2,x3…]转换成了[y1,y2,y3,y4],其中y1,y2,y3,y4对应的是簇心ID。

Inverted File System(IVFPQ——倒排乘积量化)

PQ优化了向量距离计算的过程,但是对于每个库本身,仍然需要遍历整个库,因为对于查询向量,还是需要和库的全部簇心进行计算并相加,效率依旧不够高,所以就有了Faiss用到的另外一个关键技术——Inverted File System。

如果能够通过某种手段快速将全局遍历锁定为感兴趣区域,则可以舍去不必要的全局计算以及排序。倒排PQ乘积量化的“倒排”,就是这种思想,通过聚类的方式实现感兴趣区域的快速定位。

要想减少需要计算的目标向量的个数,做法就是直接对库里的所有向量做KMeans CLustering,假设簇心个数为1024,那么每来一个query向量,首先计算其与1024个粗聚类簇心的距离,然后选择距离最近的top N个簇,只计算查询向量与这几个簇底下的向量的距离,计算距离的方式就是前面的PQ。

Faiss具体的实现有个小细节,在计算查询向量和簇底下的向量的距离的时候,所有向量都会被转化成与簇心的残差,这类似于归一化操作,使得后面用PQ计算距离更准确一点。使用IVF后,需要计算的向量个数就少了几个数量级,提高向量检索的速度。

Faiss的使用

  • Flat
import numpy as np# 定义数据维度
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.# 导入 Faiss 库
import faiss  
# 创建一个 L2 距离的 Flat 索引,维度为 d
index = faiss.IndexFlatL2(d)  
# 打印索引是否已经训练(Flat 索引不需要训练)
print(index.is_trained)  
# 将数据库中的向量添加到索引中
index.add(xb)  
# 打印索引中的向量总数
print(index.ntotal)# 设置要检索的最近邻个数
k = 4  
# 对数据库中的前 5 个点进行搜索,检索每个点的 4 个最近邻
D, I = index.search(xb[:5], k)  
# 打印最近邻的索引
print(I)  
# 打印最近邻的距离
print(D)  
# 对所有查询点进行搜索,检索每个查询的 4 个最近邻
D, I = index.search(xq, k)  
# 打印前 5 个查询点的最近邻索引
print(I[:5])  
# 打印最后 5 个查询点的最近邻索引
print(I[-5:])  

这段代码演示了如何使用 Faiss 库创建一个基于 L2 距离(欧几里得距离)的最近邻搜索索引,向索引中添加数据点,然后使用这些数据点进行查询以找到最接近的 k 个邻居。代码中的 search 函数返回两个数组:D 包含与最近邻点的距离,而 I 包含最近邻点在数据库中的索引。

  • IVFFlat
import numpy as np# 定义数据的维度
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.import faiss# 定义量化列表的大小
nlist = 100  
# 定义要检索的最近邻个数
k = 4  
# 创建量化器,使用 L2 距离的 Flat 索引
quantizer = faiss.IndexFlatL2(d)  
# 创建倒排文件索引(IVF)结合量化器
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)                  
# 执行实际的搜索,检索每个查询的 4 个最近邻
D, I = index.search(xq, k)     
# 打印最后 5 个查询点的最近邻索引
print(I[-5:])                  # 设置 nprobe 参数,nprobe 默认是 1,尝试设置为更大的值
index.nprobe = 10              
# 使用更新的 nprobe 参数重新执行搜索
D, I = index.search(xq, k)     
# 再次打印最后 5 个查询点的最近邻索引
print(I[-5:])                  

这段代码演示了如何使用 Faiss 库创建一个倒排文件索引(Inverted File Index,IVF)结合量化的索引结构,用于高效的相似性搜索。IndexIVFFlat 索引首先使用量化器将数据库划分为多个列表(由 nlist 参数指定),然后对每个列表内的向量进行最近邻搜索。nprobe 参数控制搜索时考虑的列表数量,增加 nprobe 的值可以提高搜索的精度,但可能会降低搜索速度。

  • IVFPQ
import numpy as np# 定义数据的维度
d = 64                           # dimension
# 数据库中数据点的数量
nb = 100000                      # database size
# 查询的数量
nq = 10000                       # nb of queries
# 设置随机种子以保证结果可复现
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.import faiss# 定义每个列表中的聚类中心数
nlist = 100
# 定义每个聚类中的码本大小
m = 8
# 定义要检索的最近邻个数
k = 4  
# 创建量化器,使用 L2 距离的 Flat 索引
quantizer = faiss.IndexFlatL2(d)  
# 创建一个使用乘积量化(PQ)的倒排文件索引
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8)# 8 指每个子向量被编码为 8 位
# 训练索引,使用整个数据库
index.train(xb)
# 将数据库向量添加到索引中
index.add(xb)
# 对数据库中的前 5 个点进行搜索,检索每个点的 4 个最近邻进行测试
D, I = index.search(xb[:5], k) 
# 打印最近邻的索引
print(I)
# 打印最近邻的距离
print(D)
# 设置 nprobe 参数,使结果与之前的实验具有可比性
index.nprobe = 10              
# 使用更新的 nprobe 参数执行搜索
D, I = index.search(xq, k)     
# 打印最后 5 个查询点的最近邻索引
print(I[-5:])

这段代码演示了如何使用 Faiss 库创建一个使用乘积量化(Product Quantization, PQ)的倒排文件索引(Inverted File Index, IVF)。乘积量化是一种压缩技术,可以减少存储需求并加速最近邻搜索,同时保持较高的精度。IndexIVFPQ 索引首先使用量化器将数据空间划分为多个聚类(由 nlist 参数指定),然后在每个聚类内部使用乘积量化技术对向量进行编码和搜索。m 参数定义了每个聚类中的码本大小,而最后一个参数 8 指定了每个子向量使用 8 位进行编码。nprobe 参数控制搜索时考虑的聚类数量,增加 nprobe 的值可以提高搜索的精度,但可能会降低搜索速度。

  • GPU、Multipe-GPUs、PQFastScan、PQFastScanRefine、RefineComparison(https://github.com/facebookresearch/faiss/tree/main/tutorial/python)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/46691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue教程】一. 环境搭建与代码规范配置

目录 引言Vue 框架概述起源与设计理念核心特性优势 Vue 开发环境搭建环境要求安装 Vue CLI创建 Vue 项目项目结构介绍运行与构建 组件实例基础模板响应式更新 代码规范为什么要使用代码规范在 Vue 项目中使用 ESLint 、PrettierESLint配置 ESLintrules 自定义错误级别 Prettier…

推理的判定定理三种验证方式

1. 真值表技术 2. 公式转换法 3. 主析取范式法 参考:离散数学-电子科技大学

数据结构第21节 归并排序以及优化方案

归并排序(Merge Sort)是一种分治策略的排序算法。它将一个大数组分成两个子数组,递归地对它们进行排序,然后将排序后的子数组合并成一个有序数组。 Java代码实现: public class MergeSort {public static void main(…

4.Flink程序编程规范

目录 概述 概述 Flink程序编程规范 官网文档速递 1.Obtain an execution environment 获取执行环境2.Load/create the initial data 加载/创建初始数据 > 数据接入3.Specify transformations on this data 针对数据做处理操作 > 数据处理4.Specify where to put the re…

Java-使用Redisson实现的分布式锁

在使用Redisson实现的分布式锁时,可以很容易地在Java中加入多线程代码来模拟并发环境下 的锁行为。以下是一个使用Redisson的RLock接口创建分布式锁并在多线程环境中使用的示例代 码: 首先,需要在项目中添加Redisson的依赖。如果你使用Mav…

LLM-阿里 DashVector + langchain self-querying retriever 优化 RAG 实践【Query 优化】

文章目录 前言self querying 简介代码实现总结 前言 现在比较流行的 RAG 检索就是通过大模型 embedding 算法将数据嵌入向量数据库中,然后在将用户的查询向量化,从向量数据库中召回相似性数据,构造成 context template, 放到 LLM 中进行查询…

python如何判断变量是否可迭代

python如何判断变量是否可迭代?方法如下: 方法一: 适用于python2和python3 >>> from collections import Iterable >>> isinstance("str", Iterable) True 方法二: 适用于python3 s "hello …

InterSystems IRIS使用python pyodbc连接 windows环境,odbc驱动安装,DSN配置,数据源配置

一、创建的数据库和数据 SELECT 1SELECT $ZVERSIONCREATE TABLE MyApp.Person ( ID INT PRIMARY KEY, Name VARCHAR(100) NOT NULL, Age INT, Gender CHAR(1) );CREATE TABLE MyApp.Person2 ( ID INT PRIMARY KEY, Name VARCHAR(100) NOT NULL, Age INT, Gender CHA…

Gil-Pelaez inversion

一、特征函数 A.随即变量的特征函数定义与性质 B.特征函数与PDF的关系 傅里叶变换:C.特征函数与矩函数关系 二、Gil-Pelaez反演定理 输入功率 P i n P_{in}

八、Docker版MySQL主从复制

目录 一、MySQL主从复制原理就不做讲解了,详情请查看MySQL专栏 二、主从复制搭建步骤 1、新建主服务器容器实例3307 2、进入/usr/mysql/mysql-master/conf目录下新建my.cnf 3、修改完配置后,重启master实例 4、进入mysql-master容器 5、在mysql-ma…

MYSQL 四、mysql进阶 9(数据库的设计规范)

一、为什么需要数据库设计 二、范 式 2.1 范式简介 在关系型数据库中,关于数据表设计的基本原则、规则就称为范式。 可以理解为,一张数据表的设计结 构需要满足的某种设计标准的 级别 。要想设计一个结构合理的关系型数据库,必须满足一定的…

13 IP层协议-网际控制报文协议ICMP

计算机网络资料下载:CSDNhttps://mp.csdn.net/mp_blog/creation/editor/140148186 为了更有效的转发IP数据报和提高交付成果的机会,在网际层使用了网际控制报文协议ICMP。ICMP允许主机或路由器报告差错情况和提供有关异常情况的报告。ICMP不是高层协议数…

携程Java后端实习一面

携程的面试比较注重八股文和项目,算法相关没有字节腾讯严厉,大家参加携程的技术岗面试需要重视八股文和项目细节,要学会深挖项目,希望大家早日oc😊👍 HashMap底层原理,扩容机制,从并…

Java面试八股之Redis集群Cluster

Redis集群Cluster Redis Cluster是一种基于数据分片(Sharding)的分布式缓存和存储系统,它实现了数据的水平扩展、高可用性和自动故障转移。以下是对Redis Cluster模式详细实现流程的描述: 1. 初始化与配置 部署节点&#xff1a…

C++ //练习 15.15 定义你自己的Disc_quote和Bulk_quote。

C Primer(第5版) 练习 15.15 练习 15.15 定义你自己的Disc_quote和Bulk_quote。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 /******************************************************************…

Python酷库之旅-第三方库Pandas(026)

目录 一、用法精讲 65、pandas.bdate_range函数 65-1、语法 65-2、参数 65-3、功能 65-4、返回值 65-5、说明 65-6、用法 65-6-1、数据准备 65-6-2、代码示例 65-6-3、结果输出 66、pandas.period_range函数 66-1、语法 66-2、参数 66-3、功能 66-4、返回值 6…

Gooxi受邀参加第三届中国数据中心服务器与设备峰会

7月2-3日,第三届中国数据中心服务器与设备峰会在上海召开,作为国内最聚焦在服务器领域的专业峰会,吸引了来自全国的行业专家、服务器与机房设备厂家,企业IT用户,数据中心业主共同探讨AIGC时代下智算中心设备的设计之道…

2024年最新最全面的Apifox使用-自动化测试

正文 编排测试场景运行测试持续集成查看测试结果 编排测试场景 新建测试场景 测试场景用于将多个接口有序地组合在一起运行,用于测试一个完整业务流程。 打开 Apifox 后点击左侧菜单栏中的“自动化测试”,点击左上角 号,选择所归属的目录…

记录|.NET上位机开发和PLC通信的实现

本文记录源自:B站视频 实验结果:跟视频做下来是没有问题的。能运行。 目录 前言一、项目Step1. 创建项目Step2. 创建动态图片展示Step3. 创建图片型按钮Step4. 创建下拉框Step1~4的效果展示Step5. 编程实体类操作类Main函数 Step1~5的效果展示Main函数 最…

Binder框架(二) ServiceManager初始化

0、总体流程四部 开机由init进程解析init.rc文件启动servicemanager.rc。启动会调用main.cpp的main函数 main函数里面主要做了以下几件事 : 1.1 打开/dev/binder设备; 1.2 通过mmap映射设备的内存空间到ServiceManager进程中。 1.3 设置ServiceManager为context…