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

作者:来自 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

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

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

相关文章

flink cdc mysql整理与总结

文章目录 一、业务中常见的需要数据同步的场景CDC是什么FlinkCDC是什么CDC原理为什么是FlinkCDC业务场景flink cdc对应flink的版本 二、模拟案例1.阿里云flink sql2.开源flink sql(单机模式)flink 安装安装mysql3.flink datastream 三、总结 提示:以下是本篇文章正文…

行业分析---造车新势力之蔚来汽车

1 前言 在之前的博客中,笔者分析了苹果《行业分析---我眼中的Apple Inc.》,苹果已经成为世界级的公司。随后也分析了电动汽车公司特斯拉《行业分析---马斯克的Tesla》,特斯拉也在不断成长。目前能分析的新能源汽车公司不多,小米汽…

C#对文件进行批量重命名或者对某个单独的文件进行改名

目录 一、FolderBrowserDialog 二、OpenFileDialog 三、Path 四、ui设计 五、代码部分 一、FolderBrowserDialog FolderBrowserDialog是一个用于选择文件夹的对话框控件,可以在windows Forms应用程序中使用。使用它可以让用户选择一个文件夹,并返…

arping 一键检测网络设备连通性(KALI工具系列二)

目录 1、KALI LINUX简介 2、arping工具简介 3、在KALI中使用arping 3.1 目标主机IP(win) 3.2 KALI的IP 4、操作示例 4.1 IP测试 4.2 ARP测试 4.3 根据存活情况返回 5、总结 1、KALI LINUX简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发…

QGIS开发笔记(二):Windows安装版二次开发环境搭建(上):安装OSGeo4W运行依赖其Qt的基础环境Demo

若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/139136356 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…

如何利用线程池实现互联网验证码保护服务

如何利用线程池实现互联网验证码保护服务 1、业务背景与实现思路2、代码实操1、业务背景与实现思路 首先介绍一下业务背景,假设我们的系统是一个短视频播放网站,每个新加入的用户都需要注册账号并绑定手机号。为了验证用户手机的正确性,我们的系统会发送一条验证码到用户注…

上下文视觉提示实现zero-shot分割检测及多visual-prompt改造

文章目录 一、Closed-Set VS Open-set二、DINOv2.1 论文和代码2.2 内容2.3 安装部署2.4 使用效果 三、多visual prompt 改造3.1 获取示例图mask3.2 修改函数参数3.3 推理代码3.4 效果的提升! 四、总结 本文主要介绍visual prompt模型DINOv,该模型可输入八…

Linux基础(八):计算机基础概论

本篇博客简单介绍计算机的基础知识,为后续学习做个铺垫。 目录 一、计算机的基本组成 1.1 计算机组成五大部件 1.1.1 运算器(Arithmetic Logic Unit,ALU) 1.1.2控制器 (Control Unit,CU) …

【网络协议】应用层协议--HTTP

文章目录 一、HTTP是什么?二、HTTP协议工作过程三、HTTP协议1. fiddler2. Fiddler抓包的原理3. 代理服务器是什么?4. HTTP协议格式1.1 请求1.2 响应 四、认识HTTP的请求1.认识HTTP请求的方法2.认识请求头(header)3.认识URL3.1 URL是什么&…

uni-app实现页面之间的跳转传参(八)

界面之间的参数传递在 开发中经常会用到,这节主要将一下uni-app开发应用是的传参情况。如下图所示,我的一级界面将点检分成三类:日点检、周点检和年保养;在点击相应的会导航到相应的功能。 在uni-app中常用的方法有uni.navigateTo(OBJECT)、uni.redirectTo(OBJECT);简单的…

详解 Scala 的集合类型

一、集合简介 1. 类型 序列 Seq:类似于 Java 中的 List 接口集 Set:类似于 Java 中的 Set 接口映射 Map:类似于 Java 中的 Map 接口所有的集合都扩展自 Iterable 特质 2. 不可变集合 位于 scala.collection.immutable 包,指该集…

基于docxtpl的模板生成Word

docxtpl是一个用于生成Microsoft Word文档的模板引擎库。它结合了docx模块和Jinja2模板引擎,使用户能够使用Microsoft Word模板文件并在其中填充动态数据。这个库提供了一种方便的方式来生成个性化的Word文档,并支持条件语句、循环语句和变量等控制结构&…

Spring Boot集成testcontainers快速入门Demo

1.什么是testcontainers? Testcontainers 是一个用于创建临时 Docker 容器进行单元测试的 Java 库。当我们想要避免使用实际服务器进行测试时,它非常有用。,官网介绍称支持50多种组件。​ 应用场景 数据访问层集成测试: 使用My…

HTTP交互导致ECONNABORTED的原因之一

背景: 本次记录的,是一次使用HTTP交互过程中遇到的问题,问题不大,就是给题目上这个报错补充一种可能的解决方案。 程序大致流程: 1. 设备向服务器A请求信息 2. 拿到回复记录下回复内容中的数据包下载地址等信息 3…

2024GDCPC广东省赛记录

比赛流程体验,依托,开赛几分钟了,选手还卡在门外无法入场,也没给延时,说好的桌上会发三支笔,于是我们就没准备,要了三次笔,终于在一小时后拿到了😅 比赛题目体验&#xf…

PyTorch深度学习快速入门——P1-P13

环境配置 Anaconda,创建conda create -n pytorch python3.12,使用conda activate pytorch切换到环境。安装pytorch,conda install pytorch torchvision torchaudio pytorch-cuda11.8 -c pytorch -c nvidia,使用import torch&…

力扣496. 下一个更大元素 I

Problem: 496. 下一个更大元素 I 文章目录 题目描述思路复杂度Code 题目描述 思路 因为题目说nums1是nums2的子集,那么我们先把nums2中每个元素的下一个更大元素算出来存到一个映射里,然后再让nums1中的元素去查表即可 复杂度 时间复杂度: O ( n 1 n 2…

宁夏银川、山东济南、中国最厉害的改名大师的老师颜廷利教授的前沿思想观点

在当代社会,一个响亮的声音穿越了传统的迷雾,它来自东方哲学的殿堂,由一位现代学者颜廷利教授所发出。他的话语,如同一股清泉,在混沌的世界里激荡着思考的波澜:"有‘智’不在年高,无‘智’…

福昕PDF编辑器自定义快捷方式

你是否为用不惯福昕PDF编辑器自带的快捷键而发愁?今天,我和大家分享一下如何设置自己想要的快捷键方式,希望能对大家有帮助。 步骤一:打开福昕PDF编辑,并找到更多命令 步骤二:切换到键盘一栏,并…

Stream流常用操作

一、中间操作 中间操作是返回一个新的流,并在返回的流中包含所有之前的操作结果。它们总是延迟计算,这意味着它们只会在终止操作时执行,这样可以最大限度地优化资源使用。 1. filter(过滤) filter()方法接受一个谓词(一个返回boo…