Elasticsearch 中的位向量

了解位向量是什么、它们的实际意义以及如何在 Elasticsearch 中使用它们。

从 Elasticsearch 中向量搜索的开始,我们就支持 `float` 类型的数值。在 8.6 版本中,我们添加了对 `byte` 编码向量的支持。在 8.14 版本中,我们添加了自动量化到半字节值的功能。在 8.15 版本中,我们将添加对 `bit` 编码向量的支持。

但是位向量是什么,以及它们的实际意义是什么?顾名思义,`bit` 向量是指向量的每个维度都只用一个比特表示。当比较具有典型 `float` 值的向量的的数据大小时,`bit` 向量可以提供高达 32 倍的尺寸缩减。

每个比特都很重要

一些语义嵌入模型本身就输出 `bit` 向量,例如 Cohere。此外,其他一些类型的数据,例如 图像哈希,也直接使用 `bit` 向量。

然而,大多数语义嵌入模型输出 `float` 向量,并且不支持直接使用 `bit` 编码。

您可以自己简单地将向量二值化,因为计算很简单。对于向量的每个维度,检查该值是否 `> 中位数`。如果是,则为 `1` 比特,否则为 `0` 比特。

图 0:将 8 个 `float` 值转换为单个 `bit` 值,然后折叠成单个 `byte`,假设中位数为 `0`。

以下是一些用于二值化向量的简单 Python 代码

import numpy as np
# first determine which is greater than 0
bits = np.array(my_float_vector) > 0
# now transform to bits
bits = np.packbits(bits)
# now transform it to a hexidecimal string for indexing into Elasticsearch
hex_str = bits.tobytes().hex()

显然,这可能会丢失相当多的信息(双关语)。但是对于较大的向量或专门针对 `bit` 编码进行了优化的向量,节省的空间可能是值得的。

考虑 100 万个 1024 维浮点向量。每个向量的大小为 4KB,所有向量大约需要 4GB。使用二进制量化后,每个向量现在只有 128 字节,所有向量的总大小约为 128MB。当您考虑存储和内存成本时,这非常有吸引力。

现在,由于我们不再处于 `float` 领域,因此我们无法使用诸如 `cosineSimilarity` 或 `dotProduct` 之类的典型距离函数。相反,我们利用每个维度都是单个 `bit` 的特点,使用 汉明距离

`hamming` 距离非常简单,对于每个 `bit`,我们计算它与另一个向量中对应 `bit` 的 `xor`。然后我们对结果比特进行求和。

图 1:两个 `bit` 元素之间的汉明距离计算。

让我们回过头来思考我们那 100 万个 1024 维向量。除了节省空间之外,使用 128 字节的 `hamming` 距离与使用 1024 个浮点数的 `dotProduct` 相比,计算时间也大大减少了。

对于一些简单的基准测试(这并不详尽),我们在 Elasticsearch 中使用 `flat` 索引对 100 万个 1024 维向量进行了索引。

仅使用 2GB 的堆外内存,`bit` 向量大约需要 `40ms` 返回,而 `float` 则需要超过 `3000ms`。如果我们将堆外内存增加到 4GB,`bit` 向量仍然需要相同的时间(它们甚至在之前就已放入内存),而 `float` 向量则提高到 `200ms`。

因此,`hamming` 仍然比浮点 `dot-product` 快得多,并且需要的内存也少得多。

一些误差

`bit` 向量并不完美,很明显它是一种有损编码。问题不在于向量是否不会唯一。即使使用 `bit` 编码,386 维向量仍然有 23862^{386} 个可能的唯一向量。主要问题是距离冲突和编码引入的误差大小。

即使我们假设一个分布良好的 `bit` 编码,在收集大量向量时也可能存在许多距离冲突。直观地说,这是有道理的,因为我们的距离测量是将比特相加。例如,`00000001` 和 `10000000` 之间的距离与 `00000001` 和 `00000010` 之间的距离相同。一旦您需要收集超过 `维度` 个文档,就会发生冲突。实际上,它会比这更早发生。

为了说明这一点,这里有一项小型研究。这里的重点是找出需要收集多少个 `bit` 向量才能获得真正的最近的前 kk 个向量。

在第一个实验中,我们使用了来自 其维基百科数据集 的 100 万个 CohereV3 向量。我们随机采样(不放回)了 50 个查询向量,并使用它们来确定真实的 `dotProduct` 和 `hamming` 距离。

以下是性能“最佳”和“最差”的查询向量。其中质量是指检索正确的前 100100 个最近邻所需文档的数量(例如,更多表示更差)。

图 2:性能最佳的 CohereV3 查询向量及其距离,您可以看到距离实际上是如何很好地对齐的。

图 3:性能最差的 CohereV3 查询向量及其距离。在这里,较近的距离很好地对齐,但随着我们开始收集更远的向量,这种相关性就会减弱。

图 4:在所有 50 个查询中,获得真正的前 kk 个最近邻所需的向量的中位数。CohereV3 在这里表现出色,表明即使对于第 100th100^{th} 个最近邻,也只需要大约 10 倍的过采样。然而,从视觉上我们可以看到,所需的过采样呈指数级增长。

从这项小型研究中,我们可以看到 CohereV3 表现出色。中位数情况表明您可以通过大约 10 倍的过采样来实现类似的召回率。但是,在最坏的情况下,当收集超过 50 个最近文档时,它开始出现问题,需要远远超过 10 倍的过采样。根据查询和数据集的不同,您可能会遇到问题。

那么,当模型和数据集组合没有针对位向量进行优化时,二值化效果如何?我们使用了 e5-small-v2 并嵌入 Quora 数据集来测试这一点。随机选取 500k 个向量,然后从这些向量中随机采样 50 个查询向量。

图 5:性能最佳的 e5-small 查询向量及其距离。极其接近的距离相当好地对齐,但仍然不是特别好。

图 6:性能最差的 e5-small 查询向量及其距离。hammingdotProduct 距离实际上不相关。

图 7:获取真实 kk 个最近邻所需的向量数量的中位数。

最佳的 e5-small 向量表现中等,其 hamming 距离与 dotProduct 距离存在一定相关性。最坏情况则截然不同。距离实际上不相关。中位数表明,您需要大约 800 倍的过采样才能获得最近的 10 个向量,并且这种情况只会变得更糟。

简而言之,对于二进制量化效果良好的模型以及模型与数据集良好适配的情况,bit 量化是一个不错的选择。也就是说,请记住,随着您收集更多向量,所需的过采样可能会呈指数级增长。

对于模型无法很好地区分最近向量的域外数据集,或者对于根本没有针对二进制量化进行优化的模型,即使是少量最近向量,bit 向量也可能存在问题。

好的,但是如何使用 bit 向量呢?

在 Elasticsearch 中使用 bit 向量时,可以在映射中指定 bit 编码。例如:

{
  "mappings": {
    "properties": {
      "vector": {
        "type": "dense_vector",
        "element_type": "bit"
      }
    }
  }
}

图 8:在 Elasticsearch 中映射 bit 向量,允许使用 bit 编码。第一个文档将静态设置比特维度。

或者,如果您不想在 HNSW 索引 中进行索引,可以使用 flat 索引类型。

{
  "mappings": {
    "properties": {
      "vector": {
        "type": "dense_vector",
        "element_type": "bit",
        "index_options": {
          "type": "flat"
        }
      }
    }
  }
}

图 9:在 Elasticsearch 的 flat 索引类型中映射 bit 向量。

然后,要索引包含 bit 向量的文档,可以使用以下方法:

{
  "vector": "945fb26ec197caf96803725b6b05ba420f8bd3d19c2034391f910a3bcff98032733f75a47d1fdae134da91c71c97d9a3c9a253194bbe952dc768bd46e717fa91eafb43e0a232f8a983a6614b88ab2029b65b823f15dc32dbad5d8b4524ea896edba2f8508174f8b34dd66760187c2d38c635d42228c3ef991a0970e80bdd4aa7"
}

图 10:一个 1024 维的十六进制格式的 bit 向量。

现在,您可以利用 knn 查询

{
  "query": {
    "knn": {
      "field": "vector",
      "query_vector": "1a7bf8e8f943dcddfd8375bafef2ad630ab6bd3e8924f8e40a3755dd00ae6477e2c3bfd57ed771d8f0f33f4b2c9d443166b40ba443bd54a9c5783931dcb68c3c683034b065fe37e9c2ca15d74c44170920b18e3f485ddf1bed25cc083cf38d474992a89cba16d0c8e5d1f8a5dba099118654d863e09acb9cf2743fe0239a6a64"
    }
  }
}

图 11:使用 1024 维的十六进制向量查询 bit 向量。

就差一点了

感谢您坚持看完所有关于 2 位的玩笑。我们对 bit 向量为 Elasticsearch 8.15 带来的可能性感到非常兴奋。请在 Elasticsearch 8.15 发布后在 Elastic Cloud 中试用,或者现在就在 Elasticsearch Serverless 中试用!

Elasticsearch 充满了帮助您为您的用例构建最佳搜索解决方案的新功能。深入了解我们的 示例笔记本 以了解更多信息,开始 免费云试用,或立即在您的 本地机器 上试用 Elastic。

准备好构建最先进的搜索体验了吗?

足够高级的搜索并非一蹴而就。Elasticsearch 由数据科学家、机器学习运维工程师、软件工程师以及许多其他对搜索充满热情的人员提供支持,他们与您一样热衷于搜索。让我们联系起来,共同努力构建神奇的搜索体验,让您获得想要的结果。

亲自试一试