Elasticsearch 中的位向量
2024年7月23日 | by mebius
作者:来自 ElasticBenjamin Trent
从 Elasticsearch 中的向量搜索开始,我们就支持浮点值(float)。在 8.6 版中,我们添加了对字节(byte)编码向量的支持。在 8.14 版中,我们添加了自动量化到半字节值(half-byte)的功能。在 8.15 版中,我们添加了对位(bit)编码向量的支持。
但是,什么是位向量及其实际含义?正如字面上所言,位向量是向量的每个维度都是一个位(bit)。当将向量的数据大小与典型的浮点值进行比较时,位向量的大小减少了 32 倍之多。
每一位都很重要
一些语义嵌入模型本身输出位向量,例如 Cohere。此外,一些其他类型的数据(例如 image hashing)直接利用位向量。
但是,大多数语义嵌入模型输出浮点向量,并且不支持直接位编码。
你可以自己简单地将向量二值化,因为数学很简单。对于每个向量维度,检查值是否 > 中位数。如果是,则为 1 位,否则为 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()
显然,这会丢失相当多的信息。但对于较大的向量或专门优化以与位编码配合良好的向量,节省的空间是值得的。
考虑 100 万个 1024 维浮点向量。每个向量大小为 4KB,所有向量将需要大约 4GB。使用二进制量化,每个向量现在只有 128 字节,所有向量总共只有 128MB 左右。当你考虑存储和内存成本时,这非常有吸引力。
现在,由于我们不再处于 float 领域,我们不能使用典型的距离函数,如 cosineSimilarity 或 dotProduct。相反,我们利用汉明距离利用每个维度都是单个位的优势。
汉明距离相当简单,对于每个单独的位,我们计算与另一个向量中相应位的异或。然后我们将结果位相加。
让我们回想一下我们的 100 万个 1024 维向量。除了节省空间之外,使用 128 字节上的汉明(hamming)距离与使用 1024 个浮点上的 dotProduct 相比,计算时间显著减少。
对于一些简单的基准测试(这并不详尽),我们在 Elasticsearch 中使用 flat 索引对 100 万个 1024 维向量进行了索引。
仅使用 2GB 的堆外空间,位向量大约只需要 40ms 就能返回,但浮点向量需要超过 3000ms。如果我们将堆外空间增加到 4GB,位向量仍然需要相同的时间(它们甚至更早地装入内存),而tgcode浮点向量则缩短到 200ms。
因此,汉明距离仍然比浮点点积快得多,并且需要的内存要少得多。
一点错误
位向量并不完美,很明显这是一种有损编码。担心的不是向量不唯一。即使使用位编码,386 维向量仍然有个可能的唯一向量。主要担心的是距离冲突和编码引入的错误大小。
即使我们假设一个分布良好的位编码,在收集大量向量时也可能会发生许多距离冲突。直观地说,这是有道理的,因为我们的距离测量是对位求和。例如,00000001 和 10000000 与 00000tgcode001 和 00000010 之间的距离相同。一旦你需要收集超过维度的文档,就会发生冲突。实际上,它会比这发生得更快。
为了说明这一点,这里有一项小型研究。这里的重点是找出需要收集多少位向量才能获得真正的最近前 个向量。
对于第一个实验,我们使用了来自其 Wikipedia 数据集的 100 万个 CohereV3 向量。我们随机抽样(不重复)了 50 个查询向量,并使用它们来确定真正的 dotProduct 和hamming 距离。
以下是表现 “最佳” 和 “最差” 的查询向量。质量是检索正确的 100 个最近邻居所需的文档数量(例如,越多越差)。
从这项小型研究来看,CohereV3 表现非常出色。中位数情况显示,你可以过采样约 10 倍以实现类似的召回率。但是,在最坏的情况下,当收集超过 50 个最近的文档时,它开始出现问题,需要超过 10 倍的过采样。根据查询和数据集,你可能会遇到问题。
那么,当模型和数据集组合未针对位向量进行优化时,二值化效果如何?我们使用 e5-small-v2 并嵌入 quora 数据集来测试这一点。随机取 500k 个向量,然后从这些向量中随机抽取 50 个查询向量。
最佳的 e5-small 向量表现中等,其汉明距离与点积呈半相关。最坏的情况则完全不同。距离实际上是不相关的。中值表明,你需要过采样大约 800 倍才能获得最接近的 10 个向量,而且情况只会越来越糟。
简而言之,对于二进制量化效果良好且模型很好地适应数据集的模型,位量化是一个很好的选择。也就是说,请记住,随着你收集更多向量,所需的过采样可能会呈指数级增长。
对于域外数据集,其中最近的向量对于模型没有很好区分,或者对于根本没有针对二进制量化进行优化的模型,即使只有少量的最近向量,位向量也可能存在问题。
好的,但是我该如何使用它?
在 Elasticsearch 中使用位向量时,你可以在映射中指定位编码。例如:
{
"mappings": {
"properties": {
"vector": {
"type": "dense_vector",
"element_type": "bit"
}
}
}
}
图 8:在 Elasticsearch 中映射位向量,允许进行位编码。第一个文档将静态设置位维度。
或者如果你不想在 HNSW 索引中编制索引,那么你可以使用 flat 索引类型。
{
"mappings": {
"properties": {
"vector": {
"type": "dense_vector",
"element_type": "bit",
"index_options": {
"type": "flat"
}
}
}
}
}
图 9:在平面索引类型中映射 Elasticsearch 中的位向量。
然后,要使用位向量索引文档,可以使用以下命令:
{
"vector": "945fb26ec197caf96803725b6b05ba420f8bd3d19c2034391f910a3bcff98032733f75a47d1fdae134da91c71c97d9a3c9a253194bbe952dc768bd46e717fa91eafb43e0a232f8a983a6614b88ab2029b65b823f15dc32dbad5d8b4524ea896edba2f8508174f8b34dd66760187c2d38c635d42228c3ef991a0970e80bdd4aa7"
}
图 10:十六进制格式的 1024 维位向量。
现在你可以使用 knn 查询:
{
"query": {
"knn": {
"field": "vector",
"query_vector": "1a7bf8e8f943dcddfd8375bafef2ad630ab6bd3e8924f8e40a3755dd00ae6477e2c3bfd57ed771d8f0f33f4b2c9d443166b40ba443bd54a9c5783931dcb68c3c683034b065fe37e9c2ca15d74c44170920b18e3f485ddf1bed25cc083cf38d474992a89cba16d0c8e5d1f8a5dba099118654d863e09acb9cf2743fe0239a6a64"
}
}
}
图 11:使用 1024 维十六进制向量查询位向量。
还有一点
感谢你看完所有这些 2-bit 笑话。我们对位向量在 8.15 中为 Elasticsearch 带来的可能性感到非常兴奋。请在 8.15 发布后在 Elastic Cloud 中试用它,或者立即在 Elasticsearch Serverless 中试用它!
准备好自己尝试一下了吗?开始免费试用。
Elasticsearch 集成了 LangChain、Cohere 等工具。加入我们的高级语义搜索网络研讨会,构建你的下一个 GenAI 应用程序!
文章tgcode来源于互联网:Elasticsearch 中的位向量
相关推荐: 城市之旅:使用 LLM 和 Elasticsearch 简化地理空间搜索(二)
我们在之前的文章 “城市之旅:使用 LLM 和 Elasticsearch 简化地理空间搜索(一)”,在今天的练习中,我将使用本地部署来做那里面的 Jupyter notebook。 安装 Elasticsearch 及 Kibana 如果你还没有安装好自己的…