本文1W2000字,本文简要介绍了向量数据库,重点阐述了其在检索增强生成(RAG)应用中的关键作用。文章突出了ChromaDBPineconeWeaviate等热门数据库,强调了高效存储和检索对优化 RAG 性能的重要性。
文中深入探讨了各种索引技术和算法,对Annoy倒排文件(IVF)索引随机投影乘积量化局部敏感哈希(LSH)HNSWDBSCAN 算法进行了详细解读。此外,还介绍了余弦相似度欧几里得距离点积等相似性度量方法,以及它们在文档相似性图像检索协同过滤等实际应用场景中的应用。有些内容是翻译自 HuggingFace 和一些论文,更多 LLM 架构文章点击查看:LLM 架构专栏
欢迎加入大模型交流群:加群链接 https://docs.qq.com/doc/DS3VGS0NFVHNRR0Ru#
柏企阅文

欢迎大家点赞转发收藏和关注,您的推荐是我坚持下去的动力~~


1. 引言

在之前的博客中,我们已经介绍了如何将原始数据嵌入为向量。为了能够重复使用这些嵌入信息,我们需要存储这些向量,以便按需访问。为此,我们会使用一种特殊的数据库——向量数据库。

对于使用检索增强生成(RAGs)的大规模应用来说,高效存储和检索向量,并具备增删改查(CRUD)操作、元数据过滤和横向扩展等功能至关重要。像 ChromaDB、Pinecone 和 Weaviate 这样的向量数据库在这方面表现出色,能够提供快速的检索和相似性搜索服务。

集成合适的向量数据库对于最大化 RAG 的性能至关重要。根据具体用例仔细选择向量数据库,能够确保数据的无缝存储和检索,充分发挥检索增强生成模型的优势。

2. 向量数据库与其他数据库的对比

数据库对比

3. 向量数据库与向量索引的区别

在技术行业,有一种普遍的误解,认为向量数据库只是近似最近邻(ANN)搜索算法的包装器。

从本质上讲,向量数据库是针对非结构化数据的综合解决方案。与这种误解相反,它具备如今结构化/半结构化数据库管理系统中的一些用户友好特性,比如云原生、多租户和可扩展性。随着我们对本教程的深入探讨,就会发现它克服了独立向量索引的局限性,解决了可扩展性难题、集成复杂性,以及缺乏实时更新和内置安全措施等问题。

另一方面,像 FAISS 和 ScaNN 这样的轻量级 ANN 库是构建向量索引的工具。这些库旨在加速多维向量的最近邻搜索。虽然在生产系统中适用于小数据集,但随着数据集的增长,可扩展性就会成为一个挑战。

4. 热门向量数据库

热门向量数据库

4.1 向量数据库如何工作?

我们都知道,传统数据库以行和列的形式存储字符串、数字和其他类型的标量数据。而向量数据库则是基于向量进行操作的,因此其优化方式和查询方式都大不相同。

在传统数据库中,我们通常查询数据库中值与查询条件完全匹配的行。而在向量数据库中,我们通过应用相似性度量来找到与查询向量最相似的向量。

向量数据库结合使用多种不同的算法,这些算法都参与近似最近邻(ANN)搜索。这些算法通过各种索引技术来优化搜索过程。

这些算法组合成一个流程,能够快速准确地检索查询向量的邻近向量。由于向量数据库提供的是近似结果,我们主要考虑的是在准确性和速度之间进行权衡。结果越准确,查询速度就越慢。不过,一个优秀的系统能够以近乎完美的准确性实现超快速搜索。

以下是向量数据库的常见流程:

索引:向量数据库使用诸如 PQ、LSH 或 HNSW 等算法对向量进行索引(下文会详细介绍这些算法)。这一步将向量映射到一种数据结构,以便加快搜索速度。

查询:向量数据库将索引后的查询向量与数据集中的索引向量进行比较,找到最近邻向量(应用该索引使用的相似性度量)。

后处理:在某些情况下,向量数据库从数据集中检索最终的最近邻向量,并对其进行后处理以返回最终结果。这一步可能包括使用不同的相似性度量对最近邻向量重新排序。

在接下来的部分,我们将更详细地讨论这些算法中的每一种,并解释它们如何影响向量数据库的整体性能。

5. 索引技术

基于树的方法对于低维数据非常有效,并且可以提供精确的最近邻搜索。然而,由于“维度诅咒”,它们在高维空间中的性能通常会下降。此外,它们需要大量内存,对于大型数据集效率较低,这会导致构建时间更长和延迟更高。

量化方法在内存利用上效率较高,通过将向量压缩为紧凑的代码来实现快速搜索。但是,这种压缩可能会导致信息丢失,从而降低搜索准确性。另外,这些方法在训练阶段的计算成本较高,会增加构建时间。

哈希方法速度快且相对节省内存,它将相似的向量映射到同一个哈希桶中。在处理高维数据和大规模数据集时表现良好,具有较高的吞吐量。然而,由于哈希冲突可能会导致误报和漏报,从而降低搜索结果的质量。选择合适数量的哈希函数和哈希表至关重要,因为它们会显著影响性能。

聚类方法可以通过将搜索空间缩小到特定的聚类来加快搜索操作,但搜索结果的准确性可能会受到聚类质量的影响。聚类通常是一个批处理过程,这意味着它不太适合不断添加新向量的动态数据,因为这会导致频繁的重新索引。

基于图的方法在准确性和速度之间取得了较好的平衡。它们对高维数据很有效,并且可以提供高质量的搜索结果。但是,由于需要存储图结构,它们可能会占用大量内存,而且图的构建在计算上也很昂贵。

使用不同索引方法的各种数据库

向量数据库中算法的选择取决于任务的具体要求,包括数据集的大小和维度、可用的计算资源,以及在准确性和效率之间可接受的权衡。值得注意的是,许多现代向量数据库使用混合方法,结合不同方法的优点来实现高速和高精度。对于任何希望充分发挥向量数据库性能的人来说,了解这些算法及其权衡至关重要。现在,让我们来看看这些类别中每种流行的算法。

6. 精确匹配

6.1 扁平索引(暴力搜索)

扁平索引能提供完美的搜索质量,但代价是搜索速度较慢。扁平索引的内存利用率较为合理。

我们首先要了解的是最简单的索引——扁平索引。

扁平索引之所以被称为“扁平”,是因为我们不会对输入的向量进行任何修改。

由于不对向量进行近似或聚类,这些索引能产生最准确的结果。我们能获得完美的搜索质量,但这是以显著的搜索时间为代价的。

使用扁平索引时,我们引入查询向量 $x_q$,并将其与索引中的每个其他完整向量进行比较,计算与每个向量的距离。

使用扁平索引时,我们将搜索查询向量 $x_q$ 与索引中的每个其他向量进行比较。

在计算完所有这些距离后,我们会返回其中最近的 $k$ 个向量作为最匹配的结果,这就是 $k$ 近邻(kNN)搜索。

一旦计算完所有距离,我们就返回 $k$ 个最近的向量。

6.2 平衡搜索时间

扁平索引的准确性极高,但速度极慢。在相似性搜索中,搜索速度和搜索质量(准确性)之间总是存在权衡。

我们必须确定我们的用例的最佳平衡点在哪里。使用扁平索引时,情况如下:

扁平索引的搜索质量为100%,搜索速度为0%

在这里,我们的搜索速度完全未经过优化,这适用于许多较小的索引用例,或者搜索时间无关紧要的场景。但是,其他用例则需要在速度和质量之间取得更好的平衡。

那么,如何加快搜索速度呢?主要有两种方法:

  • 通过降维或减少表示向量值的比特数来减小向量大小。
  • 通过基于某些属性、相似性或距离对向量进行聚类或将其组织成树结构来缩小搜索范围,将搜索限制在最接近的聚类中,或者通过最相似的分支进行筛选。

使用这两种方法中的任何一种,都意味着我们不再进行详尽的最近邻搜索,而是进行近似最近邻(ANN)搜索,因为我们不再搜索整个全分辨率数据集。

因此,我们得到了一种更平衡的组合,兼顾了搜索速度和搜索时间:

通常,我们希望在搜索速度和搜索质量之间取得更平衡的组合。

7. 近似匹配

7.1 Annoy(Approximate Nearest Neighbor Oh Yeah)

在向量数据库中使用的基于树的算法中,有一个因其高效和简单而脱颖而出:Annoy,即“Approximate Nearest Neighbors Oh Yeah”。Annoy 是一个 C++库,带有 Python 绑定,由 Spotify 设计,用于在空间中搜索与给定查询点接近的点(近似匹配)。它创建基于文件的大型只读数据结构,并将其内存映射到内存中,允许多个进程共享相同的数据。

Annoy 通过使用随机投影树森林来执行高效的 ANN 搜索。该算法将点投影到随机超平面上,并根据点落在超平面的哪一侧对空间进行划分。这个过程递归地重复,形成一个分区二叉树。使用不同的随机种子创建一个树森林。当收到查询点时,算法遍历森林中的每棵树,找到该点所属的叶节点。然后,通过收集所有树中找到的叶节点中的点列表,并从该列表中返回与查询点最接近的前 $k$ 个点,来近似最近邻。

Annoy 特别适合处理高维数据,在高维数据中进行精确的最近邻搜索成本可能过高。Spotify 在生产环境中使用 Annoy 进行音乐推荐,根据歌曲的音频特征找到相似的歌曲。因此,准确性会受到森林中树的数量和搜索过程中检查的点的数量的影响,这两个参数都可以根据任务的具体要求进行调整。

Annoy 的高效性和内存高效性使其成为处理高维数据和大型数据库的有力选择。不过,也有一些需要考虑的权衡因素。构建索引可能需要花费大量时间,特别是对于大型数据集。由于 Annoy 使用随机森林分区算法,索引无法使用新数据进行更新,必须从头重新构建。根据数据集的大小以及数据变化的频繁程度,重新训练索引的成本可能过高。

7.2 倒排文件(IVF)索引

IVF 具有良好的搜索质量、不错的搜索速度和合理的内存使用。条形图中半填充的部分表示修改索引参数时性能的变化范围。

倒排文件索引(IVF)通过聚类来缩小搜索范围。它是一种非常受欢迎的索引,因为它易于使用,具有较高的搜索质量和合理的搜索速度。

它基于 Voronoi 图的概念,也称为 Dirichlet 镶嵌(这个名字更酷)。

为了理解 Voronoi 图,我们需要想象将高维向量放置在二维空间中。然后在二维空间中放置一些额外的点,这些点将成为我们的“聚类”(在我们的例子中是 Voronoi 单元)质心。

然后,我们从每个质心向外扩展相同的半径。在某个时刻,每个单元圆的圆周会与另一个圆周碰撞,从而形成单元边界:

现在,每个数据点都将包含在一个单元内,并被分配给相应的质心。

与其他索引一样,我们引入查询向量 $x_q$,这个查询向量必须落在其中一个单元内,此时我们将搜索范围限制在这个单元内。

但是,如果查询向量落在单元的边缘附近,就会出现一个问题:它最接近的其他数据点很可能包含在相邻的单元中。我们称之为边缘问题:

现在,为了缓解这个问题并提高搜索质量,我们可以增加一个名为 $nprobe$ 的值的索引参数。通过 $nprobe$,我们可以设置要搜索的单元数量。

7.3 随机投影(RP)

随机投影的基本思想是使用随机投影矩阵将高维向量投影到低维空间。我们创建一个随机数矩阵,矩阵的大小将是我们想要的目标低维值。然后计算输入向量与矩阵的点积,得到一个投影矩阵,其维度比原始向量少,但仍然保留了它们的相似性。

当我们进行查询时,使用相同的投影矩阵将查询向量投影到低维空间。然后,将投影后的查询向量与数据库中的投影向量进行比较,找到最近邻。由于数据的维度降低了,搜索过程比在整个高维空间中搜索要快得多。

请记住,随机投影是一种近似方法,投影质量取决于投影矩阵的属性。一般来说,投影矩阵越随机,投影质量就越好。然而,生成真正的随机投影矩阵在计算上可能很昂贵,特别是对于大型数据集。

7.4 乘积量化(PQ)

另一种构建索引的方法是乘积量化(PQ),这是一种用于高维向量(如向量嵌入)的有损压缩技术。它将原始向量分解为较小的块,通过为每个块创建一个代表性的“代码”来简化每个块的表示,然后将所有块重新组合在一起,同时不会丢失对相似性操作至关重要的信息。PQ 的过程可以分为四个步骤:分割、训练、编码和查询。

  • 分割:将向量分解为多个段。
  • 训练:为每个段构建一个“码本”。简单来说,算法会生成一组可能分配给向量的“代码”。在实践中,这个“码本”由对向量的每个段执行 k-means 聚类得到的聚类中心点组成。段码本中的值的数量与我们用于 k-means 聚类的值相同。
  • 编码:算法为每个段分配一个特定的代码。在实践中,训练完成后,我们在码本中找到与每个向量段最接近的值。我们为该段的 PQ 代码将是码本中相应值的标识符。我们可以根据需要使用任意数量的 PQ 代码,这意味着我们可以从码本中选择多个值来表示每个段。
  • 查询:当我们进行查询时,算法将向量分解为子向量,并使用相同的码本对它们进行量化。然后,它使用索引代码找到与查询向量最接近的向量。

码本中代表性向量的数量是在表示的准确性和搜索码本的计算成本之间的权衡。码本中的代表性向量越多,在子空间中向量的表示就越准确,但搜索码本的计算成本就越高。相反,码本中的代表性向量越少,表示就越不准确,但计算成本就越低。

7.5 局部敏感哈希(LSH)

LSH 的性能范围很广,在很大程度上取决于设置的参数。搜索速度较慢时能得到较好的结果质量,而快速搜索则会导致结果质量较差。在处理高维数据时性能不佳。条形图中半填充的部分表示修改索引参数时性能的变化范围。

局部敏感哈希(LSH)的工作原理是通过一个哈希函数对向量进行处理,将相似的向量分组到同一个桶中,这个哈希函数的目的是最大化哈希冲突,而不是像通常的哈希函数那样最小化冲突。

这是什么意思呢?想象我们有一个 Python 字典。当我们在字典中创建一个新的键值对时,我们使用哈希函数对键进行哈希。键的这个哈希值决定了我们存储其对应值的“桶”:

一个类似字典的对象的典型哈希函数会试图最小化哈希冲突,目标是为每个桶只分配一个值。

Python 字典就是一个使用典型哈希函数的哈希表的例子,这种哈希函数会最小化哈希冲突,即两个不同的对象(键)产生相同的哈希值的情况。

在我们的字典中,我们希望避免这些冲突,因为这意味着多个对象会映射到同一个键上。但对于 LSH 来说,我们希望最大化哈希冲突。

为什么我们想要最大化冲突呢?嗯,为了进行搜索,我们使用 LSH 将相似的对象分组在一起。当我们引入一个新的查询对象(或向量)时,我们的 LSH 算法可以用来找到最匹配的组:

LSH的哈希函数试图最大化哈希冲突,从而对向量进行分组

7.6 分层可导航小世界图(HNSW)

HNSW 具有很好的搜索质量和不错的搜索速度,但索引大小较大。条形图中半填充的部分表示修改索引参数时性能的变化范围。

HNSW 创建一个分层的树状结构,树的每个节点代表一组向量。节点之间的边表示向量之间的相似性。该算法首先创建一组节点,每个节点包含少量向量。这可以通过随机方式或使用像 k-means 这样的聚类算法来完成,其中每个聚类成为一个节点。

然后,算法检查每个节点的向量,并在该节点和包含与它最相似向量的节点之间绘制一条边。

当我们查询 HNSW 索引时,它会使用这个图在树中导航,访问最有可能包含与查询向量最接近的向量的节点。

7.7 基于密度的空间聚类算法(DBSCAN)

DBSCAN 算法基于密度可达性和密度连通性的概念。它从数据集中的任意一个点开始,如果在给定半径eps内,该点周围至少有minPts个点,就会创建一个新的聚类。这里的eps代表 epsilon,是用户定义的输入参数,表示两个点在同一聚类中时,它们之间的最大距离;而minPts指的是形成一个聚类所需的最少数据点数量。

该算法会迭代地将eps半径内所有直接可达的点添加到聚类中。这个过程会一直持续,直到没有更多的点可以添加到聚类中。然后,算法会继续处理数据集中下一个未访问过的点,并重复上述过程,直到所有点都被访问过。

DBSCAN 算法中的关键参数是epsminPts,它们分别定义了点的聚类范围和形成聚类所需的最小点密度。

8. 相似性度量:距离度量

距离度量

相似性度量是用于确定向量空间中两个向量相似程度的数学方法。在向量数据库中,相似性度量用于比较存储在数据库中的向量,并找出与给定查询向量最相似的向量。

可以使用多种相似性度量方法,包括:

  • 余弦相似度:测量向量空间中两个向量之间夹角的余弦值。其取值范围是从-1 到 1,其中 1 表示完全相同的向量,0 表示正交向量(即相互垂直的向量),-1 表示方向完全相反的向量。
  • 欧几里得距离:测量向量空间中两个向量之间的直线距离。其取值范围是从 0 到无穷大,0 表示完全相同的向量,值越大表示向量之间的差异越大。
  • 点积:测量两个向量的模长之积与它们夹角余弦值的乘积。其取值范围是从负无穷到正无穷,正值表示向量方向相同,0 表示正交向量,负值表示向量方向相反 。

8.1 如何选择相似性度量指标?

一般来说,最好的做法是在搜索时使用与训练嵌入向量时相同的相似性度量。不过,相似性度量的选择还取决于数据的具体特征以及你要解决的问题的背景。以下是上述每种相似性度量的一些主要应用场景:

  • 欧几里得距离
    • 聚类分析:像 k-means 这样的聚类算法,是根据向量空间中的邻近程度对数据点进行分组的。
    • 异常和欺诈检测:在这些应用场景中,可以通过与正常交易质心的距离异常大这一特征,检测出异常数据点。
  • 点积
    • 图像检索和匹配:具有相似视觉内容的图像,其向量会紧密对齐,从而产生较高的点积值。因此,当你想要找到与给定查询图像相似的图像时,点积是一个不错的选择。
    • 神经网络和深度学习:在神经网络中,全连接层使用点积将输入特征与可学习的权重相结合。这有助于捕捉特征之间的关系,对分类和回归等任务很有帮助。
    • 音乐推荐:点积相似度有助于识别具有相似音频特征的曲目,这使得它在音乐推荐系统中很有价值。
  • 余弦相似度
    • 主题建模:在文档嵌入中,每个维度可以表示一个单词的频率或 TF-IDF 权重。然而,两篇长度不同的文档可能单词频率差异很大,但单词分布相同。由于这会使它们在向量空间中的方向相似,但距离不同,所以余弦相似度是一个很好的选择。
    • 文档相似性:这是主题建模的另一个应用场景。相似的文档嵌入具有相似的方向,但可能有不同的距离。
    • 协同过滤:推荐系统中的这种方法,利用用户(或物品)的集体偏好和行为来进行个性化推荐。用户(或物品)根据他们的交互行为被表示为向量。由于总体评分和受欢迎程度可能会导致不同的距离,但相似向量的方向仍然接近,所以经常使用余弦相似度。

9. 过滤

存储在数据库中的每个向量还包含元数据。除了能够查询相似向量外,向量数据库还可以根据元数据查询对结果进行过滤。为此,向量数据库通常会维护两个索引:一个向量索引和一个元数据索引。然后,它会在向量搜索之前或之后执行元数据过滤,但无论哪种情况,都存在一些困难,会导致查询过程变慢。

过滤过程可以在向量搜索之前或之后进行,但每种方法都有其挑战,可能会影响查询性能:

  • 预过滤:在这种方法中,元数据过滤在向量搜索之前完成。虽然这有助于减少搜索空间,但也可能导致系统忽略与元数据过滤标准不匹配的相关结果。此外,大量的元数据过滤可能会由于额外的计算开销而减慢查询过程。
  • 后过滤:在这种方法中,元数据过滤在向量搜索之后进行。这有助于确保考虑所有相关结果,但也可能会引入额外的开销,并在搜索完成后过滤掉不相关结果时减慢查询过程。

为了优化过滤过程,向量数据库会使用各种技术,例如利用先进的元数据索引方法或使用并行处理来加速过滤任务。在向量数据库中,平衡搜索性能和过滤准确性之间的权衡,对于提供高效且相关的查询结果至关重要。

10. 选择向量数据库

以下是一个简洁的总结,可帮助你做出决策:

  • 开源和云托管:如果你倾向于开源解决方案,Weaviate、Milvus 和 Chroma 是主要的选择。Pinecone 虽然不是开源的,但它在开发者体验和强大的完全托管解决方案方面表现出色。
  • 性能:在每秒查询数的原始性能方面,Milvus 领先,Weaviate 和 Qdrant 紧随其后。然而,在延迟方面,Pinecone 和 Milvus 都能提供令人印象深刻的低于 2 毫秒的结果。如果为 Pinecone 添加多个 pod,那么可以达到更高的每秒查询数。
  • 社区影响力:Milvus 拥有最大的社区,其次是 Weaviate 和 Elasticsearch。强大的社区通常意味着更好的支持、功能增强和漏洞修复。
  • 可扩展性、高级功能和安全性:基于角色的访问控制是许多企业应用程序的关键功能,Pinecone、Milvus 和 Elasticsearch 都具备这一功能。在扩展方面,Milvus 和 Chroma 提供动态段放置功能,使其适合不断发展的数据集。如果你需要支持多种索引类型的数据库,Milvus 对 11 种不同类型的支持是无可比拟的。虽然混合搜索在各个数据库中都得到了很好的支持,但 Elasticsearch 在磁盘索引支持方面有所欠缺。

总之,选择向量数据库没有一刀切的方法。理想的选择因具体项目需求、预算限制和个人偏好而异。

结论

本文简要介绍了向量数据库,重点阐述了其在检索增强生成(RAG)应用中的关键作用。文章突出了 ChromaDB、Pinecone 和 Weaviate 等热门数据库,强调了高效存储和检索对优化 RAG 性能的重要性。

文中深入探讨了各种索引技术和算法,对 Annoy、倒排文件(IVF)索引、随机投影、乘积量化、局部敏感哈希(LSH)、HNSW 和 DBSCAN 算法进行了详细解读。此外,还介绍了余弦相似度、欧几里得距离和点积等相似性度量方法,以及它们在文档相似性、图像检索和协同过滤等实际应用场景中的应用。