Elasticsearch:实用 BM25 - 第 1 部分:分片如何影响 Elasticsearch 中的相关性评分

news2024/11/26 19:52:59

作者:Shane Connelly 

 

背景

在 Elasticsearch 5.0 中,我们切换到 Okapi BM25 作为我们的默认相似度算法,这是用于对与查询相关的结果进行评分的算法。 在本博客中,我不会过多地介绍 BM25 与替代措施,但如果你想了解 BM25 的理论依据,你可以继续观看 Elastic{ON} 2016 的 BM25 Demystified 演示文稿。相反,我 我将为你介绍(并希望揭开神秘面纱)BM25 的实际用法,包括介绍可用的参数以及影响评分的因素。

请记住,此博客主要与那些对文本文档进行评分的人有关。 也就是说,它真正专注于帮助我们的搜索用户。 如果你正在索引日志或指标并返回按某些明确的元数据/数字顺序(如时间戳)排序的结果,则此博客可能主要用于满足好奇心。

了解分片如何影响评分

因为我希望你在家里也能跟进,所以我们需要解决的第一件事就是了解超过 1 个分片如何影响评分,因为默认情况下 Elasticsearch 每个索引使用 5 个主分片(请注意这个在最新的版本里有变化)。 让我们从创建一个名为 people 的索引开始。 我在此处提供的设置将是默认设置(因此无需定义),但为了演示目的,我无论如何都会这样做。 我将在这里使用我的名字的变体(“Shane Connelly”),但如果你在家跟随,请随时将其替换为你选择的名字。

PUT people
{
  "settings": {
    "number_of_shards": 5,
    "index": {
      "similarity": {
        "default": {
          "type": "BM25"
        }
      }
    }
  }
}

现在让我们添加一个文档,然后搜索它。 首先,我们只添加我的名字:

PUT /people/_doc/1
{
  "title": "Shane"
}

GET /people/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "title": "Shane"
    }
  }
}

上面搜索的结果为:

{
  "hits": {
    "hits": [
      {
        "_index": "people",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "title": "Shane"
        }
      }
    ]
  }
}

此时你得到 1 个命中,分数为 0.2876821。 我们稍后会深入探讨这个评分是如何得出的,但让我们先看看当我们添加更多带有我全名不同变体的文档时会发生什么。

PUT /people/_doc/2
{
  "title": "Shane C"
}

PUT /people/_doc/3
{
  "title": "Shane Connelly"
}

PUT /people/_doc/4
{
  "title": "Shane P Connelly"
}

现在再次进行相同的搜索:

GET /people/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "title": "Shane"
    }
  }
}

上面搜索的结果为:

{
  "hits": {
    "hits": [
      {
        "_index": "people",
        "_id": "3",
        "_score": 0.2876821,
        "_source": {
          "title": "Shane Connelly"
        }
      },
      {
        "_index": "people",
        "_id": "4",
        "_score": 0.2876821,
        "_source": {
          "title": "Shane P Connelly"
        }
      },
      {
        "_index": "people",
        "_id": "2",
        "_score": 0.2876821,
        "_source": {
          "title": "Shane C"
        }
      },
      {
        "_index": "people",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "title": "Shane"
        }
      }
    ]
  }
}

此时,你确实应该有 4 个命中,但如果你看分数,你可能会摸不着头脑。 文档 1,2,3,4 的得分均为 0.2876821。我们通过如下的方式来查看各个分片的文档情况:

GET /_cat/shards/people?v

上面述命令返回的结果为:

index  shard prirep state      docs store ip        node
people 0     p      STARTED       1 4.7kb 127.0.0.1 liuxgm.local
people 0     r      UNASSIGNED                      
people 1     p      STARTED       1 4.7kb 127.0.0.1 liuxgm.local
people 1     r      UNASSIGNED                      
people 2     p      STARTED       0  247b 127.0.0.1 liuxgm.local
people 2     r      UNASSIGNED                      
people 3     p      STARTED       1 4.6kb 127.0.0.1 liuxgm.local
people 3     r      UNASSIGNED                      
people 4     p      STARTED       1 4.6kb 127.0.0.1 liuxgm.local
people 4     r      UNASSIGNED                      

从上面显示的结果中,我们可以看出来,每个分片含有一个文档。其中的一个分片没有文档。

我们删除之前的所有文档,并再次写入各个文档:

DELETE people

PUT people
{
  "settings": {
    "number_of_shards": 5,
    "index": {
      "similarity": {
        "default": {
          "type": "BM25"
        }
      }
    }
  }
}

PUT /people/_doc/2?routing=1
{
  "title": "Shane C"
}

PUT /people/_doc/3
{
  "title": "Shane Connelly"
}

PUT /people/_doc/4?routing=1
{
  "title": "Shane P Connelly"
}

PUT /people/_doc/1
{
  "title": "Shane"
}

在上面,我们有意识地通过 routing 把有些文档写入到同一个分片中。我们运行如下的命令来进行查看:

GET /_cat/shards/people?v

上面的命令显示:

index  shard prirep state      docs store ip        node
people 0     p      STARTED       1 4.7kb 127.0.0.1 liuxgm.local
people 0     r      UNASSIGNED                      
people 1     p      STARTED       0  247b 127.0.0.1 liuxgm.local
people 1     r      UNASSIGNED                      
people 2     p      STARTED       0  247b 127.0.0.1 liuxgm.local
people 2     r      UNASSIGNED                      
people 3     p      STARTED       0  247b 127.0.0.1 liuxgm.local
people 3     r      UNASSIGNED                      
people 4     p      STARTED       3 9.7kb 127.0.0.1 liuxgm.local
people 4     r      UNASSIGNED                      

如上所示,这次,我们看到有一个分片含有 3 个文档,有一个分片含有一个文档。其它的三个分片不含有任何的文档。我们再次进行同样的搜索:

GET /people/_search?filter_path=**.hits
{
  "query": {
    "match": {
      "title": "Shane"
    }
  }
}

上面的命令返回的结果为:

{
  "hits": {
    "hits": [
      {
        "_index": "people",
        "_id": "3",
        "_score": 0.2876821,
        "_source": {
          "title": "Shane Connelly"
        }
      },
      {
        "_index": "people",
        "_id": "1",
        "_score": 0.16786805,
        "_source": {
          "title": "Shane"
        }
      },
      {
        "_index": "people",
        "_id": "2",
        "_score": 0.13353139,
        "_routing": "1",
        "_source": {
          "title": "Shane C"
        }
      },
      {
        "_index": "people",
        "_id": "4",
        "_score": 0.110856235,
        "_routing": "1",
        "_source": {
          "title": "Shane P Connelly"
        }
      }
    ]
  }
}

这通常会让新用户望而却步。 文档 2 和 3 非常相似 —— 它们都有 2 个词并且都匹配“ shane”,但是文档 2 的分数要低得多。 您可能会开始假设 “C” 的评分与 “Connelly” 的评分有所不同,但实际上这与文档如何落入分片有关。

提醒一下,Elasticsearch 将文档写入到不同的分片(shards)之中,每个碎片保存数据的一个子集。 这意味着术语 “shane” 的总出现次数在这些不同的分片中是不同的,这就是最终导致这种情况下分数差异的原因。 默认情况下,Elasticsearch 以每个分片为基础计算分数。

人们开始只将几个文档加载到他们的索引中并问 “为什么文档 A 的分数比文档 B 高/低”,有时答案是用户的分片与文档的比例相对较高,因此分数是倾斜的跨越不同的分片。 有几种方法可以跨分片获得更一致的分数:

1)加载到索引中的文档越多,分片的术语统计数据就越规范化。 如果文档足够多,你可能不会注意到术语统计数据的细微差异,因此不会注意到每个分片中的得分。

2)你可以使用较低的分片数来减少术语频率的统计偏差。 例如,如果我们在索引设置中将 number_of_shards 设置为 1,我们就会得到非常不同的分数。 我们会看到文档 1 的得分为 0.13245322,文档 2 和 3 的得分各为 0.105360515,文档 4 的得分为 0.0874691。 拥有不同数量的主分片需要权衡取舍,这在我们的定量集群大小网络研讨会中进行了讨论。

{
  "hits": {
    "hits": [
      {
        "_index": "people",
        "_id": "1",
        "_score": 0.13245323,
        "_source": {
          "title": "Shane"
        }
      },
      {
        "_index": "people",
        "_id": "2",
        "_score": 0.10536051,
        "_source": {
          "title": "Shane C"
        }
      },
      {
        "_index": "people",
        "_id": "3",
        "_score": 0.10536051,
        "_source": {
          "title": "Shane Connelly"
        }
      },
      {
        "_index": "people",
        "_id": "4",
        "_score": 0.0874691,
        "_source": {
          "title": "Shane P Connelly"
        }
      }
    ]
  }
}

3)你可以将 ?search_type=dfs_query_then_fetch 添加到请求中,它首先收集分布式术语频率(DFS = 分布式频率搜索),然后使用这些计算分数。 事实上,这会返回与只有 1 个分片相同的分数。 看看使用和不使用 “search_type” 参数的结果有何不同:

GET /people/_doc/_search?search_type=dfs_query_then_fetch
{
    "query": {
        "match": {
             "title": "Shane"
         }
      }
}

这与设置 number_of_shards=1 相同。 然后你可能会问,“好吧,如果这会产生更准确的分数,为什么默认情况下不打开它?” 答案是它在处理过程中增加了一次额外的往返以收集所有统计数据,对于某些用例(评分准确性不如速度重要),这种往返是不必要的。 此外,如果分片中有足够的数据,统计数据可以变得非常接近,从而也不需要往返。 如果你有足够的数据, search_type=dfs_query_then_fetch 只有在分片之间的数据继续分布不均时才最需要,就像一些自定义路由的情况一样。        

好的,现在我们了解了分片如何影响我们的评分(以及如何针对它进行调整)。 接下来,我们将研究 BM25 算法,看看不同的变量是如何发挥作用的。

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

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

相关文章

3天没睡吐血整理,性能测试瓶颈问题+分析,一篇概全...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 如何定位性能瓶颈…

白鲸优化算法优化VMD参数,并提取特征向量,以西储大学数据为例,附MATLAB代码

大家看到这篇文章,肯定会有疑问,难道本篇文章和上一篇文章不是一个意思嘛,这是来凑数的嘛……其实不然,如果各位读者仔细看,就会发现本篇文章和上一篇文章大有不同,这篇文章也是我一直以来想在上一篇文章基…

TALL论文笔记

TALL论文笔记 0.论文来源1摘要2引言3模型结构3.1视觉编码器3.2句子编码器3.3模态融合3.4时间定位回归网络 4训练4.1损失函数4.2采集训练样本 5 评估5.1数据集5.2评价指标5.3实验结果 0.论文来源 2017 TALL 1摘要 问题描述:通过语言来对未修剪视频中动作的时间定位…

【JVM】日志分析工具--gcviewer的使用

文章目录 gcviewer是什么?gcviewer的使用最后 gcviewer是什么? GCViewer是一个小工具,可以可视化Sun / Oracle、IBM、HP和BEA Java虚拟机生成的详细GC输出。它是在GNU LGPL下发布的自由软件。—官网翻译 gcviewer的使用 文章使用的配置 工具…

Dubbo面试题

Dubbo 基础知识为什么要用 Dubbo?Dubbo 是什么?Dubbo 的使用场景有哪些?Dubbo 核心功能有哪些? 架构设计Dubbo 服务器注册与发现的流程?Dubbo 的整体架构设计有哪些分层?Dubbo Monitor 实现原理? 分布式框…

一键部署通义千问预体验丨阿里云云原生 5 月动态

云原生月度动态 云原生是企业数字创新的最短路径。 《阿里云云原生每月动态》,从趋势热点、产品新功能、服务客户、开源与开发者动态等方面,为企业提供数字化的路径与指南。 本栏目每月更新。 01 趋势热点 🥇 Apache RocketMQ 入选可信开…

如何创建 GitHub 配置文件自述文件

背景 最近再搞GitHub Actions的东西。突然看到了 github推出的这项服务。就搞了一下。主要目的:装扮自己的GitHub主页。俗称装逼(小声) 步骤 创建一个与 GitHub 用户名同名(包括大小写)的新仓库。比如我的就是 创建…

Docker学习(一)

第1章 docker简介 1.1 什么是docker Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 …

ethers.js常用的操作方法

安装:npm install --save5.7.2 ethers --ethers比web3来说使用方式相对简洁很多,主要就操作三个大对象 1. Provider,(提供者)是一个用于连接以太坊网络的抽象类,提供了只读形式来访问区块链网络和获取链上…

8年测试老鸟总结,性能测试案例经验,高级测试之道...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 性能基准测试&…

测试员最令程序员讨厌的十种行为,你中了几条?

在做软件测试这些年,总会有程序员给我吐槽某某测试员令其发指的一些事。这里我总结了一些,看你有没有中呢? 1、爱较真,只要逮着问题,哪怕是技术文档中的错别字也要提到bug管理工具中,更让程序员绝望的是&a…

测试员与程序员那些烂在心里的共同秘密

1、生产环境出现了一个较严重的bug,赶在用户没发现的情况下,测试员与程序员悄悄地改了,并跳过了N个步骤做了次发布。 2、程序员告诉测试员,新来的技术leader每天做得最多的事就是copy他们的代码,一份不够还再来一份&a…

VR全景智慧园区,沉浸式数字化体验,720度全视角展示

导语: 随着科技的迅猛发展,虚拟现实(Virtual Reality,简称VR)全景技术已经成为了人们趋之若鹜的新兴领域。 而城市园区作为现代社会的重要组成部分,也正在积极寻求创新的方式来吸引更多的人流和投资。 一&…

【QQ界面展示-设置文本框的leftView属性 Objective-C语言】

一、这个搞定以后,接下来,就实现另外一个效果, 1.注意,我们上午把这个文本框,是不是本来说文本框,直接放这儿就OK了吧,但是,我们上午为了能让这个文本框兼容iOS6,是不是把这个文本框的边框,设为None,然后呢,边框设为nil边框,是不是设了一个背景图啊, 但我们做完…

Ansys Zemax | 如何在OpticStudio中建模和设计真实波片

本文介绍了如何在 OpticStudio 中建模和设计真实的单色和消色差波片。它将演示如何使用双折射材料,通过构建评价函数来计算相位延迟,并使用 Universal Plot 将相位延迟与波片厚度的关系可视化。(联系我们获取文章附件) 双折射材料…

搜索树的逻辑以及代码实现-重点删除结点的逻辑

目录 搜索二叉树概念 代码 二叉搜索树的插入 二叉搜索树的查找 二叉搜索树的删除(非常重要) a、b、c情况:删除14 d情况 打印结点中序遍历 整体代码:(有R开头的就是递归写法,逻辑一样) 搜…

合宙Air724UG Cat.1模块硬件设计指南--UART接口

UART接口 简介 串行接口简称串口,同时串口又分为同步收发串口与异步收发串口两种方式,而CAT1模块支持的串口为异步收发方式,简称UART口。串口作为设备的外部接口,是调试开发的一种重要的开发手段。 特性 UART1串口 包括数据线TXD…

ubuntu (Linux mint) 安装mysql 5.7

1 从官网下载deb格式安装包 网址: https://downloads.mysql.com/archives/community/ 2 解压安装包 执行命令: tar -xvf mysql-server_5.7.41-1ubuntu18.04_amd64.deb-bundle.tar 如下图所示: 你会发现多了很多.deb文件。 3 批量安装 .d…

当C/C++遇上高性能计算|会C/C++编程的看过来,这类岗位是真香

C/C编程语言作为经典而强大的编程语言,一直在计算机科学领域占据着重要的地位。而当C/C遇上高性能计算,这个组合将带给你无尽的机遇和挑战,成为一名高性能计算工程师,你会发现这类岗位是真香。从此告别爹不疼、娘不爱的尴尬境地。…

【RH850/U2A】:Task激活过程

Task激活过程 Autostart非AutostartTASK(Default_Init_Task)EcuM_StartupTwo(void)SchM_Init(void)BswM_Init(xx)Rte_Start(void)Task激活过程,分自动运行(AutoStart)和非自动运行。 Autostart 在DavinciCfg中的配置如下: Autostart意味作在执行StartOS()后就开始运行了。…