Faiss:加速大规模数据相似性搜索的利器

在机器学习和数据挖掘领域,相似性搜索是一项基本且重要的任务,它涉及到在大型数据集中找到与特定对象最相似的对象。Faiss是一个由Facebook AI Research开发的库,专门用于高效地进行相似性搜索和聚类,它之所以重要,是因为它提供了一种快速且准确的方式来执行这一任务,尤其是在处理大规模高维向量数据集时。

在本文中,将深入探讨Faiss的核心概念和功能。将介绍如何安装和使用Faiss,以及如何通过选择合适的索引结构、利用GPU加速和进行有效的数据预处理来优化Faiss的性能。此外,还将提供一些实用的示例,展示如何在实际应用中使用Faiss进行相似性搜索。

Faiss简介

在开始任何代码之前,许多人可能会问——Faiss是什么?
Faiss是由Facebook AI开发的一个库,专门用于高效地进行相似性搜索和聚类。它特别适合处理大规模的高维向量数据集,如图像和文本数据中的特征向量。Faiss之所以特殊,主要得益于以下几个方面:

  1. 高效的向量相似性搜索:Faiss提供了多种算法来快速找到一个向量在大型数据集中的最近邻和近邻,这对于机器学习和数据挖掘任务非常有用。例如,它支持K最近邻(K-Nearest Neighbors,KNN)搜索,这可以用于多种应用,如推荐系统、图像检索和自然语言处理。。
  2. 灵活的索引结构:Faiss支持多种索引结构,如HNSW(Hierarchical Navigable Small World)、IVF(Inverted Indexed Vector File)和PQ(Product Quantization),这些结构可以针对不同的数据和查询需求进行优化。HNSW适合于处理大规模数据集的近似最近邻搜索,而IVF和PQ则适用于需要高效存储和查询的场景。
  3. GPU加速:Faiss利用GPU进行向量计算,大大提高了相似性搜索的速度,尤其是在处理大规模数据集时。这种加速对于那些需要处理海量数据的应用来说至关重要,因为它可以显著减少搜索时间。
  4. 易于集成:Faiss可以很容易地集成到Python和其他编程语言中,并且与常见的机器学习框架(如TensorFlow和PyTorch)兼容。这使得开发者可以轻松地在他们的项目中使用Faiss,而无需进行复杂的调整。
  5. 可扩展性:Faiss可以水平扩展,支持在多个服务器和GPU上运行,非常适合处理和搜索百亿甚至千亿级别的向量。这种可扩展性使得Faiss成为处理大规模数据集的优选工具。

Faiss的基本概念是使用索引技术来加速相似性搜索。当我们有一组向量时,我们可以使用Faiss对它们进行索引——然后使用另一个向量(查询向量),我们在索引中搜索最相似的向量。Faiss不仅允许我们构建索引和搜索——它还大大加快了搜索时间,达到惊人的性能水平。

如何最好地使用Faiss?

  • 选择合适的索引:根据数据的特性和查询需求选择合适的索引结构,以实现最佳的搜索性能。
  • 利用GPU加速:如果可能的话,使用GPU来加速相似性搜索,尤其是在处理大规模数据集时。
  • 处理稀疏数据:Faiss支持稀疏数据的处理,这对于图像和文本数据特别有用。
  • 使用Faiss API:Faiss提供了丰富的API,可以方便地在各种机器学习应用中使用。
  • 考虑数据预处理:有效的数据预处理可以显著提高Faiss的性能,比如特征缩放和维度减少。

Faiss安装

安装Faiss非常简单,以下是安装Faiss的步骤:

  1. 使用conda

    • 如果使用的是conda环境管理器,可以通过以下命令安装Faiss:

      conda install -c pytorch faiss-gpu
      

      如果没有GPU或需要安装CPU版本的Faiss,可以安装faiss-cpu

      conda install -c pytorch faiss-cpu
      
  2. 使用pip

    • 如果使用的是pip,可以安装Faiss的CPU版本:

      pip install faiss-cpu
      
    • 如果有GPU支持,也可以安装GPU版本的Faiss:

      pip install faiss-gpu
      
  3. 验证安装

    • 安装完成后,可以通过以下命令验证Faiss是否安装成功:

      import faiss
      faiss.test()
      
    • 如果安装正确,这个命令会返回一个结果,表示Faiss的测试通过。


通过以上步骤,可以成功地安装Faiss,并验证其是否安装成功。

简单尝试

准备数据

首先,获取一些文本数据并处理成嵌入向量。

import requests
from io import StringIO
import pandas as pd# 获取数据
res = requests.get('https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/sick2014/SICK_train.txt')
# create dataframe
data = pd.read_csv(StringIO(res.text), sep='\t')
data.head()# 准备句子
sentences = data['sentence_A'].tolist() + data['sentence_B'].tolist()# 使用更多数据集
urls = ['https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2012/MSRpar.train.tsv','https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2012/MSRpar.test.tsv','https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2012/OnWN.test.tsv','https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2013/OnWN.test.tsv','https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2014/OnWN.test.tsv','https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2014/images.test.tsv','https://raw.githubusercontent.com/brmson/dataset-sts/master/data/sts/semeval-sts/2015/images.test.tsv'
]for url in urls:res = requests.get(url)data = pd.read_csv(StringIO(res.text), sep='\t', header=None, error_bad_lines=False)sentences.extend(data[1].tolist() + data[2].tolist())# 去重
sentences = [word for word in list(set(sentences)) if type(word) is str]# pip install sentence_transformers
from sentence_transformers import SentenceTransformer
# 初始化 transformer model
model = SentenceTransformer('bert-base-nli-mean-tokens')# 构建句子嵌入
sentence_embeddings = model.encode(sentences)
sentence_embeddings.shape

移除重复项,留下14.5K个唯一的句子,然后使用sentence-BERT库为每个句子构建密集向量表示。

构建Faiss索引

首先,可以使用IndexFlatL2来构建一个简单的索引。IndexFlatL2计算查询向量与索引中所有向量之间的L2(或欧几里得)距离。这种索引方法简单且准确,但可能不适用于大规模数据集,因为它在搜索时速度较慢。

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2Fea951a4be3acf9d379cc6f922be1468b37b7f9e5-1280x720.png&w=1920&q=75

在Python中,使用以下代码初始化IndexFlatL2索引,使用上述获得的嵌入向量维度(768维,句子嵌入的输出大小):

import faissd = sentence_embeddings.shape[1]
index = faiss.IndexFlatL2(d)
index.is_trained

通常,需要在加载数据之前对其进行训练的索引,使用is_trained方法来检查一个索引是否需要训练。IndexFlatL2是一个不需要训练的索引,所以应该返回False。准备好后,加载嵌入和查询:

index.add(sentence_embeddings)
index.ntotal
k = 4
xq = model.encode(["Someone sprints with a football"])
%%time
D, I = index.search(xq, k)  # search
print(I)  # [[8306  153 6241 8184]]
# CPU times: user 146 ms, sys: 11.7 ms, total: 158 ms
# Wall time: 7.78 ms
for i in I[0]:print(sentences[i])# ('A group of football players is running in the field',
# 'A group of people playing football is running in the field',
# 'Two groups of people are playing football',
# 'A person playing football is running past an official carrying a football')

这返回了与查询向量xq最接近的前k个向量,分别是8306,153,6241和8184。可以看到匹配的内容翻译过来就是——要么是拿着足球奔跑的人,要么是在足球比赛背景中。如果想从Faiss中提取数值向量,也可以这样做。

# we have 4 vectors to return (k) - so we initialize a zero array to hold them
vecs = np.zeros((k, d))
# then iterate through each ID from I and add the reconstructed vector to our zero-array
for i, val in enumerate(I[0].tolist()):vecs[i, :] = index.reconstruct(val)vecs.shape  # (4, 768)vecs[0][:100]

速度

IndexFlatL2索引在执行详尽搜索时计算成本高,且不易扩展。这种索引方法要求查询向量与索引中的每个向量逐一比较,对于一个包含14.5K个向量的数据集,每次搜索都会进行大约14.5K次L2距离计算。当数据集规模扩大到1M或10亿个向量时,即使只处理一个查询,搜索时间也会急剧增加。

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2F2a7cf4de5beb7a8addb82e6f899b24dd455847fa-1280x720.png&w=1920&q=75

返回结果所需毫秒数(y轴)/ 索引中的向量数(x轴)——仅依靠IndexFlatL2会迅速变得缓慢

例如,假设数据集包含1亿个向量,使用IndexFlatL2进行一次详尽搜索可能需要数小时。这对于实时应用来说是不切实际的。因此,为了提高搜索效率,需要采用更高效的索引策略,如分区索引或向量量化。

分区索引

Faiss提供了一种将索引划分为Voronoi细胞的方法,这是一种有效的策略来加速相似性搜索。通过将向量分配到特定的Voronoi细胞中,我们可以在引入新查询向量时,首先测量它与质心的距离,然后仅在相应的细胞中进行搜索。

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2Fca1ed9b80fd0788cee513ef75c1b8bd8daad8571-1400x748.png&w=1920&q=75

这种方法大大减少了需要比较的向量数量,从而显著加快了搜索速度。例如,如果我们有一个包含1亿个向量的数据集,使用分区索引可以从一个需要比较1亿次的情况减少到只需比较分区中的少量向量。

在Python中,可以通过以下步骤实现:

1. 初始化分区索引

nlist = 50  # 指定分区数量
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFFlat(quantizer, d, nlist)

2. 训练和添加数据

index.is_trainedindex.train(sentence_embeddings)
index.is_trained  # check if index is now trainedindex.add(sentence_embeddings)
index.ntotal  # number of embeddings indexed

3. 进行搜索

%%time
D, I = index.search(xq, k)  # search
print(I)# [[8306  153 6241 8184]]
# CPU times: user 0 ns, sys: 2.11 ms, total: 2.11 ms
# Wall time: 1.4 ms

通过增加nprobe参数,我们可以进一步优化搜索性能。nprobe定义了搜索时考虑的邻近细胞数量,增加它可以帮助找到更准确的近似结果。

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2Ff32a71b57eefa87ef461bb3412f9fc21bbd46514-2020x1270.png&w=2048&q=75

当nprobe == 1时(左图),搜索单个最近的细胞;当nprobe == 8时(右图),搜索八个最近的细胞。

可以轻松实现这个变化,可以通过nprobe参数增加要搜索的邻近细胞的数量。

index.nprobe = 10%%time
D, I = index.search(xq, k)  # search
print(I)# [[8306  153 6241 8184]]
# CPU times: user 5.8 ms, sys: 0 ns, total: 5.8 ms
# Wall time: 2.84 ms

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2F84b1a10186cdd9dec8ebcfff9a96dbc89951d4b6-1280x720.png&w=1920&q=75

查询时间/向量数量对于具有不同nprobe值的IVFFlat索引——1, 5, 10, 和 20

使用分区索引后,可以看到搜索时间明显减少,同时保持较高的搜索精度。这对于处理大规模数据集尤其重要,因为它允许我们以更快的速度进行近似搜索,而不会牺牲太多准确性。

向量重构

当我们使用分区索引(如IndexIVFFlat)时,原始向量与其在索引中的位置之间不再有直接的映射关系。这意味着如果我们尝试直接重构一个向量,例如使用index.reconstruct(),我们会遇到RuntimeError

为了能够重构向量,我们需要首先创建一个直接映射,这可以通过调用index.make_direct_map()来实现。这个步骤为每个向量分配一个唯一的ID,使得我们可以根据ID重构原始向量。

以下是如何创建直接映射并重构向量的示例:

index.make_direct_map()
vec = index.reconstruct(7460)[:100]  # 重构向量的前100个元素

从那时起,就可以像以前一样使用这些ID来重构向量了。

量化

在处理大规模数据集时,存储和检索完整的向量可能变得非常低效。幸运的是,Faiss提供了一种称为产品量化(Product Quantization,PQ)的向量压缩技术。PQ通过将原始向量分解成多个子向量,并为每个子向量集创建质心,从而实现向量的近似表示。

PQ的三个步骤如下:

  1. 子向量分割:将原始向量分割成多个子向量。
  2. 聚类操作:对每个子向量集执行聚类操作,创建多个质心。
  3. 向量替换:在子向量向量中,将每个子向量替换为其最近的特定质心的ID。

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2F6eb8071e80abf8fa8d6c170270efd5db52a3168f-1400x787.png&w=1920&q=75

通过使用IndexIVFPQ索引,可以实现这些步骤。在添加嵌入之前,我们需要训练索引,并且需要在索引中添加数据。

以下是如何使用PQ索引的示例:

m = 8  # 每个压缩向量的质心数量
bits = 8  # 每个质心的比特数quantizer = faiss.IndexFlatL2(d)  # 保持相同的L2距离flat索引
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, bits) # 训练索引
index.train(sentence_embeddings)# 添加向量
index.add(sentence_embeddings)

现在准备开始使用新索引进行搜索。

index.nprobe = 10  # align to previous IndexIVFFlat nprobe value%%time
D, I = index.search(xq, k)
print(I)# [[ 153 2912 8306 8043]]
# CPU times: user 2.95 ms, sys: 96 µs, total: 3.05 ms
# Wall time: 2.01 ms

通过使用PQ,可以显著减少存储空间和搜索时间,同时保持足够的搜索精度。这对于处理大规模数据集和实现高效的相似性搜索至关重要。

速度还是精度

通过添加产品量化(PQ),我们成功地将IVF搜索时间从约7.5毫秒减少到约5毫秒,这在小数据集上可能看起来微不足道,但在处理大规模数据集时,这个性能提升是非常显著的。

虽然返回的结果顺序略有不同,但我们的优化措施仍然能够提供高度相关的搜索结果,如:

[f'{i}: {sentences[i]}' for i in I[0]]

这些结果虽然与原始搜索有所不同,但仍然保持了高度的相关性,如:

  • ‘153: A group of people playing football is running in the field’
  • ‘2912: A group of football players running down the field.’
  • ‘8306: A group of football players is running in the field’
  • ‘8043: A football player is running past an official carrying a football’

这种权衡显示了在实际应用中,可以在保持搜索速度的同时,接受一定程度的搜索精度损失。这对于需要快速响应的应用场景尤为重要。

https://www.pinecone.io/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fvr8gru94%2Fproduction%2Ff0a368ac2ff6372615fef4eb3c30e89bfd54c22d-1280x720.png&w=1920&q=75

查询时间/向量数量

上图显示了随着索引大小的增加,不同索引策略下的查询时间差异。这表明,尽管存在精度损失,PQ和IVF的结合仍然能够提供高效的搜索解决方案。

总结

本文介绍了如何使用Faiss库来构建高性能的相似性搜索索引。探讨了Faiss的核心概念、主要功能以及如何安装和使用这个库。

此外,还探讨了如何通过添加产品量化(PQ)和IVF(Inverted Indexed Vector File)分区索引来进一步优化搜索速度。虽然这些优化可能以一定程度的搜索精度为代价,但它们在处理大规模数据集时提供了显著的速度提升。

最后,强调了Faiss在处理高维数据和提高搜索效率方面的潜力。无论是数据科学家还是机器学习工程师,Faiss都是一个强大的工具,可以帮助我们在相似性搜索任务中实现快速和准确的结果。

参考

  • https://www.pinecone.io/learn/series/faiss

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

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

相关文章

双指针算法——部分OJ题详解

目录 关于双指针算法: 1,对撞指针 2,快慢指针 部分OJ题详解 283.移动零 1089.复写零 202.快乐数 11.盛水最多的容器 611.有效三角形的个数 剑指offer 57.和为s的两个数字 15.三数之和 18.四数之和 关于双指针算法: …

[240622] X-CMD 发布 v0.3.12: 引入 codeberg,增强传统命令,改善对 Elvish 和 Fish 支持

目录 X-CMD 发布 v0.3.12✨ cb(codeberg.org) ,fjo,gitea✨ Elvish✨ fish✨ git✨ ls✨ last✨ ps✨ stat✨ id X-CMD 发布 v0.3.12 ✨ cb(codeberg.org) ,fjo,gitea 本次版本实验性引入了这三个代码仓库…

个性化光标和动态壁纸

光标 进入这个宝藏网页至美化 至美化 进入鼠标页面,选择自己喜欢的鼠标,进入相关页面 分为两种,那么热爱有钱的UU可以选择高清版 像我这种没钱的孩子或者觉得试用版够用的就使用上面的 点击下载 进入自己的文件夹,解压成功之…

【记录】使用远程SSH配置d2l环境(含装pytorch,同时适用于本地anaconda)

文章目录 前言一、从创建新环境开始二、使用步骤1.安装pytorch2.安装 d2l 包3.安装其他包4.使用jupyter notebook 前言 记录一下如何利用使用命令行进行anaconda配置 d2l环境、pytorch并进行训练深度学习模型。 一、从创建新环境开始 如果是本地直接装一个 anaconda 软件就行…

ReactNative和Android通信

初始化一个RN项目以后,接下来想要让Android与React Native通信 写一个继承自ReactContextBaseJavaModule类的子类,重写getName方法 package com.awesomeprojectimport android.util.Log import android.widget.Toast import com.facebook.react.bridge.…

MFC学习--CListCtrl复选框以及选择

如何展示复选框 //LVS_EX_CHECKBOXES每一行的最前面带个复选框//LVS_EX_FULLROWSELECT整行选中//LVS_EX_GRIDLINES网格线//LVS_EX_HEADERDRAGDROP列表头可以拖动m_listctl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES); 全选,全…

解析 flink sql 转化成flink job

文章目录 背景流程flink实例实现细节定义的规则定义的物理算子定义的flink exec node 背景 在很多计算引擎里,都会把sql 这种标准语言,转成计算引擎下底层实际的算子,因此理解此转换的流程对于理解整个过程非常重要 流程 flink实例 public…

视听分割相关论文阅读

1. End-to-End Referring Video Object Segmentation with Multimodal Transformers RVOS(视频中的参考对象分割)比RIS(图像中的参考对象分割)要困难得多,因为指代动作的文本表达通常无法从单个静态帧中正确推断出来。…

使用J-Link Commander查找STM32死机问题

接口:PA13,PA14,请勿连接复位引脚。 输入usb命令这里我已经连接过了STM32F407VET6了。 再输入connect命令这里我已经默认选择了SWD接口,4000K速率。 可以输入speed 4000命令选择4000K速率: 写一段崩溃代码进行测试: void CashCode(void){*((volatil…

区块链技术原理

1.起源: ➢ 中本聪(Satoshi Nakamoto), 2008 ➢ 比特币:一种点对点的电子现金系统 2.分布式账本技术原理 ➢ 将交易向全网所有节点进行广播 ➢ 众多记账节点对记账权达成共识,由共识确认的记账节点把记账区块发布给全网 ➢ 所有账本数据完整存储于区块…

机器学习基础:与Python关系和未来发展

目录 初识Python Python的由来 自由软件运动 编译方式的演进 Python语言的特点 语法简单,易于理解 语法结构清晰,快速上手 丰富的第三方库 机器学习 监督学习 无监督学习 半监督学习 欢迎回到我们的神经网络与深度学习Tensorflow实战学习&am…

MVVM架构详解:前端开发的理想选择

目录 前言1. MVVM架构概述1.1 MVVM架构的定义1.2 MVVM与MVC的区别 2. MVVM架构的核心组件2.1 模型(Model)2.2 视图(View)2.3 视图模型(ViewModel) 3. MVVM架构的优势3.1 分离关注点3.2 提高代码可测试性3.3…

AI大模型企业应用实战(14)-langchain的Embedding

1 安装依赖 ! pip install --upgrade langchain ! pip install --upgrade openai0.27.8 ! pip install -U langchain-openai ! pip show openai ! pip show langchain ! pip show langchain-openai 2 Embed_documents # 1. 导入所需的库 from langchain_openai import Open…

嵌入式中逻辑分析仪与示波器的基本原理

大家好,今天主要给大家分享一下,嵌入式中如何使用逻辑分析仪和示波器的方法,希望对大家有所帮助。 https://dreamsourcelab.cn/ 第一:什么是逻辑分析仪 是否遇到使用示波器分析数字电路的冏境:深度不够,时间太短,无法抓到想要的波形,没有协议内容解析? 逻辑分析仪…

Pnpm:包管理的新星,如何颠覆 Npm 和 Yarn

在探索现代 JavaScript 生态系统时,我们常常会遇到新兴技术的快速迭代和改进。其中,包管理工具的发展尤为重要,因为它们直接影响开发效率和项目性能。最近,pnpm 作为一种新的包管理工具引起了广泛关注。它不仅挑战了传统工具如 np…

vue3路由的使用

1、引用路由组件 npm install vue-router 2、创建路由 根据项目结构创建对应的组件(home\news\about) 在 src 目录下创建 router/index.ts //引入路由 import { createRouter,createWebHistory,createWebHashHistory } from vue-router import Home …

关于Pytorch转换为MindSpore的一点建议

一、事先准备 必须要对Mindspore有一些了解,因为这个框架确实有些和其它流程不一样的地方,比如算子计算、训练过程中的自动微分,所以这两个课程要好好过一遍,官网介绍文档最好也要过一遍 1、零基础Mindspore:https://…

linux服务器运行pycharm代码

一、pycharm代码上传服务器 1、进行配置 2、建立ssh连接(选择文件传输协议SFTP) 3、设置服务器名(自定义) 4、点击SSH配置右侧的"…",进行SSH内容设置: 5、输入服务器信息 6、进行本地项目与远程…

AIGC系列之一-一文理解什么是Embedding嵌入技术

摘要:嵌入技术(Embedding)是一种将高维数据映射到低维空间的技术,在人工智能与图形学研究中被广泛应用。本文将介绍嵌入技术的基本概念、原理以及在 AIGC(Artificial Intelligence and Graphics Computing)…