Faiss流程与原理分析

2023-10-29

1、Faiss简介

  Faiss是Facebook AI团队开源的针对聚类和相似性搜索库,为稠密向量提供高效相似度搜索和聚类,支持十亿级别向量的搜索,是目前最为成熟的近似近邻搜索库。它包含多种搜索任意大小向量集(备注:向量集大小由RAM内存决定)的算法,以及用于算法评估和参数调整的支持代码。Faiss用C++编写,并提供与Numpy完美衔接的Python接口。除此以外,对一些核心算法提供了GPU实现。相关介绍参考《Faiss:Facebook 开源的相似性搜索类库

 2、Faiss安装

  参考《faiss_note/1.Install faiss安装.ipynb》,此文是对英文版本的翻译,便于查看。

      基于本机环境,采用了anaconda进行安装,这也是faiss推荐的方式,facebook研发团队也会及时推出faiss的新版本conda安装包,在conda安装时会自行安装所需的libgcc, mkl, numpy模块。

      针对mac os系统,可以先安装Homebrew(mac下的缺失包管理,比较方便使用)。

  安装anaconda的命令如下所示:

复制代码

#安装anaconda包
brew cask install anaconda
#conda加入环境变量
export PATH=/usr/local/anaconda3/bin:"$PATH"
#更新conda
conda update conda
#先安装mkl
conda install mkl
#安装faiss-cpu
conda install faiss-cpu -c pytorch
#测试安装是否成功
python -c "import faiss”

复制代码

  备注:mkl全称Intel Math Kernel Library,提供经过高度优化和大量线程化处理的数学例程,面向性能要求极高的科学、工程及金融等领域的应用。MKL是一款商用函数库(考虑版权问题,后续可以替换为OpenBLAS),在Intel CPU上,MKL的性能要远高于Eigen, OpenBLAS和其性能差距不是太大,但OpenBLAS提供的函数相对较少,另外OpenBLAS的编译依赖系统环境。

 3、Faiss原理及示例分析

  3.1 Faiss核心算法实现

  Faiss对一些基础的算法提供了非常高效的失效

  • 聚类Faiss提供了一个高效的k-means实现
  • PCA降维算法
  • PQ(ProductQuantizer)编码/解码

  3.2 Faiss功能流程说明

       通过Faiss文档介绍可以了解faiss的主要功能就是相似度搜索。如下图所示,以图片搜索为例,所谓相似度搜索,便是在给定的一堆图片中,寻找出我指定的目标最像的K张图片,也简称为KNN(K近邻)问题。

  为了解决KNN问题,在工程上需要实现对已有图库的存储,当用户指定检索图片后,需要知道如何从存储的图片库中找到最相似的K张图片。基于此,我们推测Faiss在应用场景中具备添加功能和搜索功能,有了添加相应的修改和删除功能也会接踵而来,从上述分析看,Faiss本质上是一个向量(矢量)数据库。

      对于数据库来说,时空优化是两个永恒的主题,即在存储上如何以更少的空间来存储更多的信息,在搜索上如何以更快的速度来搜索出更准确的信息。如何减少搜索所需的时间?在数据库中很最常见的操作便是加各种索引,把各种加速搜索算法的功能或空间换时间的策略都封装成各种各样的索引,以满足各种不同的引用场景。

 3.3 组件分析

      Faiss中最常用的是索引Index,而后是PCA降维、PQ乘积量化,这里针对Index和PQ进行说明,PCA降维从流程上都可以理解。

  3.3.1索引Index

      Faiss中有两个基础索引类Index、IndexBinary,下面我们先从类图进行分析。

  下面给出Index和IndexBinary的类图如下所示:

  Faiss提供了针对不同场景下应用对Index的封装类,这里我们针对Index基类进行说明。

  基础索引的说明参考:Faiss indexes涉及方法解释、参数说明以及推荐试用的工厂方法创建时的标识等。

      索引的创建提供了工厂方法,可以通过字符串灵活的创建不同的索引。

index = faiss.index_factory(d,"PCA32,IVF100,PQ8 ")

  该字符串的含义为:使用PCA算法将向量降维到32维, 划分成100个nprobe (搜索空间), 通过PQ算法将每个向量压缩成8bit。

  其他的字符串可以参考上文给出的Faiss indexes链接中给出的标识。

 3.3.1.1索引说明

  此部分对索引id进行说明,此部分的理解是基于PQ量化及Faiss创建不同的索引时选择的量化器而来,可能会稍有偏差,不影响对Faiss的使用操作。

      默认情况,Faiss会为每个输入的向量记录一个次序id,也可以为向量指定任意我们需要的id。部分索引类(IndexIVFFlat/IndexPQ/IndexIVFPQ等)有add_with_ids方法,可以为每个向量对应一个64-bit的id,搜索的时候返回此id。此段中说明的id从我的角度理解就是索引。(备注:id是long型数据,所有的索引id类型在Index基类中已经定义,参考类图中标注,typedef long idx_t;    ///< all indices are this type)

      示例:

复制代码

import numpy as np
import faiss                   # make faiss available

# 构造数据
import time
d = 64                           # dimension
nb = 1000000                      # database size
nq = 1000000                       # 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.

# 为向量集构建IndexFlatL2索引,它是最简单的索引类型,只执行强力L2距离搜索
index = faiss.IndexFlatL2(d)   # build the index
# #此处索引是按照默认方式,即faiss给的次序id为主
# #可以添加我们需要的索引方式,因IndexFlatL2不支持add_with_ids方法,需要借助IndexIDMap进行映射,代码如下
# ids = np.arange(100000, 200000)  #id设定为6位数整数,默认id从0开始,这里我们将其设置从100000开始
# index2 = faiss.IndexIDMap(index)
# index2.add_with_ids(xb, ids)
#
# print(index2.is_trained)
# # index.add(xb)                  # add vectors to the index
# print(index2.ntotal)
# k = 4   # we want to see 4 nearest neighbors
# D, I = index2.search(xb[:5], k) # sanity check
# print(I)     # 向量索引位置
# print(D)     # 相似度矩阵

print(index.is_trained)
index.add(xb)                  # add vectors to the index
print(index.ntotal)
k = 4   # we want to see 4 nearest neighbors
# D, I = index.search(xb[:5], k) # sanity check
# # print(xb[:5])
# print(I)     # 向量索引位置
# print(D)     # 相似度矩阵

D, I = index.search(xq, 10)     # actual search
# xq is the query data
# k is the num of neigbors you want to search
# D is the distance matrix between xq and k neigbors
# I is the index matrix of k neigbors
print(I[:5])                   # neighbors of the 5 first queries
print(I[-5:]) # neighbors of the 5 last queries

#从index中恢复数据,indexFlatL2索引就是将向量进行排序
# print(xb[381])
# print(index.reconstruct(381))

复制代码

 3.3.1.2索引选择

      此部分没做实践验证,对Faiss给的部分说明进行翻译过来作为后续我们使用的一个参考。

      如果关心返回精度,可以使用IndexFlatL2,该索引能确保返回精确结果。一般将其作为baseline与其他索引方式对比,以便在精度和时间开销之间做权衡。不支持add_with_ids,如果需要,可以用“IDMap”给予任意定义id。

      如果关注内存开销,可以使用“..., Flat“的索引,"..."是聚类操作,聚类之后将每个向量映射到相应的bucket。该索引类型并不会保存压缩之后的数据,而是保存原始数据,所以内存开销与原始数据一致。通过nprobe参数控制速度/精度。

      对内存开销比较关心的话,可以在聚类的基础上使用PQ成绩量化进行处理。

  3.3.1.3检索数据恢复

      Faiss检索返回的是数据的索引及数据的计算距离,在检索获得的索引后需要根据索引将原始数据取出。

      Faiss提供了两种方式,一种是一条一条的进行恢复,一种是批量恢复。

  给定id,可以使用reconstruct进行单条取出数据;可以使用reconstruct_n方法从index中回批量复出原始向量(备注:该方法从给的示例看是恢复连续的数据(0,10),如果索引是离散的话恢复数据暂时还没做实践)。

  上述方法支持IndexFlat, IndexIVFFlat (需要与make_direct_map结合), IndexIVFPQ(需要与make_direct_map结合)等几类索引类型。

  3.3.2PCA降维

      具体的算法流程没有进行深入的了解,可以参考看:《PCA 降维算法详解 以及代码示例》,待后续算法学习中在进行深入了解。

      基于3.2节中对Faiss流程的说明,简要说下对Faiss中PCA的理解。

      PCA通过数据压缩减少内存或者硬盘的使用以及数据降维加快机器学习的速度。从数据存储的角度,图片处理中通过PCA可以将图片从高维空间(p维)转换到低维空间(q维, 其中p > q ),其具体操作便是是将高维空间中的图片向量(n*p)乘以一个转换矩阵(p*q),得到一个低维空间中的向量(n*q)。

  为了使得在整个降维的过程中信息丢失最少,我们需要对待转换图片进行分析计算得到相应的转换矩阵(p*q)。也就是说这个降维中乘以的转换矩阵是与待转换图片息息相关的。回到我们的Faiss中来,假设我期望使用PCA预处理来减少Index中的存储空间,那在整个处理流程中,除了输入搜索图库外,我必须多输入一个转换矩阵,但是这个转换矩阵是与图库息息相关的,是可以由图库数据计算出来的。如果把这个转换矩阵看成一个参数的话,我们可以发现,在Faiss的一些预处理中,我们会引入一些参数,这些参数又无法一开始由人工来指定,只能通过喂样本来训练出来,所以Index中需要有这样的一个train() 函数来为这种参数的训练提供输入训练样本的接口。

  3.3.3Product quantization(乘积量化PQ)

      Faiss中使用的乘积量化是Faiss的作者在2011年发表的论文,参考:《Product Quantization for Nearest Neighbor Search

      PQ算法可以理解为首先把原始的向量空间分解为m个低维向量空间的笛卡尔积,并对分解得到的低维向量空间分别做量化。即是把原始D维向量(比如D=128)分成m组(比如m=4),每组就是D∗=D/m维的子向量(比如D∗=D/m=128/4=32),各自用kmeans算法学习到一个码本,然后这些码本的笛卡尔积就是原始D维向量对应的码本。用qj表示第j组子向量,用Cj表示其对应学习到的码本,那么原始D维向量对应的码本就是C=C1×C2×…×Cm。用k∗表示子向量的聚类中心点数或者说码本大小,那么原始D维向量对应的聚类中心点数或者说码本大小就是k=(k∗)m。

      示例参考《实例理解product quantization算法》。

 

 

https://www.cnblogs.com/yhzhou/p/10568728.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Faiss流程与原理分析 的相关文章

  • 如何验证无锁算法?

    从理论上讲 至少应该可以对无锁算法进行暴力验证 只有这么多的函数调用组合 是否有任何工具或正式推理过程可以实际证明无锁算法是正确的 理想情况下它还应该能够检查竞争条件和 ABA 问题 注意 如果你知道一种方法来证明一点 例如 只证明它不受
  • 生成字符串及其子字符串列表的排列的算法

    我已经忘记这个算法有一段时间了 假设我得到了字符串 cccaatt 我试图生成重复字母的每个子串的所有可能变体 EG cccaatt 作为输入将返回 猫 卡特 猫 卡特 ccat 卡特 卡特彼勒 卡特彼勒 cccat cccat cccaa
  • 算法 - 如何有效删除列表中的重复元素?

    有一个list L 它包含以下元素任意类型each 如何有效删除此类列表中的所有重复元素 必须保留订单 只需要一个算法 因此不允许导入任何外部库 相关问题 在Python中 从列表中删除重复项以使所有元素都是唯一的最快算法是什么在维持秩序的
  • 什么是确定性快速排序?

    我一直在阅读有关快速排序的内容 发现有时它被称为 确定性快速排序 这是普通快速排序的替代版本吗 普通快速排序和确定性快速排序有什么区别 普通 确定性 快速排序在特定数据集上的行为可能非常差 例如 选择第一个未排序元素的实现在已排序数据上的时
  • 找到质数?

    为了判断 N 是否是质数 我们只需要查找所有小于或等于 sqrt N 的数字 这是为什么 我正在编写 C 代码 因此试图理解其背后的原因 如果 N 是一个正整数 且能被两个正整数 1 和 N 整除 则 N 是素数 由于数字的约数不能大于该数
  • 对矩阵进行舍入,保留行和列总计

    想要 以保留行和列总计的方式对矩阵进行舍入的 伪 代码 问题从向量开始 X and Y of 非负整数 with Sum X Sum Y 想要圆X Y Sum X 同时保留行和列总计 这是婚姻问题的一种 Xa需要进行一定次数的握手 拨打该号
  • 如何在单向链表(一次遍历中)中从尾部获取第 n 个节点?

    所以我在一次考试中得到了这个问题 如何从单链表的尾部获取第 n 个节点 每个节点都有一个值和一个下一个 指向下一个值的指针 我们得到这个 getNodeFromTail Node head int x 所以我的做法是通过遍历一次来求出列表的
  • 运行时间为 O(n) 且就地排序的排序算法

    有没有运行时间为O n 并且还分类到位 在某些情况下 最好的情况是 O n 但这可能是因为项目集合已经排序 你正在看 O nlogn 一些较好的平均值 话虽如此 排序算法的 Wiki 还是相当不错的 有一个表格比较了流行的算法 说明了它们的
  • 更合适地说插入未排序动态数组的摊销 O(1) 与 O(n) ?

    这属于 stackoverflow com help on topic 中的 软件算法 在本例中 是一种将项目添加到动态未排序数组的软件算法 This is chart we made in class about the runtimes
  • 神经网络的层和神经元

    我想更多地了解神经网络 我正在开发一个 C 程序来制作神经网络 但我坚持使用反向传播算法 很抱歉没有提供一些工作代码 我知道有很多库可以用多种语言创建神经网络 但我更喜欢自己制作一个 关键是我不知道要实现特定目标 例如模式识别或函数近似或其
  • 有效地合并两个数组 - 一个已排序,另一个未排序

    我正在解决一个问题 该问题有一个由 n 个元素组成的排序数组 后跟一个未排序的长度数组 O logn O 平方 n 如何最有效地对整个列表进行排序 在上述两种情况下我应该使用哪种排序 由于将单个元素插入数组并保持其排序是O n 你不可能变得
  • 序列和与 GCD

    大约一个月前 我在编程挑战中遇到了这个问题 但社论尚未发布 所以我在这里问 有一个大小为 N 的数组 A 求 A 的 K 个长度子序列的总和 GCD Example 如果 A 1 2 3 且 K 2 1 2 3 总和 1 GCD 3 1 3
  • 从原点开始在离散 2D 网格上迭代向外螺旋的算法

    例如 这是预期螺旋的形状 以及迭代的每个步骤 y 16 15 14 13 12 17 4 3 2 11 18 5 0 1 10 x 19 6 7 8 9 20 21 22 23 24 其中线条是 x 轴和 y 轴 以下是算法每次迭代 返回
  • 线段树java实现[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 你知道 二进制 的良好实现吗线段树 http en wikipedia org wiki Segmen
  • 数独算法,暴力破解[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我正在尝试
  • 如何求两个地点的经纬度距离?

    我有一组位置的纬度和经度 怎么找distance从集合中的一个位置到另一个位置 有公式吗 半正矢公式假定地球是球形的 然而 地球的形状更为复杂 扁球体模型会给出更好的结果 如果需要这样的精度 你应该更好地使用文森特逆公式 See http
  • 如何在 C# 中以编程方式创建柔和的颜色?

    根据所需的颜色数量均匀分布地生成它们 如果指定的计数为 8 则看起来像这样 List
  • 如何在C中实现带连分数的自然对数?

    这里我有一个小问题 根据这个公式创建一些东西 这就是我所拥有的 但它不起作用 弗兰基 我真的不明白它应该如何工作 我尝试用一 些错误的指令对其进行编码 N 是迭代次数和分数部分 我认为它会以某种方式导致递归 但不知道如何 谢谢你的帮助 do
  • 如何计算 3D Morton 数(交织 3 个整数的位)

    我正在寻找一种快速计算 3D Morton 数的方法 这个网站 http www graphics stanford edu seander bithacks html InterleaveBMN有一个基于幻数的技巧来处理 2D Morto
  • JavaScript 中的埃拉托斯特尼筛法对大量数据无限运行

    我一直在尝试写埃拉托斯特尼筛法 http en wikipedia org wiki Sieve of EratosthenesJavaScript 中的算法 基本上我只是按照以下步骤操作 创建从 2 到 n 1 的连续整数列表 令第一个素

随机推荐

  • 计算机网络——第四章

    网络层 主要任务是把分组从源端传送到目的端 为分组交换网上的不同主机提供通信服务 传输单位是数据报 功能 1 路由选择与分组转发 2 异构网络互联 3 拥塞控制 若所有节点都来不及接受分组 而要丢弃大量分组的话 网络就处于拥塞状态 因此要采
  • python数据库-NumPy与Matplotlib库

    NumPy 1 导入numpy库 import numpy as np python中用import导入库 这里的意思是将怒骂朋友作为np导入 通过这样的形式 之后使用numpy相关方法用np使用 2 生成numpy数组 import nu
  • LeetCode--数组类算法:删除排序数组中的重复项 II

    题目 给定一个排序数组 你需要在原地删除重复出现的元素 使得每个元素最多出现两次 返回移除后数组的新长度 不要使用额外的数组空间 你必须在原地修改输入数组并在使用 O 1 额外空间的条件下完成 示例一 给定 nums 1 1 1 2 2 3
  • 梳理webpack

    一 入门 1 项目初始化 新建一个目录 初始化npm npm init 此时会需要填入一些项目的基本描述 webpack是运行在node环境中的 我们需要安装以下两个npm包 npm i D webpack webpack cli 生成no
  • 【mcuclub】扫码枪-(型号:M100(1D)-TTL)(型号:GM861S)

    一 实物图 型号 M100 1D TTL 只能扫描一维条形码 二 原理图 编号 名称 功能 1 VCC 电源正 2 GND 电源地 3 TXD 串口数据发送引脚 接单片机上的RX引脚 4 RXD 串口数据接收引脚 接单片机上的TX引脚 三
  • Unity 处理mono内存(堆内存)泄露问题

    先讲解一下mono特性 一个很重要的信息 mono内存从系统里面申请的内存不会返回给系统 mono内存不足的时候会预申请内存 内存大小不定有可能10m有可能5m 最近优化一个mono内存泄露问题 引起mono一直撑大多数都是内存泄露 要不就
  • ArrayBlockingQueue和LinkedBlockingQueue

    ArrayBlockingQueue ArrayBlockingQueue是一个用数组实现的有界阻塞队列 其是线程安全的 内部通过 互斥锁 保护竞争资源 此队列按照先进先出 FIFO 的原则对元素进行排序 队列的头部是在队列中存在时间最长的
  • el-tabs组件切换之前拦截函数异常踩坑记录

    背景 产品需求在离开当前tab之前要对页面填写信息进行校验 若没有任何改动则可以直接切换tab 若有改动 则需要在跳转之前进行拦截 提示用户 当前页面信息未保存 确定离开吗 确定或取消由用户选择 代码实现
  • 逆向工程核心原理——DLL注入——创建远程线程

    什么是DLL注入 dll注入是一种将Windows动态链接库注入到目标进程中的技术 具体的说 就是将dll文件加载到一个进程的虚拟地址空间中 对某个进程进行dll注入 也就意味着dll模块与该进程共用一个进程空间 则这个dll文件就有了操纵
  • 可变频率正弦信号发生器的FPGA实现(Quartus)

    一 说明 实现平台 Quartus17 1 MATLAB2021a和Modelsim SE 64 10 4 二 内容 1 产生一个完整周期的正弦波信号 并保存为 mif文件 2 设计一个ROM 将正弦波信号文件初始化如该ROM中 3 设计一
  • 内存分配---kmalloc

    kmalloc 内存分配引擎是一个功能强大的工具 下面我们来讲解一下这个函数 Kmalloc 函数分配内存时有几个特点 1 获取内存空间时不会对内存空间进行清零 也就是说 分配给它的区域仍然保持着原有的数据 2 它分配的区域在物理内存中也是
  • Ubuntu中火狐浏览器Firefox打不开网页

    浏览器地址栏输入 about config 搜索 general useragent override 无则新建 输入字符串 Mozilla 5 0 X11 Linux x86 64 AppleWebKit 537 36 KHTML lik
  • 2021-09-02防火墙和CDN、Ajax跨域

    欢迎大家一起来Hacking水友攻防实验室学习 渗透测试 代码审计 免杀逆向 实战分享 靶场靶机 求关注 CDN 内容分发网络 Content Delivery Network 简称CDN 是建立并覆盖在承载网之上 由分布在不同区域的边缘节
  • 如何查看mac系统是32位还是64位的操作系统

    一 点击工具栏左上角点击 苹果Logo 标志 关于本机 gt 更多信息 gt 系统报告 gt 左侧栏中 软件 二 打开终端 输入命令 uname a 回车 x86 64 表示系统为64位 i686 表示系统32位的 比如我的 三 在终端输入
  • js实现模糊搜索

    功能一 关键字搜索 总结 1 搜索出的结果 前台先要清空原有表格 tbody empty 2 后台返回的json格式字符串 js eval 专成对象var stus eval msg 在循环进行字符串拼接到表格上 tbody html st
  • Ubuntu上vsftpd安装与多用户目录配置

    vsftpd安装与多用户目录配置 文章配置使用Ubuntu进行配置 CentOS系统的配置也是大同小异 主要理解虚拟用户的加载方式和权限目录的配置 配置目标 在 home vsftpd 目录下有3个子目录分别为folder1 folder2
  • 二叉搜索树的建立和排序

    二叉搜索树的建立和排序 今天面了一家自研 有一道二叉搜索树的题目 但是自己做的不好 就是有几个学生和成绩 使用树来存储 左子树大于等于root 右节点小于root package org example public class Main
  • 《Apache MINA 2.0 用户指南》第二章:基础知识

    最近准备将Apache MINA 2 0 用户指南英文文档翻译给大家 但是我偶然一次百度 发现 Defonds 这位大牛已经翻译大部分文档 原文链接 http mina apache org mina project userguide c
  • LAN9252芯片控制资料

    一 整个ethercat项目开发流程 通过STM32相关学习板 理解EtherCAT协议栈和通信步骤 根据项目需求构建XML 该XML将会由TwinCAT2解析 将相关特STM32程序烧写 修改应用层协议的程序 STM32作为SPI主模式与
  • Faiss流程与原理分析

    1 Faiss简介 Faiss是Facebook AI团队开源的针对聚类和相似性搜索库 为稠密向量提供高效相似度搜索和聚类 支持十亿级别向量的搜索 是目前最为成熟的近似近邻搜索库 它包含多种搜索任意大小向量集 备注 向量集大小由RAM内存决