ES-KNN搜索

这篇具有很好参考价值的文章主要介绍了ES-KNN搜索。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

根据向量查询相近的目标。

常见的使用场景:

  1. 基于自然语言算法的相关性排名

  1. 相似推荐

  1. 相似的图片或视频搜索

提前需要做的准备

使用knn需要对数据进行预处理,你需要把需要匹配的数据转换为有意义的向量值,然后写入目标索引的dense_vector字段中。

KNN方法

对于KNN搜索,ES支持两种方法:

  1. 使用script_score暴力查询

  1. 近似KNN搜索

大多数情况下,我们推荐使用近似的KNN搜索,它会有较低的延迟,但是会牺牲索引的速度和结果的准确度。

如果使用暴力的script_score,那么需要使用query限定匹配的结果集,避免造成慢查询,匹配的结果越小,效果就越好。

暴力script_score

先创建对应的索引,需要有一个或者多个dense_vector字段,如果不需要使用近似KNN搜索,可以忽略字段映射或者将index设置为false,这样便于提高索引速度。

PUT product-index
{
  "mappings": {
    "properties": {
      "product-vector": {
        "type": "dense_vector",
        "dims": 5,
        "index": false
      },
      "price": {
        "type": "long"
      }
    }
  }
}

写入数据

POST product-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "product-vector": [230.0, 300.33, -34.8988, 15.555, -200.0], "price": 1599 }
{ "index": { "_id": "2" } }
{ "product-vector": [-0.5, 100.0, -13.0, 14.8, -156.0], "price": 799 }
{ "index": { "_id": "3" } }
{ "product-vector": [0.5, 111.3, -13.0, 14.8, -156.0], "price": 1099 }

查询

POST product-index/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "range" : {
              "price" : {
                "gte": 1000
              }
            }
          }
        }
      },
      "script": {
        "source": "cosineSimilarity(params.queryVector, 'product-vector') + 1.0",
        "params": {
          "queryVector": [-0.5, 90.0, -10, 14.8, -156.0]
        }
      }
    }
  }
}

提示:如果要限制匹配的文档数量,推荐在script_score里面指定一个filter查询,就像上面这样。

近似KNN

注意:相较于其他查询,近似KNN需要指定特殊的资源,因为需要把所有向量数据放在节点的页缓存中。相关的资源配置参考近似knn搜索调整。

创建索引,下面两个操作是必须的:

  1. dense_vector字段需要开启索引

  1. 指定similarity值,用于对匹配的相似度打分,参数的设置similarity参数

PUT image-index
{
  "mappings": {
    "properties": {
      "image-vector": {
        "type": "dense_vector",
        "dims": 3,
        "index": true,
        "similarity": "l2_norm"
      },
      "title": {
        "type": "text"
      },
      "file-type": {
        "type": "keyword"
      }
    }
  }
}

写入数据

POST image-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "image-vector": [1, 5, -20], "title": "moose family", "file-type": "jpg" }
{ "index": { "_id": "2" } }
{ "image-vector": [42, 8, -15], "title": "alpine lake", "file-type": "png" }
{ "index": { "_id": "3" } }
{ "image-vector": [15, 11, 23], "title": "full moon", "file-type": "jpg" }

查询

POST image-index/_search
{
  "knn": {
    "field": "image-vector",
    "query_vector": [-5, 9, -12],
    "k": 10,
    "num_candidates": 100
  },
  "fields": [ "title", "file-type" ]
}

文档的分数由查询和文档的向量决定,具体怎么计算,参考similarity参数。

knn api 会先去每个分片找num_candidates个最近邻候选者,然后每个分片计算最优的k个。最后把每个分片的结果合并,在计算出k个全局最优。

num_candidates的值可以控制结果的精确度,但是更好的结果会带来更多的消耗。

过滤后的KNN搜索

在KNN搜索里面可以指定的一个filter查询,限定匹配的结果集:

POST image-index/_search
{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": {
        "file-type": "png"
      }
    }
  },
  "fields": ["title"],
  "_source": false
}

注意:这个查询会在KNN搜索期间过滤结果,能够确保返回的结果是k个。如果是post-filtering,那么是在knn搜索完之后再过滤的,返回的结果可能是会少于k个的。

结合近似KNN搜索和其他功能

可以使用knn搜索和不同的query混合搭配:

POST image-index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "mountain lake",
        "boost": 0.9
      }
    }
  },
  "knn": {
    "field": "image-vector",                                         (1)
    "query_vector": [54, 10, -2],                                    (2)    
    "k": 5,                                                                                (3)
    "num_candidates": 50,                                             (4)
    "boost": 0.1
  },
  "size": 10
}
  1. 要查询的目标字段,必须是一个dense_vector类型。

  1. 查询的向量,和目标字段的维度要一样。

  1. 返回最相邻的k个结果,这个值必须小于num_candidates。

  1. 每个分片要考虑的最近邻候选者的数量。 不能超过 10000。 ES 从每个分片收集 num_candidates 个结果,然后合并它们以找到前 k 个结果。 增加 num_candidates 往往会提高最终 k 个结果的准确性。

提示:这里knn里面还可以使用filter,它可以过滤需要匹配的文档,返回的k个文档都会符合匹配的条件。

这个查询是先得到全局5个最相邻结果,然后将他们与匹配查询匹配的结果组合,选出得分最高的前10个返回。分数的计算是这个样子的:

score = 0.9 * match_score + 0.1 * knn_score

近似knn还可以搭配聚合,它聚合的是top k个邻近文档的结果。如果还有query,那么聚合的是混合查询的结果。

索引注意事项

在索引是ES的每个段需要把dense_vector值存储为HNSW graph,构建这些图花费是巨大的。所以写入的时候做好响应时间的调整,调优参考近似KNN搜索调整。

另外,HNSW算法也有一些参数用来在构图开销,搜索速度和准确度之间进行权衡,可以使用index_options来调整这些参数:

PUT image-index
{
  "mappings": {
    "properties": {
      "image-vector": {
        "type": "dense_vector",
        "dims": 3,
        "index": true,
        "similarity": "l2_norm",
        "index_options": {
          "type": "hnsw",                                   (1)
          "m": 32,                                          (2)
          "ef_construction": 100                            (3)    
        }
      }
    }
  }
}
  1. knn使用的算法,当前只支持hnsw。

  1. HNSW 图中每个节点将连接到的邻居数量。 默认为 16。

  1. 在为每个新节点组装最近邻居列表时要跟踪的候选者数量。 默认为 100。

近似KNN搜索限制

  1. 不能再一个nested 映射里面使用dense_vector。

  1. 使用用knn搜索做跨集群搜索时,ccs_minimize_roundtrips 操作不支持。

  1. 因为用了HNSW算法,保证了搜索的速度,但是牺牲掉了准确度。

注意:为了跨多个分片收集全局的top k个结果,近似KNN搜索使用dfs_query_then_fetch搜索类型。这个没法更改。

近似KNN搜索调整

多用dot_product

cosine 相似性计算可以接受任何的浮点向量,对于测试来说它是非常方便的,但是它 得不到最优的效果。相反的,推荐使用dot_product计算相似性。要使用dot_product,需要把所有向量归一化长度为1。这样就能够获得更快的速度,因为它避免了额外的向量长度计算。

确保数据节点有足够的内存

ES的knn搜索用的是HNSW算法,HNSW算法需要大部分的向量数据在内存中才有效果,因此要确保数据节点有足够的RAM保留向量数据和索引结构。如果要检查向量数据的大小,可以使用 Analyze index disk usage API。

HNSW内存占用的预估方法可以参考这个公式:

num_vectors * 4 * (num_dimensions + 32)+ small buffer

特别注意的的是,需要的RAM是要与jvm的堆内存分开的。预留的一小部分缓冲,主要是考虑到其他可能需要用到缓存的地方,比如文本字段或者数值字段,也有可能需要使用文件缓存。

预热文件系统缓存

当es重启的时候,文件缓存是空的,如果等到热数据都加载到内存中,是需要一定的时间的,这个时候可以通过设置index.store.preload提前加载需要的数据到文件缓存中。

注意:当缓存不足够大时,加载过多的索引或者数据到缓存中会使搜索变慢,使用的时候要小心。

减少向量维度

knn搜索向量的维度和搜索速度呈线性关系,在可接受的效果下,应该尽量想办法减少向量的维度。可以尝试使用一些像PCA这样的数据对向量做降维处理。

查询时排除召回向量字段

向量字段一般都比较大,如果返回到结果里面,会有很大的加载开销。通常这个字段在结果里面也是不怎么需要的。在召回的时候应该尽量按需取数。

减少索引的段数量

ES的索引数据是放在segment上的,而向量数据在segment上是存成HNSW图的。当搜索的时候需要扫描多个segment,一个接一个的搜索HNSW图。如果segment过多,必然带来更多的性能开销。默认情况下,ES会有固定的策略把小的端合并成大的端。你也可以手动合并。

强制合并段

使用force merge api将数据合并到一个段里面。这样在搜索的时候KNN搜索就只需要检查一个HNSW图。强制合并段是一个非常消耗资源的操作,要特别注意集群的压力。

注意:段合并推荐在只读索引上操作,当文档更新时,ES只是标记文档已被删除,在常规的合并流程中这种文件会在段合并期间被清理掉。但是强制合并会产生非常大的段,这些段不符合常规合并的条件,因此会有大量的标记删除文档出现,从而带来更高的磁盘使用和更差的搜索性能。

在批量索引的时候创建一个大的段

在索引初始化的时候,可以通过索引的设置,让ES创建一个大的段。

  1. 首先在初始化的时候要确保没有查询服务,并且要禁止索引刷新。

  1. 给 Elasticsearch 一个大的索引缓冲区,这样它可以在刷新之前接受更多的文档。 默认情况下,indices.memory.index_buffer_size 设置为堆大小的 10%。 对于像 32GB 这样大的堆大小,这通常就足够了。 要允许使用完整的索引缓冲区,您还应该增加限制 index.translog.flush_threshold_size。

避免在索引期间做很重的索引

主动索引文档会给KNN搜索带来不好的效果,因为它会占用计算资源。当搜索和索引同时发生的时候,ES也会刷新得比较频繁,从而创建过多的小的segment,而过多的segment又会影响查询的性能。

最好在knn搜索期间大量的索引文档。如果是需要重新构建索引向量这种情况,应该新建一个索引做替换策略,而不是就地更新。

设置合适的预读值

搜索会导致大量随机读取 I/O。 当底层块设备具有高预读值时,可能会做大量不必要的读取 I/O,尤其是在使用内存映射访问文件时。

大多数 Linux 发行版对单个普通设备使用 128KiB 的敏感预读值,但是,当使用软件 raid、LVM 或 dm-crypt 时,生成的块设备(支持 Elasticsearch path.data)可能最终具有非常大的预读值(在几个 MiB 的范围)。 这通常会导致严重的页面(文件系统)缓存抖动,从而对搜索(或更新)性能产生不利影响。

可以使用 lsblk -o NAME,RA,MOUNTPOINT,TYPE,SIZE 检查 KiB 中的当前值。建议预读值为 128KiB。

注意:blockdev 期望 512 字节扇区中的值,而 lsblk 报告 KiB 中的值。 例如,要临时将 /dev/nvme0n1 的预读设置为 128KiB,请指定 blockdev --setra 256 /dev/nvme0n1(todo)

similarity参数

knn计算相似度的算法,支持的值如下:

12_norm

基于向量的L^2(欧几里德距离)计算,_score = 1 / (1 + l2_norm(query, vector)^2)

dot_product

计算两个向量的点积,能够优化cosine算法,是向量要做好归一化。包括文档向量和查询向量。

_score = (1 + dot_product(query, vector)) / 2

cosine

计算余旋相似度,计算余旋相似度最有效的办法是将向量归一化为固定长度,而不是使用dot product。如果没有办法做归一化,那没只能使用余旋。_score = (1 + cosine(query, vector)) / 2。余旋算法不允许向量幅度为0,因为这种情况下没有定义余旋。

注意:向量的相似度和文本的相似度是不一样的。文章来源地址https://www.toymoban.com/news/detail-821510.html

到了这里,关于ES-KNN搜索的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Elasticsearch:向量搜索 (kNN) 实施指南 - API 版

    作者:Jeff Vestal 本指南重点介绍通过 HTTP 或 Python 使用 Elasticsearch API 设置 Elasticsearch 以进行近似 k 最近邻 (kNN) 搜索。 对于主要使用 Kibana 或希望通过 UI 进行测试的用户,请访问使用 Elastic 爬虫的语义搜索入门指南。你也可以参考文章 “ChatGPT 和 Elasticsearch:OpenAI 遇见私有数

    2024年02月04日
    浏览(43)
  • ES8 向量搜索(knn-search)java-api 实践

    官方文档-knn-search kNN搜索 k-nearest neighbor (kNN)搜索找到与查询向量最近的k个向量,如通过相似度计算。 kNN的常见用例包括: 基于自然语言处理(NLP)算法的相关性排序 产品推荐和推荐引擎 图像或视频的相似性搜索 要运行kNN搜索,您必须能够将数据转换为有意义的向量值

    2024年02月12日
    浏览(35)
  • 增强FAQ搜索引擎:发挥Elasticsearch中KNN的威力

    英文原文地址:https://medium.com/nerd-for-tech/enhancing-faq-search-engines-harnessing-the-power-of-knn-in-elasticsearch-76076f670580 增强FAQ搜索引擎:发挥Elasticsearch中KNN的威力 2023 年 10 月 21 日 在一个快速准确的信息检索至关重要的时代,开发强大的搜索引擎是至关重要的。随着大型语言模型(LLM)和

    2024年02月02日
    浏览(28)
  • Elasticsearch:探索 k-nearest neighbor (kNN) 搜索

    由于新一代机器学习模型可以将各种内容表示为向量,包括文本、图像、事件等,人们对向量搜索的兴趣激增。 通常称为 “ 嵌入模型 (embedding models)”,这些强大的表示可以以超越其表面特征的方式捕获两段内容之间的相似性。 K 最近邻 (KNN) 搜索又名语义搜索是一种简单

    2024年02月08日
    浏览(33)
  • 增强常见问题解答搜索引擎:在 Elasticsearch 中利用 KNN 的力量

    在快速准确的信息检索至关重要的时代,开发强大的搜索引擎至关重要。 随着大型语言模型和信息检索架构(如 RAG)的出现,在现代软件系统中利用文本表示(向量/嵌入)和向量数据库已变得越来越流行。 在本文中,我们深入研究了如何使用 Elasticsearch 的 K 最近邻 (KNN) 搜

    2024年02月08日
    浏览(39)
  • Elasticsearch:什么是 kNN?

    kNN(即 k 最近邻算法)是一种机器学习算法,它使用邻近度将一个数据点与其训练并记忆的一组数据进行比较以进行预测 。 这种基于实例的学习为 kNN 提供了 “惰性学习(lazy learning)” 名称,并使算法能够执行分类或回归问题。 kNN 的假设是相似的点可以在彼此附近找到

    2024年02月20日
    浏览(28)
  • 【ES专题】ElasticSearch搜索进阶

    丑话说在前头 ,说实在这篇笔记写的不是很好,确实很多没有实操。 系列上一篇文章:《【ES专题】ElasticSearch 高级查询语法Query DSL实战》 系列下一篇文章:《【ES专题】ElasticSearch集群架构剖析》 理解ES的核心概念,最最重要的是【索引】和【文档】 理解基本的Query DSL语法

    2024年01月16日
    浏览(50)
  • ElasticSearch(ES) 搜索入门笔记

    ElasticSearch简称ES,经过多年的发展,已是很流行的搜索工具了,无需多介绍,下面就粘一点官方介绍 You know, for search (and analysis) Elasticsearch is the distributed search and analytics engine at the heart of the Elastic Stack. Logstash and Beats facilitate collecting, aggregating, and enriching your data and storing it i

    2024年01月22日
    浏览(34)
  • 【ES】【elasticsearch】分布式搜索

    因为我们还需要部署kibana容器,因此需要让es和kibana容器互联。这里先创建一个网络: docker镜像官网https://hub.docker.com/search?q=elasticsearch 运行docker命令,部署单点es: 命令解释: -e \\\"cluster.name=es-docker-cluster\\\" :设置集群名称 -e \\\"http.host=0.0.0.0\\\" :监听的地址,可以外网访问 -e \\\"ES

    2024年02月12日
    浏览(26)
  • Elasticsearch ES实现GEO位置搜索

    ES实现GEO位置搜索 Elasticsearch-7.15.2 附近查询,也叫做距离查询(geo_distance):查询到指定中心点小于某个距离值的所有文档。 创建索引 (my_geo),直接设置mapping GEO字段的创建:添加一个字段location,类型为 geo_point。 GEO类型的字段是不能使用动态映射自动生成的,我们需要在创

    2024年01月16日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包