ElasticSearch 如何计算得分及一个不太成熟的使用

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 个核心原理分词后的长度除以 33是有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的公式可能也不能这么硬套,要经过实际数据测试及业务需求匹配度验证后才能下定论,这只是一个不太成熟但是有点道理的使用,希望大家多留言讨论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值