在设计向量搜索体验时,可供选择的方案众多,可能让人感到不知所措。最初管理少量向量相对简单,但随着应用规模的扩大,这很快会成为瓶颈。
在这一系列博客文章中,我们将探讨在各种数据集和用例中使用 Elasticsearch 运行大规模向量搜索的成本和性能。
我们首先使用最大的公开可用向量数据集之一:Cohere/msmarco-v2-embed-english-v3。该数据集包含从网页中提取的 1.38 亿条段落(来自 MSMARCO-passage-v2 collection),并使用 Cohere 最新的 embed-english-v3 模型 嵌入到 1024 维中。
在此实验中,我们定义了一个 可重复的轨道,您可以在自己的 Elastic 部署中运行,以帮助您基准测试自己的高保真密集向量搜索体验。
该轨道针对实时搜索用例进行了优化,单次搜索请求的延迟必须低于 100ms。我们使用 Rally,我们的开源工具,来跨 Elasticsearch 版本进行基准测试。
在这篇文章中,我们使用了 默认的浮点向量自动量化。这可以在不影响检索质量的情况下,将运行向量搜索的 RAM 成本减少 75%。我们还提供了有关具有数十亿向量的索引在合并和量化时的影响的见解。
我们希望这个轨道能成为一个有用的基线,特别是当您没有特定于您的用例的向量时。
选择适合您需求的模型超出了这篇博客文章的范围,但在接下来的部分中,我们将讨论不同的技术,以压缩您的向量的原始大小。
通过在较早的维度中存储最重要的信息,像 Matryoshka 嵌入 这样的新方法可以在保持良好搜索准确性的同时缩小维度。使用这种技术,某些模型的尺寸可以减半,并且仍能在 MTEB 检索基准上保持 90% 的 NDCG@10。但是,并非所有模型都兼容。如果您的选择模型未经过 Matryoshka 减少训练,或者其维度已经达到最小值,则必须直接在向量数据库中管理维度。
幸运的是,mixedbread 或 OpenAI 的最新模型都内置了 MRL 支持。
在此实验中,我们选择聚焦在维度固定的用例(1024 维度),其他模型的维度变化将是另一个时间的话题。
模型开发人员现在通常提供各种权衡的模型,以应对高维向量的成本。除了关注维度减少之外,这些模型还通过调整每个维度的精度来实现压缩。
通常,嵌入模型会训练生成使用 32 位浮点的维度。但是,训练它们生成精度降低的维度有助于最小化误差。开发人员通常发布优化了已知精度的模型,这些精度直接与编程语言中的本机类型对齐。
例如,int8 表示一个范围从 -127 到 127 的有符号整数,而 uint8 表示一个范围从 0 到 255 的无符号整数。最简单的形式是二进制,表示一个位(0 或 1),对应每个维度的最小单位。
在训练过程中实施量化可以微调模型权重,以最小化压缩对检索性能的影响。然而,详细讨论训练这些模型的细节超出了这篇博客的范围。
在接下来的部分中,我们将介绍一种在选定模型缺乏此功能时应用自动量化的方法。
在模型缺乏量化感知嵌入的情况下,Elasticsearch 采用自适应量化方案,默认将浮点量化为 int8。
这种通用的 int8 量化通常会产生微不足道的性能损失。其优点在于能够适应数据漂移。
它使用动态方案,可以不时重新计算量化边界,以适应数据的任何变化。
使用 1.38 亿文档和 1024 维向量,存储 MSMARCO-v2 数据集的原始浮点向量的大小超过 520GB。使用蛮力搜索整个数据集在单个节点上需要几个小时。
幸运的是,Elasticsearch 提供了一种数据结构 HNSW(分层可导航小世界图),用于加速最近邻搜索。这种结构允许快速的近似最近邻搜索,但需要每个向量都在内存中。
从磁盘加载这些向量的成本高昂,因此我们必须确保系统有足够的内存来将它们全部加载到内存中。
每个 1024 维的向量需要 4KB 的内存。
此外,我们还需要考虑加载 HNSW 图到内存中所需的内存。默认设置下,图中每个节点有 32 个邻居,每个向量需要额外的 128 字节(每个邻居 4 字节)的内存来存储图,相当于存储向量维度内存成本的约 3%。
确保有足够的内存来满足这些要求对于最佳性能至关重要。
在 Elastic Cloud 上,我们的向量搜索优化配置为 JVM(Java 虚拟机)保留了节点总内存的 25%,每个数据节点剩余 75% 的内存用于系统页面缓存,其中加载向量。对于具有 60GB 内存的节点,这相当于为向量分配了 45GB 的页面缓存。向量搜索优化配置在所有云解决方案提供商(CSP) AWS,Azure 和 GCP 上均可用。
要满足 520GB 的内存需求,我们需要 12 个节点,每个节点具有 60GB 内存,总共 720GB。
在撰写本文时,这一配置可以在我们的 Cloud 环境中部署,总成本为每小时 $14.44(请注意,价格会因 Azure 和 GCP 环境而异):
通过将自动量化为字节,我们可以将内存需求减少到 130GB,仅为原始大小的四分之一。
应用相同的 25/75 内存分配规则,我们可以在 Elastic Cloud 上分配总共 180GB 的内存。
在撰写本文时,这一优化配置在 Elastic Cloud 上的总成本为每小时 $3.60(请注意,价格会因 Azure 和 GCP 环境而异):
在 Elastic Cloud 上开始 免费试用,只需选择新的向量搜索优化配置即可开始。
在本文中,我们将使用创建的基准测试来探讨这种成本效益高的量化。通过这样做,我们旨在展示如何在保持高搜索准确性和效率的同时,实现显著的成本节约。
msmarco-v2-vector rally 轨道定义了将使用的默认映射(https://github.com/elastic/rally-tracks/blob/master/msmarco-v2-vector/index-vectors-only-mapping
.json)。
它包含一个 1024 维的密集向量字段,使用自动 int8 量化索引,还有一个类型为 keyword 的 doc_id 字段,用于唯一标识每个段落。
在这次实验中,我们测试了两种配置:
如前所述,Elasticsearch 中的每个分片由段组成。段是数据的不可变划分,包含直接查找和搜索数据所需的结构。
文档索引涉及在内存中创建段,并定期将其刷新到磁盘。
为了管理段的数量,后台进程会合并段,以保持总数量在一定范围内。
这种合并策略对于向量搜索至关重要,因为 HNSW 图在每个段内是独立的。每个密集向量字段的搜索涉及在每个段中查找最近邻,因此总成本取决于段的数量。
默认情况下,Elasticsearch 会合并大约相同大小的段,遵循受限的策略,由每个级别允许的段数量控制。
该设置的默认值为 10,意味着每个级别不应超过 10 个大小相似的段。例如,如果第一级包含 50MB 的段,第二级将包含 500MB 的段,第三级 5GB,以此类推。
激进合并 配置将默认设置调整得更为积极:
这种配置下,我们期望搜索速度更快,但索引速度会较慢。
在这个实验中,我们对 HNSW 图的 m, ef_construction, 和 confidence_interval 选项 保持默认设置。在第一部分中,我们选择聚焦在变化合并和搜索参数。
在运行基准测试时,必须将负载驱动器(负责发送文档和查询)与评估系统(Elasticsearch 部署)分离。加载和查询数亿个密集向量需要额外资源,如果一起运行会干扰评估系统的搜索和索引能力。
为了最小化系统和负载驱动器之间的延迟,建议在与 Elastic 部署相同的云提供商区域运行负载驱动器,最好在相同的可用区。
在这个基准测试中,我们在 AWS 上配置了一个 im4gn.4xlarge 节点,具有 16 个 CPU、64GB 内存和 7.5TB 磁盘,与 Elastic 部署位于同一区域。这个节点负责向 Elasticsearch 发送查询和文档。通过这样隔离负载驱动器,我们确保在不受额外资源需求干扰的情况下准确测量 Elasticsearch 的性能。
我们使用以下配置运行了整个基准测试:
{
"track.params": {
"mapping_type": "vectors-only",
"vector_index_type": "int8_hnsw",
"number_of_shards": 4,
"initial_indexing_bulk_indexing_clients": 12,
"standalone_search_clients": 8
}
}
initial_indexing_bulk_indexing_clients 值为 12 表示我们将使用 12 个客户端从负载驱动器中摄取数据。Elasticsearch 数据节点总共有 23.9 个 vCPU,使用更多客户端发送数据可以增加并行性,使我们能够充分利用部署中的所有可用资源。
对于搜索操作,standalone_search_clients 和 parallel_indexing_search_clients 值为 8,意味着我们将使用 8 个客户端从负载驱动器并行查询 Elasticsearch。客户端的最佳数量取决于多个因素;在此实验中,我们选择了最大化 Elasticsearch 数据节点的 CPU 使用率的客户端数量。
为了比较结果,我们在同一部署上运行了第二次基准测试,但这次我们将参数 aggressive_merge 设置为 true。这有效地改变了合并策略,使其更加激进,使我们能够评估这种配置对搜索性能和索引速度的影响。
在 Rally 中,挑战是通过一系列计划的操作来执行并报告结果。
每个操作负责对集群执行一个操作并报告结果。
对于我们的新轨道,我们将第一个操作定义为 initial-documents-indexing,涉及批量索引整个语料库。接下来是 wait-until-merges-finish-after-index,等待批量加载过程结束时的后台合并完成。
此操作不使用强制合并;它只是等待自然合并过程完成,然后再开始搜索评估。
在此,我们报告这些 操作的结果,它们对应于在 Elasticsearch 中初始加载数据集的情况。搜索操作将在下一部分报告。
使用 Elasticsearch 8.14.0,138M 向量的初始索引不到 5 小时,平均速度为每秒 8,000 个文档。
请注意,瓶颈通常是嵌入的生成,这里没有报告。
等待合并完成仅增加了 2 分钟:
总索引性能(8.14.0 默认 int8 HNSW 配置)
相比之下,在 Elasticsearch 8.13.4 上进行的相同实验需要将近 6 小时进行摄取,并且需要额外的 2 小时等待合并:
总索引性能(8.13.4 默认 int8 HNSW 配置)
Elasticsearch 8.14.0 是第一个利用向量搜索的本地代码 的版本。合并过程中使用本地 Elasticsearch 编解码器加速 int8 向量之间的相似性,显著减少了整体索引时间。我们目前正在探索进一步的优化,利用这个自定义编解码器进行搜索,因此请继续关注更新!
激进合并运行在不到 6 小时内完成,平均每秒 7,000 个文档。然而,等待合并完成花费了近一个小时。与默认合并策略相比,速度下降了 40%:
总索引性能(8.14.0 激进合并 int8 HNSW 配置)
激进合并配置执行了更多的工作,可以通过下图总结。
激进合并配置合并了 2.7 倍的文档,以创建更大更少的段。默认合并配置报告从索引的 1.38 亿文档中合并了将近 3 亿文档。这意味着每个文档平均合并了 2.2 次。
每个节点的合并文档总数(8.14.0 默认 int8 HNSW 配置)
每个节点的合并文档总数(8.14.0 激进合并 int8 HNSW 配置)
在下一部分中,我们将分析这些配置对搜索性能的影响。
对于搜索操作,我们的目标是捕捉两个关键指标:最大查询吞吐量和近似最近邻搜索的准确度。
为此,standalone-search-knn-* 操作评估了使用各种近似搜索参数组合的最大搜索吞吐量。此操作涉及使用 parallel_indexing_search_clients 并行尽快执行 10,000 个来自训练集的查询。这些操作旨在利用节点上的所有可用 CPU,并在所有索引和合并任务完成后进行。
为了评估每种组合的准确度,knn-recall-* 操作计算了相关的召回率和 归一化折现累积增益 (nDCG)。nDCG 是从 msmarco-passage-v2/trec-dl-2022/judged 中
发布的 76 个查询中计算的,使用 386,000 个 qrels 注释。所有 nDCG 值的范围从 0.0 到 1.0,1.0 表示完美排名。
由于数据集的大小,生成地面实况结果以计算召回率的成本非常高。因此,我们将召回报告限制在测试集中的 76 个查询,这些查询的地面实况结果是使用蛮力方法离线计算的。
搜索配置包含三个参数:
使用自动量化,重新评分略多于 k 的向量可以显著提高召回率。
操作根据这三个参数命名。例如,knn-10-100-20 意味着 k=10, num_candidates=100, 和 num_rescore=20。如果省略最后一个数字,如 knn-10-100,则 num_rescore 默认为 0。
有关如何创建搜索请求的更多信息,请参阅 track.py 文件。
下图显示了在不同召回级别下预期的每秒查询数 (QPS)。例如,默认配置(橙色系列)可以在 0.922 的预期召回下达到 50 QPS。
召回率与每秒查询数(Elasticsearch 8.14.0)
激进合并配置在相同召回率下效率提高了 2 到 3 倍。这是预期的,因为搜索在更大且更少的段上进行,如前一部分所示。
下表显示了默认配置的完整结果:
每秒查询数、延迟(毫秒)、召回率和不同参数组合的 NDCG@10(8.14 默认 int8 HNSW 配置)
%best 列表示该配置的实际 NDCG@10 与使用蛮力离线计算的地面实况最近邻的最佳可能 NDCG@10 之间的差异。
例如,我们观察到 knn-10-20-20 配置尽管召回率@10 为 67.4%,但在该数据集上达到了最佳可能 NDCG 的 90%。请注意,这只是一个点结果,结果可能因其他模型和/或数据集而异。
下表显示了激进合并配置的完整结果:
每秒查询数、延迟(毫秒)、召回率和不同参数组合的 NDCG@10(8.14 激进合并 int8 HNSW 配置)
使用 knn-10-500-20 搜索配置,激进合并设置可以在 150 QPS 下达到超过 90% 的召回率。
在本文中,我们描述了一个新的 Rally 轨道,旨在基准测试 Elasticsearch 上的大规模向量搜索。我们探讨了运行近似最近邻搜索时涉及的各种权衡,并展示了在 Elasticsearch 8.14 中,我们如何在现实的大规模向量搜索工作负载中将成本降低 75%,同时将索引速度提高 50%。
我们正在不断努力优化和寻找增强向量搜索能力的机会。敬请关注系列的下一部分,我们将深入探讨向量搜索用例的成本和效率,特别是研究 int4 和二进制压缩技术的潜力。
通过不断改进我们的方法并发布用于测试大规模性能的工具,我们旨在突破 Elasticsearch 的可能性边界,确保其仍然是大规模向量搜索的强大且成本效益高的解决方案。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有