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

警告:此功能处于技术预览阶段,可能会在未来版本中更改或删除。语法可能会在正式发布之前发生变化。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.mfbz.cn/a/740184.html

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

相关文章

Ltv 数据粘包处理

测试数据包的生成 校验程序处理结果和原始的日志保温解析是否一致 程序粘包分解正常

Java数据结构4-链表

1. ArrayList的缺陷 由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景…

OS中断机制-外部中断触发

中断函数都定义在中断向量表中,外部中断通过中断跳转指令触发中断向量表中的中断服务函数,中断指令可以理解为由某个中断寄存器的状态切换触发的汇编指令,这个汇编指令就是中断跳转指令外部中断通过在初始化的时候使能对应的中断服务函数如何判断外部中断被触发的条件根据Da…

【zip密码】忘了zip密码,怎么办?

Zip压缩包设置了密码,解压的时候就需要输入正确对密码才能顺利解压出文件,正常当我们解压文件或者删除密码的时候,虽然方法多,但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么,忘记了zip压缩包的密…

Windows资源管理器down了,怎么解

ctrlshiftesc 打开任务管理器 文件 运行新任务 输入 Explorer.exe 资源管理器重启 问题解决 桌面也回来了

vue如何引入图标

方法1&#xff1a;iconify/vue pnpm add iconify/vue -D 网址&#xff1a;https://icon-sets.iconify.design/ 使用哪个需要安装 如下截图,安装指令&#xff1a; > npm install iconify/icons-gg在使用的页面引入 import { Icon } from “iconify/vue”; <template>…

LabVIEW与C#相互调用dll

C#调用LabVIEW创建的dll 我先讲LabVIEW创建自己的.net类库的方法吧&#xff0c;重点是创建&#xff0c;C#调用的步骤&#xff0c;大家可能都很熟悉了。 1、创建LabVIEW项目&#xff0c;并创建一个简单的add.vi&#xff0c;内容就是abc&#xff0c;各个接线端都正确连接就好。 …

机器学习之逻辑回归丨KNN测试

选择题 【 正确答案: A D】 A. B. C. D. 【 正确答案: B】 A. B. C. D. 【 正确答案: C, D】 A. B. C. D. 假设我们三个类别中心&#xff0c;若某测试样本为&#xff0c;它的 c ( i ) c^{(i)} c(i)是多少&#xff1f; 【 正确答案: B】 A.1 B.2 C.3 D.不确定 假设你…

UE5 场景物体一键放入蓝图中

场景中&#xff0c;选择所有需要加入到蓝图的模型或物体。 点击 蓝图按钮&#xff0c;点击“将选项转换为蓝图” 在创建方法中&#xff0c;选择“子Actor”或着 “获取组件” 如果需要保持相对应的Actor的父子级别&#xff08;多层&#xff09;&#xff0c;那么选择“获取组件…

如何在Linux下使用git(几步把你教会)

目录 一、注册github账号 二、新建项目 1.点击右上角自己的头像&#xff0c;然后点击Your repositories。 2.点击New。 3.配置新项目信息。 4.点击Create repository即可成功创建。 三、安装git 四、配置git 五、初始化git仓库 1.先进入想要使用git的目录。 2.初始化…

SD-WAN是什么?它有哪些应用领域?

随着企业业务的不断扩展和数字化转型的加速&#xff0c;传统网络架构已无法满足企业对高效、灵活和安全网络连接的需求。在此背景下&#xff0c;SD-WAN&#xff08;软件定义广域网&#xff09;应运而生&#xff0c;为企业带来了全新的网络连接体验。本文将详细介绍SD-WAN网络及…

vue音乐播放条

先看效果 再看代码 <template><div class"footer-player z-30 flex items-center p-2"><div v-if"isShow" class"h-12 w-60 overflow-hidden"><div :style"activeStyle" class"open-detail-control-wrap&…

Calibre - 翻译电子书(Ebook Translator)

本文参考教程 &#xff1a;https://bookfere.com/post/1057.html 使用 Ebook Translator 插件&#xff0c;详见&#xff1a; 官网&#xff1a;https://translator.bookfere.comgithub &#xff1a;https://github.com/bookfere/Ebook-Translator-Calibre-Plugin 一、基本翻译 …

【已解决】手机进入fastboot无法退出

文章目录 报错及效果图报错代码效果图 解决方案必要的解决方法可能有用的解决方法 报错及效果图 报错代码 手机屏幕显示fastboot&#xff0c;长按电源键无法正常启动 效果图 解决方案 必要的解决方法 1.在电脑上下载并安装adb/fastboot驱动&#xff0c;可以在这里免费下载&…

重学java 83.Java注解

As a failure,I met my last sound. —— 24.6.24 一、注解的介绍 1.引用数据类型: 类、数组、接口、枚举、注解 jdk1.5版本的新特性 一个引用数据类型 和类,接口,枚举是同一个层次的 引用数据类型:类、数组、接口、枚举、注解 2.作用: ① 说明&#xff1a;对代码进行说明,生…

视频格式转换方法:如何使用视频转换器软件转换视频

众所周知&#xff0c;目前存在许多不同的视频和音频格式。但我们的媒体播放器、移动设备、PC 程序等仅兼容少数特定格式。例如&#xff0c;如果不先将其转换为 MP4、MOV 或 M4V 文件&#xff0c;AVI、WMV 或 MKV 文件就无法在 iPhone 上播放。 视频转换器允许您将一种视频格式…

2024年经济与国际贸易国际会议(ICEIT 2024)

2024年经济与国际贸易国际会议&#xff08;ICEIT 2024&#xff09; 2024 International Conference on Economy and International Trade 【重要信息】 大会地点&#xff1a;温州 大会官网&#xff1a;http://www.iciceit.com 投稿邮箱&#xff1a;iciceitsub-conf.com 【注意…

cityscapes数据集转换为COCO数据集格式【速来,我吃过的苦,兄弟们就别再吃了】

利用CityScapes数据集&#xff0c;将其转换为COCO格式的实例分割数据集 – – – 进而再训练出新的YOLOv8-seg模型 写个前言&#xff1a; 人嘛&#xff0c;总想着偷点懒&#xff0c;有现成的数据集&#xff0c;就得拿来用&#xff0c;是吧&#xff1f;确实是这样。 接下来的步…

如何使用mapXplore将SQLMap数据转储到关系型数据库中

关于mapXplore mapXplore是一款功能强大的SQLMap数据转储与管理工具&#xff0c;该工具基于模块化的理念开发&#xff0c;可以帮助广大研究人员将SQLMap数据提取出来&#xff0c;并转储到类似PostgreSQL或SQLite等关系型数据库中。 功能介绍 当前版本的mapXplore支持下列功能…

贪心算法系列(二)|摆动序列最长递增子序列|买卖股票的最佳时机|买卖股票的最佳时机II

摆动序列 分析 最经典的做法还是使用两个dp表的动态规划(代码放下面)这里采用贪心算法&#xff0c;直接上结论整个序列中&#xff0c;波峰波谷起点和重点的个数就是整个最长的摆动序列长度 那么如何判断波峰/波谷呢&#xff1f;也很简单left nums[i] - nums[i-1]right nu…