赞
踩
相信很多同学是第一次听说它,或者只知道它的大名,而不知该如何使用它。本篇文章,我们就来聊聊 faiss,分享这个“黑科技”是如何发挥神奇的“魔法”的。
faiss 是相似度检索方案中的佼佼者,是来自 Meta AI(原 Facebook Research)的 开源项目 ,也是目前最流行的、效率比较高的相似度检索方案之一。虽然它和相似度检索这门技术颇受欢迎,在出现在了各种我们所熟知的“大厂”应用的功能中,但毕竟属于小众场景,有着不低的掌握门槛和复杂性。
所以,不要想着一口气就完全掌握它,咱们一步一步来。
当然,如果你实在懒得了解,希望能够和写简单的 Web 项目一样,写几行 CRUD 就能够完成高效的向量检索功能,可以试试启动一个 Milvus 实例 。或者更懒一些的话,可以试着使用 Milvus 的 Cloud 服务 ,来完成高性能的向量检索。
在正式使用 faiss 之前,我们需要先了解它的工作机制。
当我们把通过模型或者 AI 应用处理好的数据喂给它之后(“一堆特征向量”),它会根据一些固定的套路,例如像传统数据库进行查询优化加速那样,为这些数据建立索引。避免我们进行数据查询的时候,需要笨拙的在海量数据中进行一一比对,这就是它能够实现“高性能向量检索”的秘密。
我们熟知的互联网企业中比较赚钱的“搜广推”(搜索、广告、推荐)业务中,会使用它解决这些场景下的向量召回工作。在这些场景下,系统需要根据多个维度进行数据关联计算,因为实际业务场景中数据量非常大,很容易形成类似“笛卡尔积”这种变态的结果,即使减少维度数量,进行循环遍历,来获取某几个向量的相似度计算,在海量数据的场景下也是不现实的。
而 Faiss 就是解决这类海量数据场景下,想要快速得到和查询内容相似结果(Top K 个相似结果),为数不多的靠谱方案之一。
和我们在常见数据库里指定字段类型一样, Faiss 也能够指定数据类型,比如 IndexFlatL2、IndexHNSW、IndexIVF等二十来种类型,虽然类型名称看起来比较怪,和传统的字符串、数字、日期等数据看起来不大一样,但这些场景将能够帮助我们在不同的数据规模、业务场景下,带来出乎意料的高性能数据检索能力。反过来说,在不同的业务场景、不同数据量级、不同索引类型和参数大小的情况下,我们的应用性能指标也会存在非常大的差异,如何选择合适的索引,也是一门学问。(下文会提到)
除了支持丰富的索引类型之外,faiss 还能够运行在 CPU 和 GPU 两种环境中,同时可以使用 C++ 或者 Python 进行调用,也有开发者做了 Go-Faiss ,来满足 Golang 场景下的 faiss 使用。
对 Faiss 有了初步认识之后,我们来进行 Faiss 使用的前置准备。
为了尽可能减少不必要的问题,本篇文章中,我们使用 Linux 操作系统作为 faiss 的基础环境,同时使用 Python 作为和 faiss 交互的方式。
在之前的文章中,我介绍过如何准备 Linux 环境 和 Python 环境,如果你是 Linux 系统新手,可以阅读这篇文章,从零到一完成系统环境的准备:《 在笔记本上搭建高性价比的 Linux 学习环境:基础篇 》;如果你不熟悉 Python 的环境配置,建议阅读这篇文章 《用让新海诚本人惊讶的 AI 模型制作属于你的动漫视频》 ,参考“准备工作”部分,完成 “Conda” 的安装配置。
在一切准备就绪之后,我们可以根据自己的设备状况,选择使用 CPU 版本的 faiss 还是 GPU 版本的 faiss,以及选择是否要指定搭配固定 CUDA 版本使用:
# 创建一个干净的环境 conda create -n faiss -y # 激活这个环境 conda activate faiss # 安装 CPU 版本 conda install -c pytorch python=3.8 faiss-cpu -y # 或者,安装 GPU 版本 conda install -c pytorch python=3.8 faiss-gpu -y # 或者,搭配指定 CUDA 版本使用 conda install -c pytorch python=3.8 faiss-gpu cudatoolkit=10.2 -y
在配置安装的时候,推荐使用 3.8 版本的 Python,避免不必要的兼容性问题。在准备好环境之后,我们就能够正式进入神奇的向量数据世界啦。
前文提到了,适合 faiss 施展拳脚的地方是向量数据的世界,所以,需要先进行向量数据的构建准备。
本文作为入门篇,就先不聊如何对声音(音频)、电影(视频)、指纹和人脸(图片)等数据进行向量数据构建啦。我们从最简单的文本数据上手,实现一个“基于向量检索技术的文本搜索功能”。接下来,我将以我最喜欢的小说 “金庸射雕三部曲”中的《射雕英雄传》为例,你可以根据自己的喜好调整要使用的文本数据。从网络上下载好要处理为向量的文本数据(txt 文档)。
我这里的原始 TXT 文档尺寸是 3 MB 大小,为了减少不必要的向量转化计算量,我们先对内容进行必要的预处理(数据的 ETL 过程),去掉不必要的重复内容,空行等:
cat /Users/soulteary/《哈利波特》.txt | tr -d ' ' | sed '/^[[:space:]]*$/d' > data.txt
打开文本仔细观察,数据中有一些行中的文本数据格外长,是由好多个句子组成的,会对我们的向量特征计算、以及精准定位检索结果造成影响的。所以,我们还需要进行进一步的内容调整,将多个长句拆成每行一个的短句子。
为了更好的解决句子换行的问题,以及避免将一段人物对话中的多个句子拆散到多行,我们可以使用一段简单的 Node.js 脚本来处理数据:
const { readFileSync, writeFileSync } = require("fs"); const raw = readFileSync("./hp.txt", "utf-8") .split("\n") .map((line) => line.replace(/。/g, "。\n").split("\n")) .flat() .join("\n") .replace(/“([\S]+?)”/g, (match) => match.replace(/\n/g, "")) .replace(/“([\S\r\n]+?)”/g, (match) => match.replace(/[\r\n]/g, "")) .split("\n") .map((line) => line.replace(/s/g, "").trim().replace(/s/g, "—")) .filter((line) => line) .join("\n"); writeFileSync("./ready.txt", raw);
我们执行 node .
将文本处理完毕之后,当前文件夹中将出现一个名为 ready.txt
的文本文件。
为了方便后文中,我们更具象的了解向量数据库的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。