作者:来自 Elastic Valentin Crettaz
本文是三篇系列文章中的第一篇,将深入探讨向量搜索(也称为语义搜索)的复杂性,以及它在 Elasticsearch 中的实现方式。
本文是三篇系列文章中的第一篇,将深入探讨向量搜索(也称为语义搜索)的复杂性以及它在 Elasticsearch 中的实现方式。
第一部分重点介绍嵌入向量的基础知识以及向量搜索的底层工作原理。
借助第一篇文章中学到的所有知识,第二部分将指导你了解如何在 Elasticsearch 中设置向量搜索。
在第三部分中,我们将利用前两部分中学到的知识,在此基础上深入研究如何在 Elasticsearch 中编写强大的混合搜索查询。
在深入探讨本文的真正内容之前,让我们回顾一下向量的一些历史,它是语义搜索中的关键概念。
向量搜索并不新鲜
可以肯定的是,自 2022 年 11 月 ChatGPT 问世以来,我们每天都会听到或读到有关 “向量搜索” 的信息。向量搜索无处不在,而且非常普遍,我们常常会觉得这是一项刚刚问世的尖端新技术,但事实是,这项技术已经存在了 60 多年!该主题的研究始于 1960 年代中期,第一篇研究论文于 1978 年由信息检索专家 Gerard Salton 及其康奈尔大学的同事发表。Salton 在密集和稀疏向量模型方面的工作构成了现代向量搜索技术的根基。
在过去的 20 年里,许多基于他的研究的向量 DBMS 被创建并推向市场。其中包括由 Apache Lucene 项目提供支持的 Elasticsearch,该项目于 2019 年开始研究向量搜索。
向量现在无处不在,如此普遍,因此在使用它们之前,首先要很好地掌握它们的基本理论和内部工作原理。在深入研究之前,让我们快速回顾一下词汇搜索和向量搜索之间的区别,以便我们更好地理解它们的区别以及它们如何相互补充。
向量搜索与词汇搜索
介绍向量搜索的一个简单方法是将其与你可能习惯的更传统的词汇搜索进行比较。向量搜索(通常也称为语义搜索)和词汇搜索的工作方式截然不同。词汇搜索是我们在 Elasticsearch 中多年来一直使用的搜索类型。简而言之,它不会尝试理解索引和查询内容的真正含义,而是会尽力将用户在查询中输入的单词或其变体(例如词干、同义词等)的文字与之前已使用相似性算法(例如 TF-IDF)索引到数据库中的所有文字进行词汇匹配。
![](https://i-blog.csdnimg.cn/direct/4f2bfd852ace41699de7f6a23b541daa.webp)
我们可以看到,左上角的三个文档被标记并分析。然后,将生成的术语编入倒排索引,该索引只是将分析的术语映射到包含它们的文档 ID。请注意,所有术语都只出现一次,并且没有一个与任何文档共享。搜索 “nice german teacher” 将匹配所有三个文档,分数各不相同,即使它们都没有真正抓住查询的真正含义。
如下图 2 所示,在处理多义词或同形异义词时,情况会变得更加棘手,即拼写相同但含义不同的单词(right、palm、bat、mean 等)。让我们以 “right” 这个词为例,它可以表示三种不同的含义,看看会发生什么。
![](https://i-blog.csdnimg.cn/direct/9a668f9042cb4420ab22845b13efa06a.webp)
搜索 “I’m not right” 会返回一个与第一个返回结果含义完全相反的文档。如果你搜索完全相同的术语,但以不同的顺序排列它们以产生不同的含义,例如 “turn right” 和 “right turn”,则会产生完全相同的结果(即第三个文档 “Take a right turn”)。诚然,我们的查询过于简单,没有使用短语匹配等更高级的查询,但这有助于说明词汇搜索不了解索引和搜索内容背后的真正含义。如果这不清楚,请不要担心,我们将在第三篇文章中重新讨论这个例子,看看向量搜索在这种情况下如何提供帮助。
公平地说,当你可以控制如何索引结构化数据(想想映射、文本分析、提取管道等)以及如何编写查询(想想巧妙编写的 DSL 查询、查询术语分析等)时,你就可以使用词汇搜索引擎创造奇迹,这是毫无疑问的!Elasticsearch 在词汇搜索功能方面的记录令人惊叹。它在过去几年中取得的成就以及它在多大程度上普及和改进了词汇搜索领域,这确实令人惊叹。
但是,当你负责为需要提出自由文本问题的用户提供查询非结构化数据(想想图像、视频、音频、原始文本等)的支持时,词汇搜索就显得力不从心了。此外,有时查询甚至不是文本,它可能是图像,我们很快就会看到。词汇搜索在这种情况下不足的主要原因是非结构化数据既不能像结构化数据那样被索引,也不能被查询。处理非结构化数据时,语义(semantics)就会发挥作用。语义是什么意思?很简单,就是含义!
我们以图像搜索引擎(例如 Google 图片搜索或 Lens)为例。你拖放一张图片,Google 语义搜索引擎就会找到并返回与你查询的图片最相似的图片。在下面的图 3 中,我们可以在左侧看到德国牧羊犬的图片,在右侧看到所有已检索到的类似图片,第一个结果是与提供的图片相同的图片(即最相似的图片)。
![](https://i-blog.csdnimg.cn/direct/931a82251575447bb249031748c490db.webp)
来源:Google 图片搜索, https://www.google.com/imghp
尽管这对于我们人类来说听起来简单而合乎逻辑,但对于计算机来说,情况就完全不同了。这就是向量搜索能够实现和帮助实现的。正如世界最近所见证的那样,向量搜索释放的力量是巨大的。现在让我们揭开它的面纱,看看下面隐藏着什么。
嵌入向量
正如我们之前所见,使用词汇搜索引擎,文本等结构化数据可以轻松标记为可在搜索时匹配的术语,而不管这些术语的真实含义如何。然而,非结构化数据可以采用不同的形式,例如大型二进制对象(图像、视频、音频等),并且根本不适合相同的标记(tokenizing)过程。此外,语义搜索的整个目的是以这样的方式索引数据,以便可以根据其所代表的含义进行搜索。我们如何实现这一点?答案就在两个词中:机器学习!或者更准确地说是深度学习!
深度学习(Deep Learning)是机器学习的一个特定领域,它依赖于基于人工神经网络的模型,该模型由多层处理组成,可以逐步提取数据的真正含义。这些神经网络模型的工作方式深受人脑的启发。下面的图 4 显示了神经网络的外观,包括输入层和输出层以及多个隐藏层:
![](https://i-blog.csdnimg.cn/direct/af618b31e4fa46b684e49ce294446059.webp)
来源:IBM,https://www.ibm.com/topics/neural-networks
神经网络的真正壮举是它们能够将单个非结构化数据转换为一系列浮点值,这些浮点值被称为嵌入向量或简称为嵌入。作为人类,只要我们在二维或三维空间中可视化它们,我们就能很好地理解什么是向量。向量的每个分量表示二维 x-y 平面或三维 x-y-z 空间中的坐标。
但是,神经网络模型工作的嵌入向量可以有几百甚至几千个维度,并且仅表示多维空间中的一个点。每个向量维度代表非结构化数据的一个特征(feature)或特性。让我们用一个深度学习模型来说明这一点,该模型将图像转换为 2048 维的嵌入向量。该模型会将我们在图 3 中使用的德国牧羊犬图片转换为下表所示的嵌入向量。请注意,我们仅显示第一个和最后一个元素,但表中还会有 2,042 个列/维度。
is_red | is_dog | blue_sky | … | no_gras | german_shepherd | is_tree | |
---|---|---|---|---|---|---|---|
German shepherd embeddings | 0.0121 | 0.9572 | 0.8735 | … | 0.1198 | 0.9712 | 0.0512 |
每一列都是模型的一个维度,代表底层神经网络试图建模的特征或特性。提供给模型的每个输入都将根据该输入与 2048 个维度的相似程度进行表征。因此,嵌入向量中每个元素的值表示该输入与特定维度的相似度。在这个例子中,我们可以看到模型检测到狗和德国牧羊犬之间有很高的相似性,并且还检测到了一些蓝天。
与词汇搜索(其中术语可以匹配也可以不匹配)相比,使用向量搜索,我们可以更好地了解一段非结构化数据与模型支持的每个维度的相似程度。因此,嵌入向量可以作为非结构化数据的极好的语义表示。
秘诀
现在我们知道了深度学习神经网络如何将非结构化数据切分为嵌入向量,从而捕捉大量维度上数据的相似性,我们需要了解这些向量的匹配是如何进行的。答案其实很简单。彼此接近的嵌入向量表示语义上相似的数据片段。因此,当我们查询向量数据库时,搜索输入(图像、文本等)首先使用与索引所有非结构化数据相同的模型转换为嵌入向量,最终目标是找到与该查询向量最近的相邻向量。因此,我们需要做的就是弄清楚如何测量查询向量与数据库中索引的所有现有向量之间的 “距离” 或 “相似性”,仅此而已。
距离和相似性
幸运的是,借助向量算法,测量两个向量之间的距离是一个很容易解决的问题。那么,让我们来看看现代向量搜索数据库(例如 Elasticsearch)支持的最流行的距离和相似度函数。警告,前方有数学知识!
L1 距离
两个向量 x 和 y 的 L1 距离(也称为曼哈顿距离)是通过将它们所有元素的成对绝对差相加来测量的。显然,距离 d 越小,两个向量越接近。公式非常简单,如下所示:
从视觉上看,L1 距离可以用下面的图 5 来表示:
![](https://i-blog.csdnimg.cn/direct/300f010d16ec428e826c3ee1fbe1eaa1.webp)
设两个向量 x 和 y,例如 x = (1, 2) 和 y = (4, 3),则两个向量的 L1 距离为 | 1 - 4 | + | 2 - 3 | = 4。
L2 距离
L2 距离,也称为欧几里得距离,两个向量 x 和 y 的测量方法是先将它们所有元素的两两差值的平方相加,然后对结果取平方根。它基本上是两点之间的最短路径(也称为斜边)。与 L1 类似,距离 d 越小,两个向量越接近:
L2 距离如下图6所示:
![](https://i-blog.csdnimg.cn/direct/2004947665f34d8e9824cfe4a1386c7d.webp)
让我们重复使用与 L1 距离相同的两个样本向量 x 和 y,现在我们可以计算 L2 距离为 。取 10 的平方根将得出 3.16。
Linf 距离
两个向量 x 和 y 的 Linf(代表 L 无穷大)距离,也称为切比雪夫距离或棋盘距离,简单定义为任意两个元素之间的最长距离或沿其中一个轴/维度测量的最长距离。公式非常简单,如下所示:
Linf 距离的表示如下图 7 所示:
![](https://i-blog.csdnimg.cn/direct/07ec425c033546dcac81995d12612427.webp)
同样,取相同的两个样本向量 x 和 y,我们可以计算 L 无穷距离为 max ( | 1 - 4 | , | 2 - 3 | ) = max (3, 1) = 3。
余弦相似度
与 L1、L2 和 Linf 不同,余弦相似度不测量两个向量 x 和 y 之间的距离,而是测量它们的相对角度,即它们是否都指向大致相同的方向。相似度 s 越高,两个向量越 “接近”。公式同样非常简单,如下所示:
两个向量之间余弦相似度的表示方法如下图 8 所示:
![](https://i-blog.csdnimg.cn/direct/594d9cf39cc1411ca53455562959c06a.webp)
此外,由于余弦值始终在 [-1, 1] 区间内,-1 表示相反的相似性(即两个向量之间呈 180° 角),0 表示不相关的相似性(即呈 90° 角),1 表示相同(即呈 0° 角),如下图 9 所示:
![](https://i-blog.csdnimg.cn/direct/7b2589b29100434a8c2f857d869ea4f7.webp)
再次,让我们重复使用相同的样本向量 x 和 y,并使用上述公式计算余弦相似度。首先,我们可以计算两个向量的点积,即 (1x4)+(2x3) = 10。然后,我们将两个向量的长度(也称为幅度)相乘:。最后,我们将点积除以乘积的长度 10 / 11.18034 = 0.894427(即 26° 角),该值非常接近 1,因此两个向量可以被认为非常相似。
点积相似度
余弦相似度的一个缺点是它只考虑两个向量之间的角度,而不考虑它们的大小(即长度),这意味着如果两个向量大致指向同一方向,但其中一个比另一个长得多,则两者仍将被视为相似。点积相似度(也称为标量或内积)通过同时考虑向量的角度和大小来改善这一点,从而提供更准确的相似度度量。
两个等效公式用于计算点积相似度。第一个与我们之前在余弦相似度的分子中看到的相同:
第二个公式只是将两个向量的长度乘以它们之间角度的余弦:
点积相似度如下图 10 所示:
![](https://i-blog.csdnimg.cn/direct/c6038724445e437ca501a31c9b88f34d.webp)
最后一次,我们取样本 x 和 y 向量,并使用第一个公式计算它们的点积相似度,就像我们之前对余弦相似度所做的那样,即 (1x4)+(2x3) = 10。
使用第二个公式,我们将两个向量的长度相乘: 并将其乘以两个向量之间 26° 角的余弦,得到 11.18034 x cos(26°) = 10。
值得注意的是,如果先对所有向量进行归一化(即,它们的长度为 1),那么点积相似度将与余弦相似度完全相同(因为 |x| |y| = 1),即两个向量之间角度的余弦。正如我们稍后会看到的,归一化向量是一种很好的做法,它可以让向量的大小变得无关紧要,从而使相似度只关注角度。它还可以加快索引和查询时的距离计算,这在处理数十亿个向量时可能是一个大问题。
快速回顾
哇,到目前为止我们已经了解了很多信息,所以让我们暂停一下,快速回顾一下我们所处的位置。我们已经了解到……
- …语义搜索基于深度学习神经网络模型,该模型擅长将非结构化数据转换为多维嵌入向量。
- …模型的每个维度都代表非结构化数据的特征或特性。
- …嵌入向量是一系列相似度值(每个维度一个),表示给定的非结构化数据与每个维度的相似程度。
- …两个向量越 “近”(即最近邻居),它们所代表的语义相似概念就越多。
- …距离函数(L1、L2、Linf)使我们能够测量两个向量的接近程度。
- …相似度函数(余弦和点积)使我们能够测量两个向量朝同一方向的距离。
现在,我们需要深入研究的最后一部分是向量搜索引擎本身。当查询进入时,查询首先被向量化,然后向量搜索引擎找到与该查询向量最近的相邻向量。测量查询向量与数据库中所有向量之间的距离或相似度的蛮力(brute-force)方法可以适用于小型数据集,但随着向量数量的增加,它很快就会失效。换句话说,我们如何索引数百万、数十亿甚至数万亿个向量,并在合理的时间内找到查询向量的最近邻居?这就是我们需要变得聪明并找出索引向量的最佳方法的地方,这样我们就可以尽快锁定最近的邻居,而不会过多地降低精度。
更多有关距离和相似性的知识,请详细阅读 “Elasticsearch:向量相似度技术和评分”
向量搜索算法和技术
多年来,许多不同的研究团队投入了大量精力来开发非常巧妙的向量搜索算法。在这里,我们将简要介绍主要的算法。根据用例,有些算法比其他算法更适合。
线性搜索
我们之前简要介绍了线性搜索或平面索引,当时我们提到了将查询向量与数据库中存在的所有向量进行比较的蛮力(brute-force)方法。虽然它可能在小型数据集上效果很好,但随着向量数量和维度的增加(O(n)复杂度),性能会迅速下降。
幸运的是,有一种更有效的方法,称为近似最近邻 (approximate nearest neighbor - ANN),其中嵌入向量之间的距离是预先计算的,并且相似的向量以保持它们靠近的方式存储和组织,例如使用集群、树、哈希或图形。这种方法被称为 “近似”,因为它们通常不能保证 100% 的准确性。最终目标是尽可能快速地缩小搜索范围,以便只关注最有可能包含相似向量的区域,或者降低向量的维数。
K 维树 - K - Dimensional trees
K 维树或 KD 树是二叉搜索树的泛化,它将点存储在 k 维空间中,并通过将搜索空间连续二等分为较小的左树和右树来工作,其中向量被索引。在搜索时,算法只需访问查询向量周围的几个树枝(图 11 中的红点)即可找到最近的邻居(图 11 中的绿点)。如果请求的邻居超过 k 个,则黄色区域会扩展,直到算法找到更多邻居。
![](https://i-blog.csdnimg.cn/direct/c596f252d8bf4498bd29cb7138f95fcb.webp)
Source: Kd-tree and Nearest neighbor (NN) search (2D case) | Alexey Abramov | Salzi | Blog
KD 树算法的最大优点是它允许我们快速地只关注一些局部的树分支,从而排除大部分向量的考虑。然而,这种算法的效率会随着维数的增加而降低,因为与低维空间相比,需要访问的分支要多得多。
倒排索引
倒排索引 (inverted file index - IVF) 方法也是一种空间分区(space-partitioning)算法,它将彼此接近的向量分配到它们的共享质心。在 2D 空间中,最好使用 Voronoi 图来可视化,如图 12 所示:
![](https://i-blog.csdnimg.cn/direct/437148cc14514b9da99a10ac77f7bba6.webp)
Source: AUTOINDEX Explained | Cloud | Zilliz Cloud Developer Hub
我们可以看到,上面的二维空间被划分为 20 个簇,每个簇的质心都用黑点表示。空间中的所有嵌入向量都被分配给质心最接近的簇。在搜索时,算法首先通过找到最接近查询向量的质心来确定要关注的簇,然后它可以简单地将焦点集中在该区域,如果需要,还可以将焦点集中在周围的区域,以找到最近的邻居。
在高维空间中使用时,此算法会遇到与 KD 树相同的问题。这称为维数灾难,当空间体积增加太多以至于所有数据看起来都很稀疏,并且获得更准确结果所需的数据量呈指数增长时,就会发生这种情况。当数据稀疏时,这些空间分区算法将数据组织成簇变得更加困难。幸运的是,还有其他算法和技术可以缓解这个问题,如下所述。
量化 - Quantization
量化是一种基于压缩的方法,它允许我们通过降低嵌入向量的精度来减少数据库的总大小。这可以通过使用标量量化 (scalar quantization - SQ) 来实现,即将浮点向量值转换为整数值。这不仅可以将数据库的大小减少 8 倍,还可以减少内存消耗并加快搜索时向量之间的距离计算。
另一种技术称为乘积量化 (product quantization - PQ),它首先将空间划分为低维子空间,然后使用聚类算法(类似于 k 均值 - k-means)将彼此接近的向量分组到每个子空间中。
请注意,量化不同于降维,降维是减少维数(dimensionality reduction),即向量只是变短了。
分层可导航小世界 (Hierarchical Navigable Small Worlds - HNSW)
如果仅从名称来看它看起来很复杂,别担心,其实它并不复杂!简而言之,分层可导航小世界是一种基于多层图形的算法,非常流行且高效。它被许多不同的向量数据库使用,包括 Apache Lucene。下图 13 中可以看到 HNSW 的概念表示。
![](https://i-blog.csdnimg.cn/direct/fcfc8cc4dd444aa0988634794269d842.webp)
Source: Similarity Search, Part 4: Hierarchical Navigable Small World (HNSW) | Towards Data Science
在顶层,我们可以看到一个由极少数向量组成的图,这些向量之间的链接最长,即相似度最低的连接向量图。我们越深入底层,找到的向量就越多,图就越密集,向量之间的距离也越来越近。在最低层,我们可以找到所有向量,其中最相似的向量彼此距离最近。
在搜索时,算法从顶层的任意入口点开始,找到最接近查询向量的向量(以灰色点表示)。然后,它移动到下一层并重复相同的过程,从上一层留下的相同向量开始,依此类推,一层接一层,直到到达最低层并找到查询向量的最近邻居。
局部敏感哈希 (Locality-sensitive hashing - LSH)
与迄今为止介绍的所有其他方法一样,局部敏感哈希力求大幅减少搜索空间,以提高检索速度。通过这种技术,嵌入向量被转换为哈希值,所有这些都通过保留相似性信息来实现,这样搜索空间最终就变成了一个可以查找的简单哈希表,而不是需要遍历的图或树。基于哈希的方法的主要优点是,包含任意(大)维数的向量可以映射到固定大小的哈希,这极大地加快了检索时间,而不会牺牲太多的精度。
通常,对数据进行哈希处理的方法有很多种,特别是对嵌入向量进行哈希处理的方法,但本文不会深入讨论每种方法的细节。传统的哈希方法通常会为看起来非常相似的数据产生非常不同的哈希。由于嵌入向量由浮点值组成,我们取两个在向量算法中被认为非常接近的浮点值样本(例如 0.73 和 0.74),并将它们通过几个常见的哈希函数运行。从下面的结果可以看出,常见的哈希函数显然无法保留输入之间的相似性。
Hashing function | 0.73 | 0.74 |
---|---|---|
MD5 | 1342129d04cd2924dd06cead4cf0a3ca | 0aec1b15371bd979cfa66b0a50ebecc5 |
SHA1 | 49d2c3e0e44bff838e1db571a121be5ea874e8d9 | a534e76482ade9d9fe4bff3035a7f31f2f363d77 |
SHA256 | 99d03fc3771fe6848d675339fc49eeb1cb8d99a12e6358173336b99a2ec530ea | 5ecbc825ba5c16856edfdaf0abc5c6c41d0d8a9c508e34188239521dc7645663 |
虽然传统的哈希方法试图最小化相似数据片段之间的哈希冲突,但局部敏感哈希的主要目标恰恰相反,即最大化哈希冲突,以便相似的数据以高概率落入同一个存储桶中。通过这样做,在多维空间中彼此接近的嵌入向量将被哈希为落入同一个存储桶中的固定大小值。由于 LSH 允许这些哈希向量保持其接近度,因此该技术对于数据聚类和最近邻搜索非常有用。
所有繁重的工作都发生在需要计算哈希值的索引时,而在搜索时,我们只需要对查询向量进行哈希处理,以查找包含最近嵌入向量的存储桶。一旦找到候选存储桶,通常会进行第二轮以确定与查询向量最近的相邻向量。
让我们总结一下
为了介绍向量搜索,我们必须在本文中介绍相当多的内容。在比较了词汇搜索和向量搜索之间的差异之后,我们了解了深度学习神经网络模型如何设法捕获非结构化数据的语义并将其含义转码为高维嵌入向量,即表示数据沿模型每个维度的相似性的浮点数序列。还值得注意的是,向量搜索和词汇搜索不是竞争关系,而是互补的信息检索技术(我们将在本系列的第三部分深入研究混合搜索时看到)。
之后,我们介绍了向量搜索的一个基本构建块,即距离(和相似度)函数,它使我们能够测量两个向量的接近度并评估它们所代表的概念的相似性。
最后,我们回顾了最流行的向量搜索算法和技术的不同类型,这些算法和技术可以基于树、图、集群或哈希,其目标是快速缩小多维空间的特定区域,以便找到最近的邻居,而不必像线性蛮力搜索那样访问整个空间。
请继续关注本系列的其他部分:
- 第 2 部分:如何在 Elasticsearch 中设置向量搜索
- 第 3 部分:使用 Elasticsearch 进行混合搜索
使用此自定进度的 Search AI 动手学习亲自尝试向量搜索。您现在可以开始免费云试用或在本地机器上试用 Elastic。
原文:A quick introduction to vector search - Elasticsearch Labs