如何在 Elasticsearch 中选择精确 kNN 搜索和近似 kNN 搜索

news2024/11/18 23:44:57

作者:来自 Elastic Carlos Delgado

kNN 是什么?

语义搜索(semantic search)是相关性排名的强大工具。 它使你不仅可以使用关键字,还可以考虑文档和查询的实际含义。

语义搜索基于向量搜索(vector search)。 在向量搜索中,我们要搜索的文档具有为其计算的向量嵌入。 这些嵌入是使用机器学习模型计算的,并作为向量返回,与我们的文档数据一起存储。

执行查询时,将使用相同的机器学习模型来计算查询文本的嵌入。 语义搜索包括通过将查询嵌入与文档嵌入进行比较来查找最接近查询的结果。

kNN(或 k nearest neighbors - k 最近邻)是一种用于获取与特定嵌入最接近的前 k 个结果的技术。

使用嵌入计算查询的 kNN 有两种主要方法:精确和近似。 这篇文章将帮助你:

  • 了解什么是精确和近似 kNN 搜索
  • 如何为这些方法准备索引
  • 如何确定哪种方法最适合你的用例

精确 kNN:搜索所有内容

计算更接近结果的一种方法是将所有现有文档嵌入与查询的文档嵌入进行比较。 这将确保我们获得尽可能最接近的匹配,因为我们将比较所有匹配。 我们的搜索结果将尽可能准确,因为我们正在考虑整个文档语料库并将所有文档嵌入与查询嵌入进行比较。

当然,与所有文档进行比较有一个缺点:需要时间。 我们将使用相似度函数对所有文档逐一计算嵌入相似度。 这也意味着我们将线性扩展 —— 文档数量增加一倍可能需要两倍的时间。

可以使用 script_score 和用于计算向量之间相似度的向量函数在向量场上进行精确搜索。

近似 kNN:一个很好的估计

另一种方法是使用近似值而不是考虑所有文档。 为了提供 kNN 的有效近似,Elasticsearch 和 Lucene 使用分层导航小世界 HNSW (Hierachical Navigation Small Worlds)。

HNSW 是一种图数据结构,它维护不同层中靠近的元素之间的链接。 每层都包含相互连接的元素,并且还与其下方层的元素相连接。 每层包含更多元素,底层包含所有元素。

图 1 - HNSW 图示例。 顶层包含开始搜索的初始节点。 这些初始节点充当较低层的入口点,每个层包含更多节点。 下层包含所有节点。

可以把它想象成开车:有高速公路、道路和街道。在高速公路上行驶时,你会看到一些描述高层次区域(如城镇或社区)的出口标志。然后你会到达一条有具体街道指示的道路。一旦你到达某条街道,你就可以找到具体的地址,以及同一社区内的其他地址。

HNSW(Hierarchical Navigable Small World)结构类似于此,它创建了不同层次的向量嵌入。它计算离初始查询较近的 “高速公路”,选择看起来更有希望的出口,继续寻找更接近目标地址的地方。这在性能方面非常优秀,因为它不必考虑所有文档,而是使用这种多层次的方法快速找到接近目标的近似结果。

但是,这只是一个近似值。并不是所有节点都是互联的,这意味着可能会忽略某些更接近特定节点的结果,因为它们可能没有连接。节点的互联程度取决于 HNSW 结构的创建方式。

HNSW 的效果取决于多个因素:

  • 它是如何构建的。HNSW 的构建过程会考虑一定数量的候选节点,作为某一特定节点的近邻。增加考虑的候选节点数量会使结构更精确,但会在索引时花费更多时间。dense vector index_options 中的 ef_construction 参数用于此目的。

  • 搜索时考虑的候选节点数量。在寻找更近结果时,过程会跟踪一定数量的候选节点。这个数量越大,结果越精确,但搜索速度会变慢。kNN 参数中的 num_candidates 控制这种行为。

  • 我们搜索的分段数量。每个分段都有一个需要搜索的 HNSW 图,其结果需要与其他分段图的结果结合。分段越少,搜索的图就越少(因此速度更快),但结果集的多样性会减少(因此精度较低)。

总的来说,HNSW 在性能和召回率之间提供了良好的权衡,并允许在索引和查询两方面进行微调。

使用 HNSW 进行搜索可以在大多数情况下通过 kNN 搜索部分完成。对于更高级的用例,也可以使用 kNN 查询,例如:

  • 将 kNN 与其他查询结合(作为布尔查询或固定查询的一部分)
  • 使用 function_score 微调评分
  • 提高聚合和字段折叠(field collapse)的多样性

你可以在这篇文章中查看关于 kNN 查询及其与 kNN 搜索部分的区别。我们将在下面深入讨论何时使用这种方法与其他方法。

为精确和近似搜索建立索引

dense_vector 字段类型

对于存储嵌入,dense_vector 字段有两种主要的索引类型可供选择:

  1. flat 类型(包括 flat 和 int8_flat):存储原始向量,不添加 HNSW 数据结构。使用 flat 索引类型的 dense_vector 将始终使用精确的 kNN,kNN 查询将执行精确查询而不是近似查询。

  2. HNSW 类型(包括 hnsw 和 int8_hnsw):创建 HNSW 数据结构,允许使用近似 kNN 搜索。

这是否意味着你不能对 HNSW 字段类型使用精确的 kNN?并非如此!你可以通过 script_score 查询使用精确 kNN,也可以通过 kNN 部分和 kNN 查询使用近似 kNN。这样可以根据你的搜索用例提供更多的灵活性。

使用 HNSW 字段类型意味着需要构建 HNSW 图结构,这需要时间、内存和磁盘空间。如果你只会使用精确搜索,可以使用 flat 向量字段类型。这确保了你的嵌入索引是最佳的,并且占用更少的空间。

请记住,在任何情况下都应避免将嵌入存储在 _source 中,以减少存储需求。

量化

使用量化技术,无论是 flat(int8_flat)还是 HNSW(int8_hnsw)类型的索引,都可以帮助你减少嵌入的大小,从而使用更少的内存和磁盘存储来保存嵌入信息。

由于搜索性能依赖于尽可能多地将嵌入存储在内存中,因此你应该始终寻找减少数据的方法。使用量化是在内存和召回率之间进行权衡。

如何在精确搜索和近似搜索之间做出选择?

没有一种适用于所有情况的答案。你需要考虑多个因素,并进行实验,以找到性能和准确性之间的最佳平衡:

数据规模

不应该不惜一切代价避免搜索所有内容。根据你的数据规模(文档数量和嵌入维度),进行精确的 kNN 搜索可能是合理的。

作为一个经验法则,如果需要搜索的文档少于一万,可能表明应该使用精确搜索。请记住,可以提前过滤需要搜索的文档数量,因此通过应用过滤条件可以限制实际需要搜索的文档数量。

近似搜索在文档数量方面具有更好的扩展性,因此如果你有大量文档需要搜索,或者预计文档数量会显著增加,应该选择近似搜索。

图 2 - 使用 so_vector rally track 中 768 维向量进行精确和近似 kNN 的示例运行。该示例展示了精确 kNN 的线性运行时间与 HNSW 搜索的对数运行时间。

过滤 - filtering

过滤非常重要,因为它减少了需要考虑搜索的文档数量。在决定使用精确搜索还是近似搜索时,需要考虑这一点。可以使用 query filters 来减少需要考虑的文档数量,无论是精确搜索还是近似搜索。

然而,近似搜索在过滤时采用了不同的方法。在使用 HNSW 进行近似搜索时,查询过滤器将在检索到前 k 个结果后应用。这就是为什么与 kNN 查询一起使用查询过滤器被称为 kNN 的后过滤。

图 3 - kNN 搜索中的后过滤。

这种特定的 kNN 查询过滤器被称为 kNN 预过滤器,因为它在检索结果之前应用,而不是之后。因此,在使用 kNN 查询的上下文中,常规查询过滤器被称为后过滤器。

幸运的是,还有另一种与 kNN 一起使用的方法,即在 kNN 查询本身中指定过滤器。 当遍历 HNSW 图收集结果时,此过滤器适用于图元素,而不是事后应用。 这确保返回前 k 个元素,因为将遍历图 - 跳过未通过过滤器的元素 - 直到我们获得前 k 个元素。

即将推出的功能

即将推出的一些改进将有助于精确和近似 kNN。

Elasticsearch 将增加将 dense_vector 类型从 flat 升级到 HNSW 的功能。这意味着你可以先使用 flat 向量类型进行精确 kNN,当需要扩展时可以开始使用 HNSW。使用近似 kNN 时,你的段将透明地被搜索,并在合并时自动转换为 HNSW。

一个新的精确 kNN 查询将被添加,以便使用简单的查询来对 flat 和 HNSW 字段进行精确 kNN,而不是依赖于 script score 查询。这将使精确 kNN 更加简便。

结论

那么,你应该在文档上使用近似 kNN 还是精确 kNN 呢?请检查以下几点:

  • 文档数量:如果少于一万(应用过滤器后),可能适合使用精确搜索。
  • 你的搜索是否使用了过滤器:这会影响要搜索的文档数量。如果需要使用近似 kNN,请记住使用 kNN 预过滤器以获取更多结果,代价是性能下降。

你可以通过使用 HNSW dense_vector 进行索引,并将 kNN 搜索与 script_score 进行精确 kNN 的对比,来比较两种方法的性能。这允许在使用相同字段类型的情况下比较两种方法(如果决定使用精确搜索,请记住将 dense_vector 字段类型更改为 flat)。

祝你搜索愉快!

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

原文:kNN in Elasticsearch: How to choose between exact and approximate kNN search — Elastic Search Labs

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

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

相关文章

Gradient-checkpointing的原理

原文: 将更大的网络安装到内存中。|by 雅罗斯拉夫布拉托夫 |张量流 |中等 (medium.com) 前向传播时,隔几层就保留一层activation数据,其余层的activation都释放掉; 反向传播时,从最近的checkpoint去重新跑forward&…

Docker部署SpringBoot项目(jar包+Mysql)

部署Java项目 项目准备准备Java项目镜像准备配置网络 部署项目细节展示 项目准备 准备Java项目 hmall项目是一个maven聚合项目,使用IDEA打开hmall项目,查看项目结构如图: 我们要部署的就是其中的hm-service,其中的配置文件采用…

前 9 名最佳视频转换器软件完全免费

前 9 名免费视频转换器是什么?在此视频转换器评论中,我们收集了一些有用的提示并列出了顶级免费视频转换器软件,并找出适合所有级别(从初学者到专家)的最佳免费视频转换器。 顶级视频转换器列表 在这一部分中&#xf…

go 爬虫之 colly 简单示例

1. 背景 colly 是 Go 实现的比较有名的一款爬虫框架,而且 Go 在高并发和分布式场景的优势也正是爬虫技术所需要的。它的主要特点是轻量、快速,设计非常优雅,并且分布式的支持也非常简单,易于扩展。 2. 官方文档 https://go-col…

【简单易用,新人友好】一个轻量级生物信息学流程框架,从此解决99%的生物信息学流程搭建问题...

生物信息学数据分析流程的搭建是一项繁重而复杂的工作。随着行业的发展,各种生信流程框架层出不穷,比如有: NextflowSnakemakeCWLWDL 各种标准,各种规则,令人眼花缭乱。选择太多,往往令人无所适从。特别是新进入行业的…

03自动辅助导航驾驶NOP其实就是NOA

蔚来NOP是什么意思?蔚来NOP是啥 蔚来NOP的意思就是NavigateonPilot智能辅助导航驾驶,也就是大家俗称的高阶辅助驾驶,在车主设定好导航路线,并且符合开启NOP条件的前提下,蔚来NOP可以代替驾驶员完成从A点到B点的智能辅助…

<学习笔记>从零开始自学Python-之-实用库篇(一)-pyscript

由Anaconda创建的PyScript是一项实验性的但很有前途的新技术,它使python运转时在支撑WebAssembly的浏览器中作为一种脚本言语运用。 每个现代常用的浏览器现在都支撑WebAssembly,这是许多言语(如C、C和Rust)能够编译的高速运转时…

springboot项目,@Test写法 @Before @After

某文件示例 package cn.xxx.crm.boss;import cn.xxxx.crm.manager.mq.rabbit.AliyunCredentialsProvider; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; im…

大模型提示词Prompt学习

引言 关于chatGPT的Prompt Engineer,大家肯定耳朵都听起茧了。但是它的来由?,怎么能用好?很多人可能并不觉得并不是一个问题,或者说认定是一个很快会过时的概念。但其实也不能说得非常清楚(因为觉得没必要深…

x264 码率控制中实现 VBV 算法源码分析

关于 VBV 的解释与原理可以参考x264 码率控制 VBV 原理。 x264中 VBV 算法执行的流程 vbv 参数配置相关函数 x264_param_default函数 功能:编码参数默认设置,关于 vbv的参数的默认设置;函数内vbv相关代码:/* ... */ //代码有删减 param->rc.i_vbv_max_bitrate = 0; par…

《软件方法(下)》8.3.4.3 关于“整体-部分”结构

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 8.3 建模步骤C-2 识别类的关系 8.3.4 识别关联关系 8.3.4.2 关联的进一步细分 是否进一步细分各种关联,各种面向对象方法学观点不同。有的认为关联就是关联,…

mvc的常见注解

问文心一言的,记录一下。 PathVariable 路径变量注解 PathVariable 是 Spring MVC 提供的一个注解,它用于从 URI 模板变量中绑定值到控制器方法的参数上。当你在 RequestMapping、GetMapping、PostMapping、PutMapping、DeleteMapping 等注解的 URL 路…

maven默认src下的xml,properties文件不打包到classes文件夹下

一、第一种是建立src/main/resources文件夹,将xml,properties等资源文件放置到这个目录中。maven工具默认在编译的时候,会将resources文件夹中的资源文件一块打包进classes目录中。 这时候注意把resources设置成resource目录,已经…

操作系统 c语言模仿 动态分区存储管理方式的主存分配回收

1.实验目的 深入了解动态分区存储管理方式的主存分配回收的实现。 2.实验预备知识 存储管理中动态分区的管理方式。 3.实验内容 编写程序完成动态分区存储管理方式的主存分配回收的实现。实验具体包括:首先确定主存空间分配表&…

Android 布局中@NULL的使用和代码实现方式详解

文章目录 1、使用场景2、示例代码实现2.1、移除背景2.2 、移除文本2.3、移除布局宽度或高度2.4、移除提示文本2.5、移除图像资源 3、综合示例3.1、布局文件 activity_main.xml3.2、主活动文件 MainActivity.java3.4、资源文件3.5、运行结果 4、优点5、缺点6、综合分析6.1、适用…

地下城游戏(leetcode)

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 地下城游戏https://leetcode.cn/problems/dungeon-game/description/ 图解分析&#xff1a; 代码 class Solution { public:int calculateMinimumHP(vector<vector<int>>& vv) {int row vv.size(), col …

【云原生】Kubernetes基础命令合集

目录 引言 一、命令概述 &#xff08;一&#xff09;命令分类 &#xff08;二&#xff09;基本语法 二、查看基本信息 &#xff08;一&#xff09;环境指令 1.查看版本信息 2.查看资源对象简写 3.添加补全信息 4.查看日志 5.查看集群信息 &#xff08;二&#xff0…

5月26号总结

目录 刷题记录(Codeforces Round 947 &#xff08;Div. 1 Div. 2&#xff09;前三题) 1.A. Bazoka and Mochas Array 2.B. 378QAQ and Mochas Array 3.C. Chamo and Mochas Array 刷题记录(Codeforces Round 947 &#xff08;Div. 1 Div. 2&#xff09;前三题) 1.A. Bazok…

力扣 滑动窗口题目总结

Leetcode3.无重复字符的最长子串 思路&#xff1a; 这道题主要用到思路是&#xff1a;滑动窗口 什么是滑动窗口&#xff1f; 其实就是一个队列,比如例题中的 abcabcbb&#xff0c;进入这个队列&#xff08;窗口&#xff09;为 abc 满足题目要求&#xff0c;当再进入 a&#x…

使用 LangFuse 意外被挂马!我是怎么恢复系统稳定的?

在使用 LangFuse 过程中,被意外挂马!通过一番折腾服务恢复正常~ 本文将详细介绍应对恶意脚本和进程的完整方案,包括识别、清理、恢复和预防步骤。 阿里云扫到的信息 被执行的 Base64 SUlaQnRTCmV4ZWMgJj4vZGV2L251bGwKSUhDa0hQbmQ9Li8uJChkYXRlfG1kNXN1bXxoZWFkIC1jMjApCl…