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

作者: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 算法,看看不同的变量是如何发挥作用的。

请继续阅读第二部分 “Elasticsearch:实用 BM25 - 第 2 部分:BM25 算法及其变量”。

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

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

相关文章

【备战秋招】每日一题:4月23日美团春招第一题:题面+题目思路 + C++/python/js/Go/java带注释

为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第一题-申请奖学金 在线评测链接:P1245 题目内容 塔子哥是一个热爱学习的大学生,他的梦想是成为一名优秀的算法竞赛高手。为了实现自己的梦想,他需…

Vue中v-text、v-html、v-on的基本语法(二)

文章目录 前言一、vue中data属性定义对象、数组相关数据二、v-text、v-html指令使用三、v-on基本指令使用(一)四、v-on指令基本使用(二)之在函数中获取vue实例本身this五、v-on指令基本使用(二)之在函数中传递参数六、v-on指令基本使用(二)之简化写法绑定函数和事件定义的两种写…

关于VPN的一些总结和理解

关于VPN的一些总结和理解 前言一、VPN的概述二、VPN的原理2.1 原理概述2.2 虚拟网卡2.3 点对点隧道的建立 三、其他3.1 vpn和vlan的区别?3.2 vpn和web代理的关系? 参考 前言 同样的机缘巧合,最近看了一些关于vpn的内容,总结一下&a…

go+vue自建运维管理平台

文章目录 鲁班运维平台容器管理集群管理namespace管理节点管理工作负载存储管理网络管理配置管理事件中心 kuboard 鲁班运维平台 这个平台和spug很像,感觉就像是spug运维平台的容器版本。 但是如果是容器平台则选择的余地很大,优秀的如kubersphere、kub…

LeetCod刷题笔记

目录 2739.总行驶距离 思路:模拟 代码 6890.找出分区值 思路:急转弯 代码: 1254.统计封闭岛屿的数目​编辑 思路:DFS 代码: 6447.给墙壁刷油漆 思路:动态规划 代码: 思路:状态DP 代码&…

Rust in Action笔记 第四章生命周期、所有权、借用

第四章用了一个行星通信的例子来阐述整个主题,主要角色有地面站(Ground station)、人造卫星(CubeSat),两者有不同的状态并且能互相发消息通信; Rust有类型安全(type safety&#xf…

WinDbg安装入坑1(C#)

由于作者水平有限,如有写得不对的地方,请指正。 使用WinDbg的过程中,坑特别的多,对版本要求比较严格,如: 1 32位应用程序导出的Dump文件要用32位的WinDbg打开,想要没有那么多的问题&#xff…

传统机器学习算法解析(opencv实现)

前言 文本主要解析在传统机器学习当中一些小的算法与思想,只是传统机器学习算法当中的一小部分,更多传统机器学习算法可参考我的另外几篇博客 链接1: PCA主成分分析 链接2: Canny边缘检测算法 链接3: K-Means聚类算法 链接4: SIFT算法分析 1. opencv …

农村饮水安全政策要求与解决措施

农村饮水安全,是指农村居民能够及时、方便地获得足量、洁净、负担得起的生活饮用水。农村饮水安全包括水质、水量、用水方便程度和供水保证率4项评价指标。 一、农村饮水安全问题 农村饮水安全问题一直是农村发展的重要问题。在过去,由于农村供水设施落…

Linux之多线程(下)——线程控制

文章目录 前言一、POSIX线程库1.概念2.pthread线程库是应用层的原生线程库3.错误的检查 二、线程控制1.创建线程——pthread_createpthread_create函数例子创建一个新线程主线程创建一批新线程 2.获取线程ID——pthread_self3.线程等待——pthread_join4.线程终止——return、p…

Flutter的状态管理之Provider

Provider简介 Flutter Provider是Flutter中一个非常流行的状态管理库,它可以帮助开发者更加方便地管理应用程序中的状态。Provider提供了一种简单的方式来共享和管理应用程序中的数据,并且可以根据数据的变化来自动更新UI界面。 Provider的核心思想是将…

C# 自动更新(基于FTP)

效果 启动软件后,会自动读取所有的 FTP 服务器文件,然后读取本地需要更新的目录,进行匹配,将 FTP 服务器的文件同步到本地 Winform 界面 一、前言 在去年,我写了一个 C# 版本的自动更新,这个是根据配置文…

qt学习——基本使用、对象树、按钮、信号与槽

初识qt **qt****qt命名规范以及相关快捷键的使用****QPushButton****对象树****点击按钮关闭窗口****信号和槽****标准的信号和槽****自定义信号和槽****带参数的自定义信号和槽传参以及函数的二义性问题****信号和槽的拓展****qt4的信号与槽****QDebug的输出转义问题****lambd…

STM32 Proteus仿真自动刹车系统超声波测距电机控制-0042

STM32 Proteus仿真自动刹车系统超声波测距电机控制-0042 Proteus仿真小实验: STM32 Proteus仿真自动刹车系统超声波测距电机控制-0042 功能: 硬件组成:STM32F103C6单片机 LCD1602显示器HCSR04超声波传感器按键(加 减)电机蜂鸣器 1.单片机…

Qt编写视频监控系统76-Onvif跨网段组播搜索和单播搜索的实现

一、前言 在视频监控行业一般会用国际onvif工具来测试设备是否支持onvif协议,工具的名字叫ONVIF Device Manager(还有个工具叫ONVIF Device Test Tool,专用于程序员测试各种数据交互),可以自行搜索下载,此…

04-编织灵魂旋律:Golang 函数的魔力绽放

📃个人主页:个人主页 🔥系列专栏:Golang基础 💬Go(又称Golang)是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性,拥有高效的并发编程能力和简洁的语法。G…

通过共享内存进行通信(嵌入式学习)

通过共享内存进行通信 概念特点函数示例代码 概念 在Linux中,共享内存是一种进程间通信(IPC)机制,允许多个进程共享同一块内存区域。这种通信方式可以提供高效的数据传输,特别适用于需要频繁交换数据的场景。 IO间进…

为CentOs配置静态IP

目录 第一步:查看物理机IP 第二步:虚拟机网络设置 点击虚拟机->编辑虚拟机设置 第三步:CentOS网络配置文件 第四步:重启网络 第五步:测试网络 为什么要设置静态IP 在安装好CentOS虚拟机以后,一般我…

程序替换原理

文章目录 一、程序替换 一、程序替换 程序替换用于将当前进程的用户空间的代码和数据全部替换为新程序的代码和数据,程序替换不会创建新进程,而是用当前进程执行新程序的代码,fork 创建子进程后,子进程默认执行的是父进程的代码&…

vue2和vue3的渲染过程简述版

文章目录 vue2渲染过程vue3渲染过程优化和扩充 vue2和vue3对比 vue2渲染过程 在Vue 2的渲染过程中,包括以下几个关键步骤: 解析模板:Vue 2使用基于HTML语法的模板,首先会将模板解析成抽象语法树(AST)&…