Elasticsearch:倒数排序融合 - Reciprocal rank fusion - 8.14

news2024/11/28 14:37:42

警告:此功能处于技术预览阶段,可能会在未来版本中更改或删除。语法可能会在正式发布之前发生变化。Elastic 将努力修复任何问题,但技术预览中的功能不受官方正式发布功能的支持 SLA 约束。

倒数排序融合 (reciprocal rank fusion - RRF) 是一种将具有不同相关性指标的多个结果集组合成单个结果集的方法。RRF 无需调整,并且不同的相关性指标不必相互关联即可获得高质量的结果。

注意:在今天的文章中,RFF 有别于之前版本。这个描述是从 8.14.0 开始的。在这个版本之前,请参阅 “Elasticsearch:倒数排序融合 - Reciprocal rank fusion (RRF)”。8.13.0 版本的描述在地址可以看到。在它里面它使用 sub_searches 而不是 rertievers。

RRF 使用以下公式来确定对每个文档进行排名的分数:

score = 0.0
for q in queries:
    if d in result(q):
        score += 1.0 / ( k + rank( result(q), d ) )
return score

# where
# k is a ranking constant
# q is a query in the set of queries
# d is a document in the result set of q
# result(q) is the result set of q
# rank( result(q), d ) is d's rank within the result(q) starting from 1

一个例子是:

倒数排序融合 API

你可以将 RRF 用作 search 的一部分,使用来自使用 RRF 检索器的子检索器(child retrievers)组合的独立顶级文档集(结果集)来组合和排名文档。排名至少需要两个子检索器。

RRF 检索器是一个可选对象,定义为搜索请求的检索器参数(retriever parameter)的一部分。 RRF 检索器对象包含以下参数:

参数描述
retrievers

(必需,检索器对象数组)

子检索器列表,用于指定哪些返回的顶级文档集将应用 RRF 公式。每个子检索器作为 RRF 公式的一部分具有相等的权重。需要两个或更多个子检索器。

rank_constant

(可选,整数)

此值决定每个查询中单个结果集中的文档对最终排名结果集的影响程度。值越高,表示排名较低的文档影响力越大。此值必须大于或等于 1。默认为 60。

window_size

(可选,整数)

此值决定每个查询的单个结果集的大小。较高的值将提高结果相关性,但会降低性能。最终排名的结果集将缩减为搜索请求的大小。window_size 必须大于或等于 size 且大于或等于 1。默认为 size 参数。

使用 RRF 的示例请求:

GET example-index/_search
{
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "term": {
                "text": "shoes"
              }
            }
          }
        },
        {
          "knn": {
            "field": "vector",
            "query_vector": [
              1.25,
              2,
              3.5
            ],
            "k": 50,
            "num_candidates": 100
          }
        }
      ],
      "window_size": 50,
      "rank_constant": 20
    }
  }
}

在上面的例子中,我们独立执行 knn 和标准检索器。然后我们使用 rrf 检索器来合并结果。

  1. 首先,我们执行 knn 检索器指定的kNN搜索以获取其全局前 50 个结果。
  2. 其次,我们执行 standard 检索器指定的查询以获取其全局前 50 个结果。
  3. 然后,在协调节点上,我们将 kNN 搜索热门文档与查询热门文档相结合,并使用来自 rrf 检索器的参数根据 RRF 公式对它们进行排序,以使用默认 size 为 10 获得组合的顶级文档。

注意,如果 knn 搜索中的 k 大于 window_size,则结果将被截断为 window_size。如果 k 小于 window_size,则结果为 k 大小。

倒数排序融合支持的特征

rrf 检索器支持:

  • aggregations
  • from

rrf 检索器目前不支持:

  • scroll
  • point in time
  • sort
  • rescore
  • suggesters
  • highlighting
  • collapse
  • explain
  • profiling

在使用 rrf 检索器进行搜索时使用不受支持的功能会导致异常。

使用多个 standard 检索器的倒数排序融合

rrf 检索器提供了一种组合和排名多个标准检索器的方法。主要用例是组合来自传统 BM25 查询和 ELSER 查询的顶级文档,以提高相关性。

使用 RRF 和多个 standard 检索器的示例请求:

GET example-index/_search
{
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "term": {
                "text": "blue shoes sale"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "text_expansion": {
                "ml.tokens": {
                  "model_id": "my_elser_model",
                  "model_text": "What blue shoes are on sale?"
                }
              }
            }
          }
        }
      ],
      "window_size": 50,
      "rank_constant": 20
    }
  }
}

在上面的例子中,我们分别独立执行两个 standard 检索器。然后我们使用 rrf 检索器来合并结果。

  1. 首先,我们使用标准 BM25 评分算法运行 standard 检索器,指定 “blue shoes sales” 的术语查询。
  2. 接下来,我们使用 ELSER 评分算法运行 standard 检索器,指定 “What blue shoes are on sale?”的文本扩展查询。
  3. rrf 检索器允许我们将完全独立的评分算法生成的两个顶级文档集以相等的权重组合在一起。

这不仅消除了使用线性组合确定适当权重的需要,而且 RRF 还显示出比单独查询更高的相关性。

使用子搜索的倒数排学融合

使用子搜索的 RRF 不再受支持。请改用 retriever API。请参阅使用多个标准检索器的示例。

相互排名融合完整示例

我们首先创建一个带有文本字段、向量字段和整数字段的索引映射,并索引多个文档。对于此示例,我们将使用只有一个维度的向量,以便更容易解释排名。

PUT example-index
{
  "mappings": {
    "properties": {
      "text": {
        "type": "text"
      },
      "vector": {
        "type": "dense_vector",
        "dims": 1,
        "index": true,
        "similarity": "l2_norm"
      },
      "integer": {
        "type": "integer"
      }
    }
  }
}

PUT example-index/_doc/1
{
    "text" : "rrf",
    "vector" : [5],
    "integer": 1
}

PUT example-index/_doc/2
{
    "text" : "rrf rrf",
    "vector" : [4],
    "integer": 2
}

PUT example-index/_doc/3
{
    "text" : "rrf rrf rrf",
    "vector" : [3],
    "integer": 1
}

PUT example-index/_doc/4
{
    "text" : "rrf rrf rrf rrf",
    "integer": 2
}

PUT example-index/_doc/5
{
    "vector" : [0],
    "integer": 1
}

POST example-index/_refresh

我们现在使用 rrf 检索器执行搜索,其中 standard 检索器指定 BM25 查询,knn 检索器指定 kNN 搜索,以及术语聚合

GET example-index/_search
{
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "term": {
                "text": "rrf"
              }
            }
          }
        },
        {
          "knn": {
            "field": "vector",
            "query_vector": [
              3
            ],
            "k": 5,
            "num_candidates": 5
          }
        }
      ],
      "window_size": 5,
      "rank_constant": 1
    }
  },
  "size": 3,
  "aggs": {
    "int_count": {
      "terms": {
        "field": "integer"
      }
    }
  }
}

我们收到了带有排名 hits 和术语聚合结果的响应。请注意,_score 为空,我们改用 _rank 来显示排名靠前的文档。

{
  "took": 14,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 5,
      "relation": "eq"
    },
    "max_score": null,
    "hits": [
      {
        "_index": "example-index",
        "_id": "1",
        "_score": null,
        "_rank": 1,
        "_source": {
          "text": "rrf",
          "vector": [
            5
          ],
          "integer": 1
        }
      },
      {
        "_index": "example-index",
        "_id": "3",
        "_score": null,
        "_rank": 2,
        "_source": {
          "text": "rrf rrf rrf",
          "vector": [
            3
          ],
          "integer": 1
        }
      },
      {
        "_index": "example-index",
        "_id": "2",
        "_score": null,
        "_rank": 3,
        "_source": {
          "text": "rrf rrf",
          "vector": [
            4
          ],
          "integer": 2
        }
      }
    ]
  },
  "aggregations": {
    "int_count": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": 1,
          "doc_count": 3
        },
        {
          "key": 2,
          "doc_count": 2
        }
      ]
    }
  }
}

让我们分析一下这些命中结果的排名方式。我们首先分别运行指定查询的标准检索器和指定 kNN 搜索的 knn 检索器,以收集它们各自的命中结果。

首先,我们查看 standard 检索器中查询的命中结果。

GET example-index/_search
{
  "query": {
    "term": {
      "text": {
        "value": "rrf"
      }
    }
  }
}
"hits" : [
    {
        "_index" : "example-index",
        "_id" : "4",
        "_score" : 0.16152832,              
        "_source" : {
            "integer" : 2,
            "text" : "rrf rrf rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "3",                        
        "_score" : 0.15876243,
        "_source" : {
            "integer" : 1,
            "vector" : [3],
            "text" : "rrf rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "2",                        
        "_score" : 0.15350538,
        "_source" : {
            "integer" : 2,
            "vector" : [4],
            "text" : "rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "1",                        
        "_score" : 0.13963442,
        "_source" : {
            "integer" : 1,
            "vector" : [5],
            "text" : "rrf"
        }
    }
]
  1. rank 1, _id 4
  2. rank 2, _id 3
  3. rank 3, _id 2
  4. rank 4, _id 1

请注意,我们的第一个结果没有向量字段的值。现在,我们来看看 knn 检索器的 kNN 搜索的结果。

GET example-index/_search
{
  "knn": {
    "field": "vector",
    "query_vector": [
      3
    ],
    "k": 5,
    "num_candidates": 5
  }
}
"hits" : [
    {
        "_index" : "example-index",
        "_id" : "3",                   
        "_score" : 1.0,
        "_source" : {
            "integer" : 1,
            "vector" : [3],
            "text" : "rrf rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "2",                   
        "_score" : 0.5,
        "_source" : {
            "integer" : 2,
            "vector" : [4],
            "text" : "rrf rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "1",                   
        "_score" : 0.2,
        "_source" : {
            "integer" : 1,
            "vector" : [5],
            "text" : "rrf"
        }
    },
    {
        "_index" : "example-index",
        "_id" : "5",                   
        "_score" : 0.1,
        "_source" : {
            "integer" : 1,
            "vector" : [0]
        }
    }
]
  1. rank 1, _id 3
  2. rank 2, _id 2
  3. rank 3, _id 1
  4. rank 4, _id 5

我们现在可以获得两个单独排名的结果集,并使用 rrf 检索器的参数对它们应用 RRF 公式以获得最终排名。

# doc  | query     | knn       | score
_id: 1 = 1.0/(1+4) + 1.0/(1+3) = 0.4500
_id: 2 = 1.0/(1+3) + 1.0/(1+2) = 0.5833
_id: 3 = 1.0/(1+2) + 1.0/(1+1) = 0.8333
_id: 4 = 1.0/(1+1)             = 0.5000
_id: 5 =             1.0/(1+4) = 0.2000

我们根据 RRF 公式对文档进行排序,window_size 为 5,截断 RRF 结果集中 size 为 3 的底部 2 个文档。最终结果为 _id:3 作为 _rank:1,_id:2 作为 _rank:2,_id:4 作为 _rank:3。此排名与原始 RRF 搜索的结果集匹配,符合预期。

RRF 中的分页

使用 rrf 时,你可以使用 from 参数对结果进行分页。由于最终排名完全取决于原始查询排名,因此为了确保分页时的一致性,我们必须确保虽然 from 发生变化,但我们已经看到的顺序保持不变。为此,我们使用固定的 window_size 作为可以进行分页的整个可用结果集。这本质上意味着,如果:

  • from + size ≤ window_size :我们可以从最终的 rrf 排名结果集中返回 results[from: from+size] 文档
  • from + size > window_size :我们将得到 0 个结果,因为请求超出了可用的 window_size 大小的结果集。

这里要注意的一件重要事情是,由于 window_size 是我们将从各个查询组件中看到的所有结果,因此分页保证了一致性,即,当且仅当 window_size 保持不变时,不会跳过或重复多个页面中的文档。如果 window_size 发生变化,那么结果的顺序也可能会发生变化,即使是相同的排名。

为了说明上述所有内容,让我们考虑以下简化的示例,其中我们有两个查询,queryA 和 queryB 以及它们的排名文档:

     |  queryA   |  queryB    |
_id: |  1        |  5         |
_id: |  2        |  4         |
_id: |  3        |  3         |
_id: |  4        |  1         |
_id: |           |  2         |

对于 window_size=5,我们将看到来自 queryA 和 queryB 的所有文档。假设 rank_constant=1,rrf 分数将是:

# doc   | queryA     | queryB       | score
_id: 1 =  1.0/(1+1)  + 1.0/(1+4)      = 0.7
_id: 2 =  1.0/(1+2)  + 1.0/(1+5)      = 0.5
_id: 3 =  1.0/(1+3)  + 1.0/(1+3)      = 0.5
_id: 4 =  1.0/(1+4)  + 1.0/(1+2)      = 0.533
_id: 5 =    0        + 1.0/(1+1)      = 0.5

因此,最终排名结果集将是 [1, 4, 2, 3, 5],我们将对其进行分页,因为 window_size == len(results)。在这种情况下,我们将有:

  • from=0, size=2 将返回文档 [1, 4],排名为 [1, 2]
  • from=2, size=2 将返回文档 [2, 3],排名为 [3, 4]
  • from=4, size=2 将返回文档 [5],排名为 [5]
  • from=6, size=2 将返回一个空结果集,因为没有更多结果可以迭代

现在,如果我们的 window_size=2,我们只能分别看到查询 queryA 和 queryB 的 [1, 2] 和 [5, 4] 文档。计算一下,我们会发现结果现在会略有不同,因为我们不知道这两个查询中位置 [3: end] 的文档。

# doc   | queryA     | queryB         | score
_id: 1 =  1.0/(1+1)  + 0              = 0.5
_id: 2 =  1.0/(1+2)  + 0              = 0.33
_id: 4 =    0        + 1.0/(1+2)      = 0.33
_id: 5 =    0        + 1.0/(1+1)      = 0.5

最终排序的结果集将是 [1, 5, 2, 4],并且我们将能够对顶部的 window_size 结果进行分页,即 [1, 5]。因此,对于与上述相同的参数,我们现在将有:

  • from=0, size=2 将返回 [1, 5],排名为 [1, 2]
  • from=2, size=2 将返回一个空结果集,因为它超出了可用的 window_size 结果范围。

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

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

相关文章

QTday5 2024-06-19

作业要求&#xff1a; 1.思维导图 2.整理代码&#xff1a;TCP服务器 作业1&#xff1a;思维导图 作业2&#xff1a;整理代码 运行代码&#xff1a; widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QList>…

力扣-最长连续序列

文章目录 题目题解解释代码 题目 原题链接&#xff1a;最长连续序列 题解 思路&#xff1a; 定义变量 res 用来记录最长连续序列的长度。对集合中的每个元素进行如下处理&#xff1a; 检查该元素是否是某个连续序列的起点&#xff08;即 num - 1 不在集合中&#xff09;。如…

90 岁老人靠一辆自行车年赚 170 亿,捷安特如何打造山地车极致产品力?

一位富家小开在中年时经商失败&#xff0c;38岁时从零开始创业&#xff0c;最终在自行车整车市场占据了70%的份额&#xff0c;他是怎么做到的&#xff1f; 一家曾为美国自行车品牌代工的台湾工厂&#xff0c;成功从ToB转型为ToC业务&#xff0c;从90%的代工业务转变为全球最大…

迅狐多商户直播商城系统源码:电商领域的创新融合

随着直播技术的兴起和电子商务的蓬勃发展&#xff0c;迅狐多商户直播商城系统源码应运而生&#xff0c;为商家和消费者提供了一个全新的互动购物平台。 多商户直播商城系统源码概述 迅狐多商户直播商城系统源码是一个高度集成的解决方案&#xff0c;它结合了直播的即时性和电…

进入容器修改内容_提交改变后的镜像_镜像保存成tar压缩包离线传输_镜像传输_镜像推送到公共仓库---分布式云原生部署架构搭建009

然后再来看,进入docker内部去看看. 用 docker exec -it imgid /bin/bash 这样就可以进入容器内部 而且关于,镜像的,内部放到什么地方了,都可以找到比如 在hub.docker的地址里面,找到nginx可以看到,对应的 /usr/share/nginx/html 可以看到这个路径. 然后去看看,进入到/usr…

浏览器(Browser):轻量级浏览器,高效浏览新体验

在可的哥桌面&#xff08;Codigger Desktop&#xff09;&#xff0c;我们始终秉持创新精神&#xff0c;致力于提供卓越的用户体验。如今&#xff0c;我们激动地宣布一项全新功能的发布——轻量级浏览器Browser。这款浏览器的推出&#xff0c;正是我们对用户体验追求的再次体现&…

C++:你用过MultiIndex容器吗?

作为C开发者&#xff0c;我们对键值容器非常熟悉&#xff0c;例如std::set、std::map、std::unordered_map等。这些容器以其强大的功能和高效的性能&#xff0c;成为我们处理数据存储和检索任务时的得力助手。但是你用过多键容器&#xff08;MultiIndex&#xff09;吗&#xff…

【大分享06】收、治、用、安“四管齐下”, 做好多业务系统电子文件归档与管理

关注我们 - 数字罗塞塔计划 - 本篇是参加由电子文件管理推进联盟联合数字罗塞塔计划发起的“大分享”活动投稿文章&#xff0c;来自上海泰宇信息技术股份有限公司&#xff0c;作者&#xff1a;金靓。 随着数字政府建设的深入推进以及“互联网政务服务”的快速发展&#xff0c…

TS安装及JS转换

第一步&#xff1a;先安装好node.js&#xff0c;可以在官方下载或我们提供压缩包直接安装好node.js Node.js的官方网站是下载 | Node.js 中文网 下载对应系统的版本。我们提供是64位。安装好后调出dos命名&#xff0c;输入 npm确认下有没有安装好。 第二步&#xff1a;安装cnpm…

初识 GPT-4 和 ChatGPT

文章目录 LLM 概述理解 Transformer 架构及其在 LLM 中的作用解密 GPT 模型的标记化和预测步骤 想象这样⼀个世界&#xff1a;在这个世界里&#xff0c;你可以像和朋友聊天⼀样快速地与计算机交互。那会是怎样的体验&#xff1f;你可以创造出什么样的应用程序&#xff1f;这正是…

【Linux进程】手把手教你如何调整----进程优先级(什么是优先级?为什么要有优先级?)

目录 一、前言 二、优先级的基本概念 &#x1f95d; 什么是优先级&#xff1f; &#x1f34d; 为什么要有优先级&#xff1f; 三、如何查看并修改 --- 进程优先级 &#x1f347; PRI and NI &#x1f525;PRI&#x1f525; &#x1f525;NI&#x1f525; &#x1f3…

关键属性描述ASYNC_REG

关键属性描述 属性信息 本章提供有关XilinxVivadoDesign Suite属性的信息。条目 每个属性包含以下信息&#xff08;如适用&#xff09;&#xff1a; •物业说明&#xff0c;包括其主要用途。 •支持该特性的Xilinx FPGA体系结构&#xff0c;包括UltraScale™ 架构设备&#xff…

【fiddler】fiddler抓取websocket

1.先了解websocket流 下载4.5版本以上的fiddler 如图所示&#xff1a;在rules--customize rules 里面插入以下代码&#xff1a; static function OnWebSocketMessage(oMsg: WebSocketMessage) { // Log Message to the LOG tab FiddlerApplication.Log.LogString(oMsg.ToStr…

docker最详细基础教程:如何在Win11中使用docker desktop

目录 前言 界面说明 设置界面翻译图一些简单说明 主要功能界面介绍 关于切换docker镜像源 如何拉取镜像 搜索镜像 创建容器 docker run命令的OPTIONS详细介绍 设置容器名词 以后台模式运行容器 端口映射 设置挂载卷 设置环境变量 容器退出后自动删除容器 容器…

如何将 ChatGPT 集成到你的应用中

在当今快速发展的技术环境中&#xff0c;将人工智能聊天解决方案集成到你的应用程序中可以显著提升用户体验和参与度。OpenAI 的 ChatGPT 以其对话能力和高级语言理解而闻名&#xff0c;对于希望在其应用程序中实现智能聊天功能的开发人员来说是一个绝佳的选择。那我们今天就来…

【jdk】jdk11 jdk17 jdk21的新特性

前言&#xff1a;按照博主的个人理解&#xff0c;一般来说 除了jdk8时代 说jdk8的新特性是特指jdk8这一个版本的特性&#xff0c;之后例如jdk11 jdk17新特性 都是泛特性 什么意思呢&#xff1f; 比如jdk11新特性&#xff0c;一般是指jdk9——jdk11 这一个泛版本的所有新特性&am…

MATLAB绘图技巧-多边形区域填充图

MATLAB绘图技巧-多边形区域填充图 以下内容来自&#xff1a;科学网—MATLAB绘图技巧-多边形区域填充图 - 彭真明的博文 (sciencenet.cn)START 为了突出某个区域或局部数据的特性&#xff0c;便于数据的可视化和解释&#xff0c;常需要绘制二维区域填充图。MATLAB提供了三种类型…

5G如何推动工业数字化转型?

据中国信息通信研究院测算&#xff0c;5G商用五年来&#xff0c;直接带动经济总产出约5.6万亿元&#xff0c;间接带动总产出约14万亿元&#xff0c;有力促进了经济社会高质量发展。而工业数字化转型&#xff0c;作为应对市场变革的关键战略&#xff0c;也借助5G技术卓越的高带宽…

海南聚广众达电子商务咨询有限公司抖音电商新标杆

在数字经济的浪潮中&#xff0c;抖音电商正成为一股不可忽视的力量。海南聚广众达电子商务咨询有限公司&#xff0c;作为专注于抖音电商服务的领军企业&#xff0c;凭借其专业的团队和创新的思维&#xff0c;不断助力商家在抖音平台上实现商业价值的最大化。 海南聚广众达电子…

几十块就能“开盒”,还能查祖上三代?

近期&#xff0c;网络上突然开始掀起一阵“人肉开盒”风波。明星、博主、素人无一幸免。 隐匿在此般恶劣行径背后的幕后黑手们无视法律、充满戾气。他们才不管你是什么人&#xff0c;只要有哪里让他们不愉快&#xff0c;那么不好意思&#xff0c;马上“开”你没商量。 这个“…