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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯2024年第十五届省赛真题-数字接龙

思路:DFS,因为输入的i,j的顺序导致,方向向量中x是行编号,y是列编号。方向向量可能和直觉上不同。 错的 //int dx[8]{0,1,1,1,0,-1,-1,-1}; //int dy[8]{1,1,0,-1,-1,-1,0,1}; 对的 int dx[]{-1,-1,0,1,1,1,0,-1}; int…

论文复现《SplaTAM: Splat, Track Map 3D Gaussians for Dense RGB-D SLAM》

前言 SplaTAM算法是首个开源的基于RGB-D数据,生成高质量密集3D重建的SLAM技术。 通过结合3DGS技术和SLAM框架,在保持高效性的同时,提供精确的相机定位和场景重建。 代码仓库:spla-tam/SplaTAM: SplaTAM: Splat, Track & Map 3…

算法一:数字 - 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 来源:力扣(LeetCode) 链接&#xf…

政安晨:【Keras机器学习示例演绎】(一)—— 利用类 U-Net 架构进行图像分割

目录 下载数据 准备输入图像的路径和目标分割掩码 一幅输入图像和相应的分割掩码是什么样子的? 准备数据集,以加载和矢量化成批数据 准备 U-Net Xception 风格模型 预留验证分割 训练模型 可视化预测 政安晨的个人主页:政安晨 欢迎 &…

4.18学习总结

多线程补充 等待唤醒机制 现在有两条线程在运行,其中一条线程可以创造一个特殊的数据供另一条线程使用,但这个数据的创建也有要求:在同一时间只允许有一个这样的特殊数据,那么我们要怎样去完成呢?如果用普通的多线程…

FTP客户端Transmit 5 for Mac中文激活版

Transmit 5是一款功能强大的Mac FTP客户端软件,它由Panic公司开发,为用户提供简单、高效的文件传输体验。 Transmit 5 for Mac中文激活版下载 Transmit 5支持多种传输协议,如FTP、SFTP、WebDAV和Amazon S3等,满足用户不同的文件传…

eCongnition 获取特征(shp)

目录 1、加载数据和分割的shp文件 2、将专题(导入的shp)转换为对象 3、导出特征 1、加载数据和分割的shp文件 我们加载数据,在第二个框(Thematic La..)里加载矢量shp 导入的.shp文件称为专题层(Thematic Layer), 显示方式如下所示&#x…

深入探索:Facebook如何重塑社交互动

在当代社会中,社交互动已成为日常生活的核心组成部分。而在众多的社交媒体平台中,Facebook凭借其卓越的用户基础和创新的功能,已经成为了全球最大的社交媒体平台。本文将深入探讨Facebook如何通过其独特的特性和功能,重塑了人们的…

Python 字符串 Base64

因消息传输的需要,我们需要对大量文本的字符串进行一下 Base64 转换。 这样的好处是因为在传输的字符串中可能有存在一些特殊字符,这些特殊在经过网络传输的时候会出现编码的问题,并且会影响传输稳定性。 使用 Base64 可以避免这个问题。 方…

数据库--Sqlite3

1、思维导图 2sqlite3在linux中是实现数据的增删&#xff0c;改 #include<myhead.h> int main(int argc, const char *argv[]) { //1、定义一个数据库句柄指针 sqlite3* ppDb NULL; //2、创建或打开数据库 if(sqlite3_open("./mydb…

深入解析Apache Hadoop YARN:工作原理与核心组件

什么是YARN&#xff1f; YARN&#xff08;Yet Another Resource Negotiator&#xff09;是Apache Hadoop生态系统中的一个重要组件&#xff0c;用于资源管理和作业调度。它是Hadoop 2.x版本中的一个关键特性&#xff0c;取代了旧版本中的JobTracker和TaskTracker。YARN的设计目…

ElasticSearch实战之项目搜索高亮

文章目录 1. 前情配置2、数据操作2.1 操作API2.2 数据入库 3. 高亮搜索3.1 方法封装3.2 高亮搜索 1. 前情配置 为满足ElasticSearch可在项目中实现搜索高亮&#xff0c;我们需要先做一些前情配置 导入ElasticSearch依赖 <dependency><groupId>org.springframewor…

【Flutter】多语言方案一:flutter_localizations 与 GetX 配合版

系列文章目录 多语言方案&#xff1a;flutter_localizations 与 GetX 配合版&#xff0c;好处&#xff1a;命令行生成多语言字符串的引用常量类&#xff0c;缺点&#xff1a;切换语言以后&#xff0c;主界面需要手动触发setState&#xff0c;重绘将最新的Locale数据设置给GetM…

【Leetcode每日一题】 分治 - 排序数组(难度⭐⭐)(60)

1. 题目解析 题目链接&#xff1a;912. 排序数组 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路&#xff1a; 快速排序作为一种经典的排序算法&#xff0c;其核心思想在于通过“分而治之”的策略&#xff…

Idea修改【Help->Edit Custom VM Options...】后,导致idea无法正常启动的解决方法

一、错误场景: 二、解决方法&#xff1a; 修改文件路径&#xff1a;C:\Users\tianjm&#xff08;写自己的用户名&#xff09;\AppData\Roaming\JetBrains\IdeaIC2024.1&#xff08;选自己安装的版本&#xff09;

Linux 网络编程项目--简易ftp

主要代码 config.h #define LS 0 #define GET 1 #define PWD 2#define IFGO 3#define LCD 4 #define LLS 5 #define CD 6 #define PUT 7#define QUIT 8 #define DOFILE 9struct Msg {int type;char data[1024];char secondBuf[128]; }; 服务器: #i…

传统零售行业如何做数字化转型?

传统零售行业的数字化转型是一个系统性的过程&#xff0c;涉及到企业的多个方面。以下是一些关键步骤和策略&#xff0c;帮助传统零售企业实现数字化转型&#xff1a; 1、明确转型目标和战略 首先&#xff0c;企业需要明确数字化转型的目标和战略。包括确定企业的核心竞争力、…

Java内存模型和 JVM 内存运行时

文章目录 前言一、什么是Java 的内存模型&#xff1f;二、什么是 JVM 的运行时数据区Java8 之前和之后的区别JVM 内存模型JVM 内存区域JVM 内存垃圾回收JVM如何判断哪些对象不在存活&#xff1f;JVM运行过程中如何判断哪些对象是垃圾&#xff1f; JVM 垃圾回收Java8 中的 jvm如…

.rmallox勒索病毒来袭:如何守护您的数据安全?

引言&#xff1a; 随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中勒索病毒更是成为了网络安全领域的一大难题。.rmallox勒索病毒作为一种典型的恶意软件&#xff0c;通过加密受害者文件并勒索赎金的方式&#xff0c;给企业和个人带来了巨大的经济损…

指纹浏览器如何高效帮助TikTok账号矩阵搭建?

TikTok的账号矩阵&#xff0c;可能听起来还比较陌生&#xff0c;但随着TikTok业务已经成为吃手可热的跨境业务&#xff0c;TikTok多账号矩阵已成为流行策略。但它有什么优点呢&#xff1f;操作多个帐户会导致被禁止吗&#xff1f;如何有效地建立账户矩阵开展业务&#xff1f;这…