技术干货

Hello,Vector DB|可能是最易上手的 Faiss 教程

2023-07-25
Keshav Malik

Keshav Malik

Hello,Vector DB|可能是最易上手的 Faiss 教程

大家会不会有这样的疑问:

网易云音乐是如何根据我的音乐口味推荐相似歌曲的?淘宝是如何判断我的购买喜好的?手机相册又是如何识别照片中的人脸,并将同一个人的照片归为同一组的?

其实,实现这一切的背后技术就是相似性搜索(有时也被称为最近邻搜索)。相似性搜索在许多人工智能(AI)和机器学习(ML)应用中发挥着核心作用,能够找到与给定查询数据最相似的数据。例如,网易云音乐可以根据用户最喜欢的歌曲,在系统中查询与其最相似的歌曲集,这个过程就叫做相似性搜索。

尽管相似性搜索看起来非常强大,不过背后却存在一个问题,如果数据量非常大,传统的相似性搜索方法可能会十分低效。在此情况下,我们便需要用到 Faiss。Faiss 是 Facebook AI 开发的向量检索库,提供高效可靠的大规模数据相似性搜索解决方案。

本文为 「Hello,Vector Database」系列,将从 Milvus 和 Zilliz Cloud 的源起——Faiss 出发,聚焦 Faiss 的安装方法、最佳实践以及其和向量数据库的对比,话不多说,上 Faiss!

什么是 FAISS?

Faiss 全称为 Facebook AI Similarity Search,也就是 Facebook AI 相似性搜索。Faiss 一个向量检索库,专为处理大规模数据设计。

Faiss 中的核心概念就是“向量相似性”。简单解释一下,向量是一串数字,而向量相似性就是比较两个向量之间有多相似。举个例子,一首歌包含很多元素和特性,我们可以用一个数字来代表一个特性或元素。那么,一首歌就可以用一串数字(也就是向量)来表示。如果你需要搜索与你最喜欢的歌曲相似的歌曲,那么我们就可以通过比较歌曲向量之间的相似性的方法得到答案。

在这个过程中,我们可以使用 Faiss 快速、高效、准确地比较数百万(甚至数十亿)向量之间的相似性 。Faiss 就如同一个超级搜索引擎,能够以闪电般的速度扫描一个大型音乐数据库,从中精准定位到与你最爱歌曲相似的歌曲。

Faiss 的魔力不仅仅只局限在音乐推荐系统,许多日常生活中的应用场景都有可能用到 Faiss,例如图像识别、文本检索、数据聚类、数据分析等。总之,想从海量数据库中迅速找到相似数据,使用 Faiss 准没错。

安装 Faiss

以下教程将展示如何在 Linux 系统上安装 Faiss:

  1. 安装 Conda。

在安装 Faiss 之前,先在系统上安装 Conda。Conda 是一个开源软件包和环境管理系统,可在 Windows、macOS 和 Linux 操作系统上运行。根据以下步骤在 Linux 系统上安装 Conda。

  1. 官网下载 Miniconda 安装包。
  1. 完成安装包 Hash 校验。
  1. 打开终端,运行以下命令启动安装程序:

bash Miniconda3-latest-Linux-x86_64.sh

  1. 安装时,如不确定某些设置,可以选择默认选项,安装完成后可随时更改设置。
  1. 安装完成后,关闭终端并重新打开,这一步骤主要目的是确保激活所有更新。
  1. 检查安装是否正确。在终端或 Anaconda Prompt 中输入 conda list 并按下回车键。如果安装正确,将出现安装的软件包列表。

接下来便可以通过 Conda 安装 Faiss。Faiss 提供 2 个版本的软件包:CPU 版(faiss-cpu)和 GPU 版(faiss-gpu)。

我们可以通过以下 2 种方式,按需选择安装 CPU 或 GPU 版 Faiss:

  1. 通过 PyTorch Conda 安装 Faiss(推荐此种安装方式)。
  • 安装 CPU 版

conda install -c pytorch faiss-cpu

  • 安装 GPU 版

conda install -c pytorch faiss-gpu

  1. 通过 conda-forge 安装 Faiss。
  • 安装 CPU 版

conda install -c conda-forge faiss-cpu

  • 安装 GPU 版

conda install -c conda-forge faiss-gpu

如需查看 Conda 软件包来源,请使用 conda list 命令。

使用 SQuAD 数据集进行演示

现在,我们可以通过示例演示了解 Faiss 功能。本次示例中,将使用斯坦福的问答数据集(SQuAD)。SQuAD 是一个常用的自然语言处理(NLP)数据集,该数据集基于用户在百科中提出的问题,每个问题的答案都来自于对应阅读段落的一段文本,共计 500 多篇文章中的 10 万多个问答配对。

在我们深入学习示例代码前,请先下载 SQuAD 数据集:

  1. 下载 SQuAD 数据集(https://rajpurkar.github.io/SQuAD-explorer/)

本文示例将使用 SQuAD 1.1。你可以在此下载 SQuAD 1.1 数据集。下载完成后,请将下载的 JSON 文件(train-v1.1.json)保存在常用文件目录中。

  1. 读取下载的 JSON 文件,可以使用 Python JSON 加载数据。
with open('train-v1.1.json', 'r') as file:
    squad_data = json.load(file)
  1. 导入函数库

首先,导入所有必要的函数库,NumPy 用于进行数字运算,Faiss 用于进行相似性搜索,JSON 用于加载数据集,NLTK 用于将文本进行分词(tokenize)。

import numpy as np
import faiss
import json
from nltk.tokenize import word_tokenize
  1. 加载和预处理数据

第二步中,首先加载 SQuAD 数据集。该数据集是一个 JSON 文件,因此我们可以使用 JSON 模块的 load 函数来加载数据。

with open('train-v1.1.json', 'r') as file:
    squad_data = json.load(file)

数据集加载完成后,需要预处理数据。我们将使用 NLTK 的 word_tokenize 函数对每个段落进行分词。也就是说,使用这个函数,我们可以将一句话拆分为多个单独的单词。随后,我们将每个单词表示为一个独热编码(one-hot encoding)向量。

vocabulary = set(word for article in squad_data['data'] for paragraph in article['paragraphs'] for word in word_tokenize(paragraph['context']))
word_to_index = {word: index for index, word in enumerate(vocabulary)}

def convert_text_to_vector(text):
    words = word_tokenize(text)
    bow_vector = np.zeros(len(vocabulary))
    for word in words:
        if word in word_to_index:
            bow_vector[word_to_index[word]] = 1    return bow_vector

paragraph_vectors = [convert_text_to_vector(paragraph['context']) for article in squad_data['data'] for paragraph in article['paragraphs']]
  1. 构建索引

完成加载和预处理数据后,我们可以为这些数据构建 Faiss 索引。本示例将使用 IndexFlatL2 (基本的 L2 索引)的索引类型。

dimension = len(vocabulary)
index = faiss.IndexFlatL2(dimension)
# 将 NumPy 数组转换为 1 个二维数组
paragraph_vectors = np.stack(paragraph_vectors).astype('float32')
index.add(paragraph_vectors)
  1. 相似性搜索

建完索引之后就可以开始在数据集中查找与输入的搜索内容最相似的文本段落。

以下是搜索函数:

def search_for_paragraphs(search_term, num_results):
    search_vector = convert_text_to_vector(search_term)
    search_vector = np.array([search_vector]).astype('float32')
    distances, indexes = index.search(search_vector, num_results)
    for i, (distance, index) in enumerate(zip(distances[0], indexes[0])):
        print(f"Result {i+1}, Distance: {distance}")
        print(squad_data['data'][index]['paragraphs'][0]['context'])
        print()

指定搜索项为 “What is the capital of France?(法国的首都在哪里?)” ,且返回 5 个最相似的结果。

search_term = "What is the capital of France?"search_for_paragraphs(search_term, 5)

search_for_paragraphs()首先将我们的搜索项转换为编码向量。然后,使用索引上的 search 方法。使用 search 方法时需要指定我们需要多少个查询结果(也就是 num_results 的值)。search 方法返回 2 个二维数组:一个是最相似结果的向量距离,一个是其索引。我们可以使用索引在数据集中找到实际的段落,最终打印结果包含排名、向量距离和段落文本。

以上示例展示了如何使用 Faiss 查找相似文本片段。当然,Faiss 可以用于更复杂的应用场景,例如在高维空间中进行搜索。

最佳实践与技巧

熟悉数据:在使用 Faiss 前,需要先花一点时间理解数据。可以问自己一些问题,例如:这个数据集的数据量多大?数据信息是否完整?熟悉数据将有助于选择正确 Faiss 索引类型、确定处理数据的最佳方式。

数据预处理:数据预处理情况会极大影响 Faiss 的使用效果。对于文本数据,可以考虑使用更智能的方法将单词转换为数字,例如 TF-IDF 或 Word2Vec 等模型。对于图片数据,可以尝试使用卷积神经网络(CNN)来处理。

选择最适合的索引类型:Faiss 提供多种索引类型,每类索引都有不同的适用场景。有些索引可以高效处理高维度数据,有些索引适用于处理二进制向量,还有些索引则是专为处理海量数据而设计。因此,可以根据大家的需求和实际情况,选择最适合的索引类型。

批量查询:如果有多个查询需要同时运行,可以使用 Faiss 将其一同处理,一次性运行批量查询会更高效,Faiss 针对批式处理进行了优化。

调整参数:Faiss 支持灵活调参,例如构建索引时可以调节数据聚类数和查询次数(nprobe)。默认值并不一定能够发挥某一索引的最大功效。因此,可以尝试调节参数值,寻找最合适的参数设置。

向量数据库 VS Faiss

Faiss 是高效的近似最近邻(ANN)搜索解决方案,不过当面临数千万个向量需要存储和检索,并且同时需要实时响应或更多高级功能时,Faiss 的能力就有限了。

向量数据库可以有效解决这些问题:

  • 向量数据库支持基本的数据增删改查(CRUD)操作,可调节一致性等级,提供条件过滤等功能;
  • 向量数据库能够提供更强大的数据持久性,在灾难恢复和系统可用性方面表现更出色;
  • 向量数据库采用计算存储分离的分布式架构,支持负载平衡、灵活扩展,系统可用性更高;
  • 向量数据库提供多租户、指标监控、RBAC 等高级功能,提供各种编程语言的 SDK 和 RESTful API。

Milvus 是全球首个开源向量数据库,可以存储、索引和管理十亿级向量数据。Zilliz 将 Milvus 作为孵化项目贡献给了 LF AI & Data 基金会,2021 年 6 月,Milvus 从基金会中毕业。Milvus Lite 是由社区活跃成员嵇斌贡献的轻量级 Milvus 版本。

Zilliz Cloud 是基于 Milvus 构建的全托管向量数据库。使用 Zilliz Cloud,可将向量检索速度提高 10 倍多,方便开发人员轻松部署、灵活扩展向量搜索应用。

总结

本文介绍了什么是 Faiss、如何安装 Faiss 以及如何使用 Faiss。在本文的最后,我们还对比了向量数据库和 Faiss 之间的区别。Faiss 是一个十分有用的工具,可以帮助我们高效搜索海量数据,适用于不同场景。希望大家通过本文能有所启发,并开启新的探索、学习之旅。当然,也欢迎大家继续深入研究 Faiss 提供的各种索引类型,探索更复杂的数据预处理技术,或是尝试自己上手搭建一些向量相似性搜索应用!