浅析 ElasticSearch 默认算法 —— BM25

1.背景

最近在做 ES 相关东西,只最会在查询的时候给不同的字段设置不同的权重,但是得分具体怎么算的不太明白,花了4-5 天研究和总结了一下。这样不至于被别人问到“这个分数怎么算出来的?”,两眼一抹黑,不知其所以然,所以自我总结了下;

2.准备

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

  • 我创建了一个 book 索引,是一个分片一个副本,有三条数据;我 match 查询 java 时, java 编程思想 的得分 0.5619609,深入理解 Java 虚拟机得分 0.40390933;(分片会影响得分,请先使用一个片,后面会给出解释)

3.详情

  • 使用 explain 参数查询会给出得分计算过程
    在这里插入图片描述
  • 把 detail 全拿出来就是
"details" : [
            {
              "value" : 0.5619609,
              "description" : "score(freq=1.0), computed as boost * idf * tf from:",
              "details" : [
                {
                  "value" : 2.2,
                  "description" : "boost",
                  "details" : [ ]
                },
                {
                  "value" : 0.47000363,
                  "description" : "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
                  "details" : [
                    {
                      "value" : 2,
                      "description" : "n, number of documents containing term",
                      "details" : [ ]
                    },
                    {
                      "value" : 3,
                      "description" : "N, total number of documents with field",
                      "details" : [ ]
                    }
                  ]
                },
                {
                  "value" : 0.54347825,
                  "description" : "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
                  "details" : [
                    {
                      "value" : 1.0,
                      "description" : "freq, occurrences of term within document",
                      "details" : [ ]
                    },
                    {
                      "value" : 1.2,
                      "description" : "k1, term saturation parameter",
                      "details" : [ ]
                    },
                    {
                      "value" : 0.75,
                      "description" : "b, length normalization parameter",
                      "details" : [ ]
                    },
                    {
                      "value" : 3.0,
                      "description" : "dl, length of field",
                      "details" : [ ]
                    },
                    {
                      "value" : 5.0,
                      "description" : "avgdl, average length of field",
                      "details" : [ ]
                    }
                  ]
                }
              ]
            }
          ]

3.1 粗略查看 ES 得分详情

  • 我们发现第四行好像就是公式 ,而且与 details 数组的元素对应
    在这里插入图片描述
  • 而且每个元素都有一个 value 值,把 value 值带进公式,正好等于 0.5619608507173045
    在这里插入图片描述

3.2 详细查看 es 得分

  • 第一项 2.2 不用解释,是 es 默认的一个常数,我们看第二项 idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:和第三项 tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:
  • 第二项截图
    在这里插入图片描述
    小 n :number of documents containing term,翻译一下,包含词项(也就是 java) 的文档数量,因为我们查询的是 name 字段,所以就是所有文档中 name 字段,包含 java 的文档数量,即 Java 编程思想 和***深入理解 Java 虚拟机*** ,vulue 等于 2 。
    大 N :total number of documents with field,翻译一下,有 name 这个字段的文档的数量,我们的三个文档都有 name 这个字段,即 Java 编程思想深入理解 Java 虚拟机Spring 5 个核心原理 value 等于 3 。
  • log(1 + (N - n + 0.5) / (n + 0.5)) = log(1 + (3 - 2 + 0.5) / (2 + 0.5) = log(1 + 1.5 / 2.5) = log1.6 =0.47000363,取的是自然数的对数,如图:
    在这里插入图片描述

3.2 第三项

  • 第三项截图
    在这里插入图片描述
    freq:occurrences of term within document,翻译一下,freq 是 frequency 的简写,频率的意思,即词项(java)在 ***Java 编程思想***发生的频率, value 等于 1.
    k1:term saturation parameter,翻译一下,词项饱和度参数,value 是一个常数 1.2
    b:length normalization parameter,翻译一下,长度规格化参数,value 是一个常数 0.75
    dl:length of field,翻译一下,“字段”的长度,这里长度可不是 Java 编程思想.length()***,而是***Java 编程思想***能分成多少个词,看下面 3.3 截图,
    avgdl:average length of field,翻译一下,“字段”的平均长度,同样也不是***Java 编程思想 、 深入理解 Java 虚拟机、Spring 5 个核心原理.length()/3
    ,而是***Java 编程思想 、 深入理解 Java 虚拟机、Spring 5 个核心原理***分词后的长度除以 3,***3***是有name 字段的数量,也看下面 3.3 截图

3.3 ik 分词器分词

在这里插入图片描述

  • Java 编程思想 、 深入理解 Java 虚拟机、Spring 5 个核心原理,分词后词项太多截图不全,我放到 JSON 解析器下,可以看到 数量是 15 ,平均数量是 5
    在这里插入图片描述
    在这里插入图片描述
  • freq / (freq + k1 * (1 - b + b * dl / avgdl) = 1 /(1 + 1.2 * (1 - 0.75 + 0.75 * 3 /5)) = 1 / (1 + 1.2 * (0.25 + 0.75 * 0.6)) = 1 / (1 + 1.2 * 0.7) = 1 / 1.84 = 0.54347826 约等于0.54347825,不纠结那 0.00000001至此,得分的所有项解读完毕,另一个查询结果“深入理解 Java 虚拟机”也可以按照这个方式计算出来

4.公式解释

  • 已经知其然了,现在看知其所以然。主角就是 BM25 算法,公式如下
    在这里插入图片描述
  • 我们在 kibana 上 explain 后公式的第二项和第三项分别对应这个公式的这两部分:
    在这里插入图片描述
  • 倒写的 3,表示求和,就是***查询项***分词后,每个分词都要计算分数,把每个查询词项的分数相加,比如我查询的是 “Java Spirng”,那么会把 Java 得分计算出来,再把 Spring 得分计算出来,然后相加;
  • 第二项,原谅我打不出这个公式,就是上面截图中上面两个箭头的第一个, idf 是 inverse document frequency 的简写,翻译一下,逆文档频率,详情是 log(1 + (N - n + 0.5) / (n + 0.5));如果小 n 趋近于 大 N,那么整个公式值越小,大家可以把 N 固定为 3,然后小 n 分别为 1、2、3 时,换算下是不是 小 n 越大,计算结果越小。翻译成人话就是,当一个词在所有文档中都出现了,那么它显得不那么重要,得分就低了。
  • 第三项,上面截图中上面两个箭头的第二个,如果说第二项是各个文档之间的纵向的比较,那么第三项更倾向于定位某个文档后的横向比较,
  • **第三项(1),比较频率,即这个词项出现的次数 ,ES 的的公式是freq / (freq + k1 * (1 - b + b * dl / avgdl)),比 BM25 分子少乘了一个 (k1 + 1)有区别,但差别不大;因为分母 k1 * (1 - b + b * dl / avgdl))可以看作是一个常数,那么当 freq 越大时,freq / (freq + k1 * (1 - b + b * dl / avgdl))值越大,但是最大值不超过 1,是无限趋近于 1 的数,当 freq 越小时,计算结果越小,翻译成人话也好理解,词项频率越高,得分越高。
  • **第三项(2),比较词项占比,这里的此项是 name 字段分词后的个数除以所有 name 字段分词平均值,当 dl越大时分母越大,得分越小,翻译成人话是,比如我的 name 有一千个字且包含 java ,其他文档是十个字且包含 Java,那么我一千个字里面有个 Java 显得没那么重要。

4.分片情况下

如果你按照我上面计算时,发现中分词数量计算不对,那么很可能是你有多个分片,ES 不会把同一个字段各个分片的内容统一计算,而是每个片单独计算得分后就排名了,你可以通过指定 routing 来固定某个分片,验证得分结果。
在这里插入图片描述

5.前人的解读,帮助很大

bm25算法详解-bilibili
Elasticsearch BM25相关度评分算法超详细解释

6.一个不太成熟的使用

  • 我项目中有一个索引,有两个字段,分别记录用户搜索的内容 searchFor 和这个内容被搜索的次数 count,我想要把 count 也融入得分公式中,ES 默认的得分记作 _score,我本想把 count 按 log10 取对数变成 _score*log10(count),但是当 count 超过很大时会把 _score的得分放大很多倍,不同次数的文档得分跨度也比较大;
  • 研究 ES 的得分步骤后,我发现我的次数和第三项 freq / (freq + k1 * (1 - b + b * dl / avgdl))特别像,count 没有字段长度的比较所以我直接把关于长度 b(饱和度)dl/avgdl去掉了,改成 count / (count+k1) = count / (count + 1.2),最终得分是 _score*count / (count + 1.2)
  • 当然这样算不一定成熟,但是达到我的效果了,不同次数的文档得分跨度也变小了;因为我工作中的测试数据量不够,这个类 BM25的公式可能也不能这么硬套,要经过实际数据测试及业务需求匹配度验证后才能下定论,这只是一个不太成熟但是有点道理的使用,希望大家多留言讨论
<think>好的,我现在需要详细解释BM25算法的介绍及实现原理。首先,用户提供了三个引用资料,我需要仔细分析这些内容,确保回答的准确性。 根据引用[2],BM25是基于概率检索框架的算法,由Robertson等人提出,最初在OKapi系统中实现。所以开头应该介绍BM25的基本概念和应用场景,比如信息检索中的文档相关性评分。 接下来,引用[3]提到BM25计算query和文档相关性的三个部分:单词与文档的相关性、单词与query的相关性、单词权重。需要将这些部分拆解开来,逐一说明。比如,词频(TF)、逆文档频率(IDF)、文档长度归一化这些要素。 然后,引用[1]详细列出了BM25的四个组成部分:词频、逆文档频率、长度归一化和评分公式。我需要将这些技术点整合到回答中,可能需要用数学公式展示,并解释每个参数的作用,比如k1和b的意义。 在实现原理部分,需要分步骤说明,比如预处理、计算各组件、综合评分。这里要结合引用中的示例,可能需要用LaTeX公式展示BM25的公式,确保格式正确,如使用$$...$$。 然后,关于在ElasticSearch中的应用,引用[1]提到参数调整和混合搜索,这里需要简要说明如何调整k1和b,以及与其他算法的结合使用。 最后,生成相关问题时要覆盖原理、优化、应用、对比等,参考用户示例中的结构,确保问题有针对性且相关。 需要检查是否所有数学表达式都正确使用$或$$,比如词频部分可能用行内公式,而BM25公式用独立公式。同时,确保回答结构清晰,分点阐述,引用标识正确添加。</think>### BM25算法介绍及实现原理 #### 一、BM25算法简介 BM25(Best Match 25)是一种基于概率检索模型的相关性评分算法,主要用于信息检索系统中评估搜索词(query)与文档(document)之间的相关性[^2]。它由Stephen E. Robertson等人提出,并在OKapi系统中首次实现,因此也称为**OKapi BM25**。该算法结合了词频(TF)、逆文档频率(IDF)和文档长度归一化,能够有效平衡搜索结果的准确性与多样性。 --- #### 二、BM25实现原理 BM25的核心思想是通过以下三个组件计算相关性得分: 1. **词频(TF)** 衡量单词在文档中的出现频率,词频越高得分越高,但增长趋势会通过饱和函数控制,公式为: $$\text{TF}_{\text{BM25}} = \frac{(k_1 + 1) \cdot \text{tf}}{k_1 \cdot \left(1 - b + b \cdot \frac{\text{dl}}{\text{avgdl}}\right) + \text{tf}}$$ 其中: - $\text{tf}$:单词在文档中的词频 - $\text{dl}$:文档长度 - $\text{avgdl}$:语料库中文档的平均长度 - $k_1$(默认1.2)和$b$(默认0.75)为可调参数,控制词频饱和度和长度归一化强度[^1]。 2. **逆文档频率(IDF)** 衡量单词在整个语料库中的区分能力,公式为: $$\text{IDF} = \log\left(\frac{N - n + 0.5}{n + 0.5} + 1\right)$$ - $N$:文档总数 - $n$:包含该单词的文档数 3. **长度归一化** 通过参数$b$调节文档长度对得分的影响,避免长文档因包含更多单词而天然占优。 **最终BM25评分公式**: $$\text{BM25}(D, Q) = \sum_{i=1}^{n} \text{IDF}(q_i) \cdot \text{TF}_{\text{BM25}}(q_i, D)$$ --- #### 三、BM25算法实现步骤 1. **预处理** - 对文档和query进行分词、停用词过滤。 2. **计算IDF值** 统计每个单词的$n$(包含该词的文档数)和$N$(总文档数)。 3. **计算词频与长度归一化** 根据文档长度和参数$k_1, b$调整词频权重。 4. **综合得分** 对query中的每个单词计算$\text{IDF} \cdot \text{TF}$,并累加得到最终相关性得分。 --- #### 四、BM25ElasticSearch中的应用 在ElasticSearch中,BM25默认的相似度评分算法: - **参数调整**:通过修改`k1`和`b`优化结果(例如`"similarity": {"type": "BM25", "k1": 1.5}`)。 - **混合搜索**:结合布尔模型或语义搜索模型提升效果。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值