Elasticsearch:简化 KNN 搜索

作者:来自 Elastic Panagiotis Bailis

在这篇博客文章中,我们将深入探讨我们为了使 KNN 搜索的入门体验变得更加简单而做出的努力!

向量搜索

向量搜索通过在 Elasticsearch 中引入一种新的专有的 KNN 搜索类型,已经可以使用一段时间了,而我们还在 8.12.0 版本中引入了 KNN 作为一种查询方式(关于这个话题,我们最近发布了一篇精彩的博客文章!)。

尽管每种方法的执行流程和应用有所不同,但进行基本 KNN 检索的语法大体相似。因此,典型的 KNN 搜索请求看起来是这样的:

GET products/_search
{"knn": {"field": "my_vector","query_vector": [1, 2, 3],"k": 5,"num_candidates": 10}
}

前几个参数相当直观:我们指定数据存储的位置(field)和我们想要比较的对象(query_vector)。

另一方面,k 和 num_candidates 参数稍微有些晦涩,需要一定的理解才能进行精细调整。这些参数特定于我们使用的算法和数据结构,即 HNSW,并主要用于控制我们希望进行的图探索量。

Elasticsearch 文档是关于搜索的所有事务的绝佳资源,因此我们可以查看 KNN 部分:

k:要返回的最近邻居的数量作为顶部结果。该值必须小于 num_candidates。

num_candidates:每个分片考虑的最近邻居候选数。需要大于 k,或者如果 k 被省略,则大于 size,并且不能超过 10,000。Elasticsearch 从每个分片收集 num_candidates 结果,然后将它们合并以找到前 k 个结果。增加 num_candidates 往往会提高最终 k 结果的准确性。

然而,当你第一次遇到这样的设置时,这些值应该是多少并不明显,正确配置它们可能会成为一个挑战。这些值越大,我们将探索的向量就越多,但这将带来性能成本。我们再次遇到了准确性与性能之间永恒的权衡。

为了使 KNN 搜索更简单、更直观,我们决定使这些参数成为可选的,这样你只需要提供你想搜索的位置和内容,如果真的需要,还可以调整这些参数。虽然看似是一个较小的改变,但它使事情变得更加清晰!因此,上面的查询现在可以简单地重写为:

GET products/_search
{"knn": {"field": "my_vector","query_vector": [1, 2, 3]}
}

使 k 和 num_candidates 成为可选项

我们想要将 k 和 num_candidates 设为可选项。太好了!但是默认值应该设置为多少呢?

此时有两个选项。选择看起来不错的选项,发布并希望一切顺利,或者做出艰苦的努力,进行广泛的评估以让数据驱动我们的决策。在 Elastic,我们喜欢这样的挑战,并希望确保任何决定都是有充分理由的!

刚刚我们提到,KNN 搜索中的 k 是每个分片返回的结果数,因此一个明显的默认值是使用 size。因此,每个分片将返回 size 个结果,然后我们将它们合并并排序以找到全局前 size 个结果。这也非常适用于 KNN 查询,其中根本没有 k 参数,而是基于请求的 size 进行操作(请记住,KNN 查询的行为就像任何其他查询,如 term、prefix 等)。因此,size 看起来是一个合理的默认值,可以涵盖大多数用例(或者至少在入门体验期间足够好!)。

另一方面,num_candidates 则大不相同。这个参数特定于 HNSW 算法,控制着我们将考虑的最近邻队列的大小(对于感兴趣的人来说:这相当于原始论文中的 ef 参数)。

在这里,我们可以采取多种方法:

  • 我们可以考虑每个图的大小,并制定一个函数,用于计算适当的 num_candidates,以匹配 N 个索引向量。
  • 我们可以查看底层数据分布,并尝试估算所需的探索(也许还可以考虑 HNSW 的入口点)。
  • 我们可以假设 num_candidates 与索引数据无直接关系,而与搜索请求相关,并确保我们将进行必要的探索,以提供足够好的结果。

作为一个起点,并且为了保持简单,我们研究了将 num_candidates 值设置为与 k(或 size)相关的值。因此,你实际想要检索的结果越多,我们就会对每个图执行更高的探索,以确保我们摆脱局部最小值。我们主要关注的候选者如下:

  • num_candidates = k
  • num_candidates = 1.5 * k
  • num_candidates = 2 * k
  • num_candidates = 3 * k
  • num_candidates = 4 * k
  • num_candidates = Math.max(100, k)

值得注意的是,最初考虑了更多的替代方案,但更高的值提供的好处很小,因此在博客的剩余部分,我们将主要关注上述内容。

现在我们有了一组 num_candidates 候选者(无意中的双关!),接下来我们将关注 k 参数。我们选择同时考虑标准搜索以及非常大的 k 值(以查看我们所做探索的实际影响)。因此,我们决定更加关注的值是:

  • k = 10(考虑到指定了 no size 的请求)
  • k = 20
  • k = 50
  • k = 100
  • k = 500
  • k = 1000

数据

鉴于没有放之四海而皆准的解决方案,我们希望测试具有不同属性的各种数据集。因此,这些数据集具有不同的总向量数、维度,并且来自不同的模型,因此具有不同的数据分布。同时我们也可以使用 rally,这是一个很棒的基准测试工具(https://github.com/elastic/rally),它已经支持运行一系列查询并为多个向量数据集提取指标。

在 rally 上运行基准测试就像运行以下命令一样简单:

pip3 install esrally && esrally race --track=dense-vector

为此,我们稍微修改了轨迹(即 rally 的测试场景),以包括额外的度量配置,添加了一些新的配置,最终得到了以下一组轨迹:

  • dense-vector (2M docs, 96dims): https://github.com/elastic/rally-tracks/tree/master/dense_vector
  • so-vector (2M docs, 768 dims): https://github.com/elastic/rally-tracks/tree/master/so_vector
  • cohere-vector (3M docs, 768 dims): rally-tracks/cohere_vector at master · elastic/rally-tracks · GitHub
  • openai-vector (2.5M docs, 1536dims): https://github.com/elastic/rally-tracks/tree/master/openai_vector
  • Glove 200d (1.2M, 200dims) new track created based on GitHub - erikbern/ann-benchmarks: Benchmarks of approximate nearest neighbor libraries in Python repo

值得注意的是,对于前几个数据集,我们还希望考虑到是否只有一个段或多个段,因此我们包含了每种情况的两个变体:

  • 一个变体,我们执行 force_merge 并获得单个段,
  • 一个变体,我们依赖底层的 MergeScheduler 来执行其工作,以得到合适数量的段。

指标

针对上述每个轨迹,我们计算了标准的召回率和精确度指标、延迟,以及我们在图上实际进行的探索,通过报告我们访问的节点。前面几个指标是针对真实最近邻进行评估的,因为在我们的情况下,这是黄金标准数据集(请记住,我们评估的是近似搜索的好坏,而不是向量本身的质量)。nodes_visited 属性最近已添加到 knn 的配置文件输出中(https://github.com/elastic/elasticsearch/pull/102032),因此,通过对轨迹定义进行一些微小的更改以提取所有所需的指标,我们应该可以进行评估了!

开始动手操作

现在我们知道我们想要测试什么,要使用哪些数据集,以及如何评估结果,是时候真正运行基准测试了!

为了获得标准化的环境,对于每个测试,我们使用了一个干净的 n2-standard-8(8 vCPU,4 核心,32 GB 内存)云节点。Elasticsearch 配置,以及必要的映射和其他所有内容,都是通过 rally 进行配置和部署的,因此对于所有类似的测试都是一致的。

上述每个数据集都执行了多次,收集了所有候选集定义的可用指标,确保没有结果是偶然的。

结果

每个指定数据集和参数组合的召回率 - 延迟图可以在下面找到(越高且越靠左越好):

将范围缩小到 dense_vector 和 openai_vector 轨迹,我们有关于 latency@50th 和召回率的绝对值数据如下:

Dense Vector latency@50

Dense Vector recall

OpenAI latency@50

OpenAI Recall

类似地,每个场景的 HNSW 图节点访问的第 99 百分位数如下(数值越小越好):

Dense Vector Multiple Segments
标题

Cohere Vector

OpenAI Vector
Dense Vector

   

Dense Vector Multiple Segments

Glove Vector

少即是多*

*嗯,并非在所有情况下都是如此,但嘿 :) 从结果中可以看出两点:

  • 单个段与多个段明显影响召回率和延迟指标呈反比关系。段数较少会降低延迟(因为我们要遍历的图较少,即运行的搜索较少),这很好,但它也以相反的方式影响了召回率,因为由于 num_candidates 列表较少,可能会漏掉一些好的候选项。
  • 即使探索量很少,我们在几乎所有情况下都可以达到足够好的召回率,这很棒!

我们一直致力于改进多段搜索(这篇文章中有一个很好的例子),因此我们预计这种权衡在未来将不再是一个问题(这里报告的数字不包括这些改进)。

综合考虑所有因素,我们讨论的两个主要选项如下:

  • num_candidates = 1.5 * k - 这几乎在所有情况下都能达到足够好的召回率,并且延迟得分非常好。
  • num_candidates = Math.max(100, k) - 这在特别是较低的 k 值时达到了稍微更好的召回率,但权衡之处在于增加了图探索和延迟。

经过认真考虑和(漫长的!)讨论,我们决定采用前一种默认设置,即设置 num_candidates = 1.5 * k。我们需要进行的探索要少得多,而且召回率始终在 75% 以上,大多数情况下在 90% 以上,这应该能够提供足够好的入门体验。

结论

我们在 Elastic 处理 knn 搜索的方式在不断进化,我们持续引入新功能和改进,因此这些参数和整体评估很可能很快就会过时!我们总是在密切关注,一旦发生变化,我们将确保跟进并相应调整我们的配置!

需要记住的一件重要事情是,这些值仅作为简化入门体验和非常通用用例的合理默认值。用户可以轻松地在自己的数据集上进行实验,并根据自己的需求进行相应调整(例如,在某些情况下,召回率可能比延迟更重要)。

快乐调优 😃

准备在你的应用中构建 RAG 吗?想尝试使用向量数据库的不同 LLM 吗? 查看 Github 上的 LangChain、Cohere 等示例笔记本,并加入即将开始的 Elasticsearch 工程师培训!

原文:Simplifying knn search — Elastic Search Labs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/825694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【粉丝福利 | 第5期】教你快速入门三大层次学习企业架构框架TOGAF

⛳️ 写在前面参与规则!!! ✅参与方式:关注博主、点赞、收藏、评论,任意评论(每人最多评论三次) ⛳️本次送书1~4本【取决于阅读量,阅读量越多,送的越多】 三大层次学习…

如何取消电脑屏幕保护?学会这3招,操作无难度!

“我之前在电脑上设置了电脑屏幕保护,现在想将它取消掉,大家有什么比较好的方法可以分享一下吗?” 在日常使用电脑的过程中,屏幕保护程序是一个常见的功能。它可以在电脑空闲一段时间后自动启动,以动画或图片的形式展示…

玄子Share-网络布线与数制转换

玄子Share-网络布线与数制转换 网络传输介质 信号概述 什么是信号 信息数据信号 信号的分类 模拟信号数字信号 信号在传输过程中产生的失真 噪声距离速度 数字信号的优势 抗干扰能力强传输距离远并能保证质量 双绞线 双绞线 总共8根双绞线,两两绞合在一起常用…

C语言—常用字符串函数剖析

字符串函数 cplusplus.com/reference/cstring/ 更多没有总结到的函数大家可以自行查阅 这篇文章只是把最需要知道的函数做一个总结 strlen size_t strlen ( const char * str );字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的…

软考 - 系统架构设计师 - 架构风格例题

问题一: 什么是软件架构风格? 软件架构风格指特定软件系统组织方式的惯用模式。组织方式描述了系统的组成构件和这些构件的组织方式。惯用模式反映了众多系统所共有的结构和语义。 集成开发环境与用户的交互方式 (实际上询问在交互方面&am…

聚观早报 | 华为Pura70系列先锋计划;月之暗面升级Kimi

聚观早报每日整理最值得关注的行业重点事件,帮助大家及时了解最新行业动态,每日读报,就读聚观365资讯简报。 整理丨Cutie 4月19日消息 华为Pura70系列先锋计划 月之暗面升级Kimi OPPO Find X7将推白色版本 波士顿动力推出人形机器人 v…

Tomcat漏洞利用工具-TomcatVuln

检测漏洞清单 CVE-2017-12615 PUT文件上传漏洞 tomcat-pass-getshell 弱认证部署war包 弱口令爆破 CVE-2020-1938 Tomcat 文件读取/包含项目地址 https://github.com/errors11/TomcatVuln TomcatVuln put文件上传 ajp协议漏洞 默认读取web.xml文件,漏洞利用…

RedHat9 KVM虚拟技术

以下有使用RedHat9单独的虚拟机也有使用RHEL9学员练习机和RHEL7学员练习机 KVM虚拟技术介绍 Linux的KVM(Kernel-based Virtual Machine)虚拟技术是一种基于Linux内核的虚拟化解决方案。它允许在单个物理服务器上创建和运行多个隔离的虚拟机,每个虚拟机都有自己的操作系统和…

启动appium服务的2种方法(python脚本cmd窗口)

1.通过cmd窗口命令来启动 2.通过python代码启动 2.1启动单个appium服务 2.2启动多个appium服务 3.端口说明 一.端口号设置Appium服务器端口:4723 bp端口:4724 Appium服务器端口:4725 bp端口:4726可以看到appium服务器端口和bp端…

Redis快速入门操作

启动Redis 进入命令行客户端 字符串命令常用操作(redis默认使用字符串来存储数据) 列表(Lists)常用操作 集合(Sets)常用操作 (无序集合且元素不可重复) 有序集合(So…

YOLOv9最新改进系列:YOLOv9改进加入新型高效的多尺度注意力(EMA)模块保留每个通道的信息并减少计算成本!助力v9检测性能遥遥领先!

YOLOv9最新改进系列:YOLOv9改进加入新型高效的多尺度注意力(EMA)模块保留每个通道的信息并减少计算成本!助力v9检测性能遥遥领先! YOLOv9原文链接戳这里,原文全文翻译请关注B站Ai学术叫叫首er 详细的改进…

PCL SAC_IA配准高阶用法——统计每次迭代的配准误差并可视化

目录 一、概述二、代码实现三、可视化代码四、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 在进行论文写作时,需要做对比实验,来分析改进算法的性能,期间用到了迭代误差分布统计的比较分析,为直…

Java web应用性能分析之服务端慢和优化概叙

前面已经分析了客户端慢、前端页面慢、入口Nginx慢,按照上图接下来就是我们服务端重点的接口慢分析优化、服务器资源性能瓶颈分析、服务器带宽性能瓶颈分析。 性能优化的目的 性能优化的目标是提高应用的性能,使其更加高效、稳定和可靠。性能优化包括服…

CSS 格式化上下文 + CSS兼容处理

个人主页:学习前端的小z 个人专栏:HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 ✍CSS 格式化上下文🔥1 格式化上下文🌷1.1 块级格式化…

Kubernetes Pod的配置管理 ConfigMap和Secret

目录 前言 一、为什么需要配置管理 二、使用ConfigMap管理Pod的配置信息 2.1 创建ConfigMap(4种方式) 2.1.1 指定ConfigMap的参数创建 2.1.2 指定配置文件创建ConfigMap 2.1.3 通过一个文件内的多个键值对创建ConfigMap 2.1.4 yaml文件创建Config…

环境多介质逸度模型实践技术与典型案例【代码】

随着污染物在各种环境中的迁移和转化,多介质污染物模型日益受到关注。在各类多介质模型中,基于逸度概念的逸度模型由于运用范围广,建模数据要求较低而广受欢迎。 专题一:基本理论 1.逸度的定义 2.逸度模型的基本原理 3.各介质…

Golang | Leetcode Golang题解之第36题有效的数独

题目: 题解: func isValidSudoku(board [][]byte) bool {var rows, columns [9][9]intvar subboxes [3][3][9]intfor i, row : range board {for j, c : range row {if c . {continue}index : c - 1rows[i][index]columns[j][index]subboxes[i/3][j/3]…

爆炸之linux-nacos2.0系列集群安装部署

一、环境配置 1、新建磁盘分区 fdisk /dev/vdb 2、创建文件系统 mkfs.xfs /dev/vdb13、创建挂载点: 在 / 目录下创建一个新的目录作为挂载点。/afc 目录 mkdir /afc4、挂载磁盘: 使用 mount 命令将磁盘挂载到新创建的目录。 mount /dev/vdb /afc5、…

用html写一个搜索页面

<!DOCTYPE html> <html lang"en" > <head><meta charset"UTF-8"><title>搜索框设计</title><link rel"stylesheet" href"./style.css"> </head> <body> <div class"se…

pytorch-手写数字识别之全连接层实现

目录 1. 背景2. nn.Linear线性层2. 实现MLP网络3. train4. 完整代码 1. 背景 上一篇https://blog.csdn.net/wyw0000/article/details/137622977?spm1001.2014.3001.5502中实现手撸代码的方式实现了手写数字识别&#xff0c;本文将使用pytorch的API实现。 2. nn.Linear线性层…