Elasticsearch——评分机制详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch——评分机制详解相关的知识,希望对你有一定的参考价值。

参考技术A

一个搜索引擎使用的时候必定需要排序这个模块,如果在不选择按照某一字段排序的情况下,都是按照打分的高低进行一个默认排序的,所以如果正式使用的话,必须对默认排序的打分策略有一个详细的了解才可以,否则被问起来为什么这个在前面,那个在后面?

将查询作为输入,将每一个因素最后通过公式综合起来,返回该文档的最终得分。这个综合考量的过程,就是将相关的文档被优先返回的考量过程。

Elasticsearch是基于Lucene的,所以它的评分机制也是基于Lucene的。在Lucene中把这种相关性称为得分(score),确定文档和查询有多大相关性的过程被称为打分(scoring)。

ES最常用的评分模型是 TF/IDF和BM25,TF-IDF属于向量空间模型,而BM25属于概率模型,但是他们的评分公式差别并不大,都使用IDF方法和TF方法的某种乘积来定义单个词项的权重,然后把和查询匹配的词项的权重相加作为整篇文档的分数。

在ES 5.0版本之前使用了TF/IDF算法实现,而在5.0之后默认使用BM25方法实现。

relevance score相关性算分:简单来说,就是计算出,一个索引中的文本,与搜索文本,他们之间的关联匹配程度。

通过倒排索引可以获取与查询语句相匹配的文档列表,那么如何将最符合用户查询需求的文档放到前列呢?

本质是一个排序问题,排序的依据是相关性算分。

Elasticsearch使用的是 term frequency/inverse document frequency算法,简称为TF/IDF算法。TF词频(Term Frequency),IDF逆向文件频率(Inverse Document Frequency)

相关性算分的几个重要概念如下:

ES目前主要有两个相关性算分模型,如下:

BM25中的IDF公式为:

原版BM25的log中是没有加1的,Lucene为了防止产生负值,做了一点小优化。虽然对公式进行了更改,但其实和原来的公式没有实质性的差异,下面是新旧函数曲线对比:

BM25中TF的公式为:

其中tf是传统的词频值。先来看下改良前后的函数曲线对比(下图中k=1.2):

可以看到,传统的tf计算公式中,词频越高,tf值就越大,没有上限。但BM中的tf,随着词频的增长,tf值会无限逼近(k+1),相当于是有上限的。这就是二者的区别。一般 k取 1.2,Lucene中也使用1.2作为 k 的默认值。

在传统的计算公式中,还有一个norm。BM25将这个因素加到了TF的计算公式中,结合了norm因素的BM25中的TF计算公式为:

和之前相比,就是给分母上面的 k 加了一个乘数 (1.0−b+b∗L)(1.0−b+b∗L)。 其中的 L 的计算公式为:

其中,|d|是当前文档的长度,avgDl 是语料库中所有文档的平均长度。

b 是一个常数,用来控制 L 对最总评分影响的大小,一般取0~1之间的数(取0则代表完全忽略 L )。Lucene中 b 的默认值为 0.75。

通过这些细节上的改良,BM25在很多实际场景中的表现都优于传统的TF-IDF,所以从Lucene 6.0.0版本开始,上位成为默认的相似度评分算法。

上例是通过similarity属性来指定打分模型,用到了以下三个参数:

如果我们要使用某种特定的打分模型,并且希望应用到全局,那么就在elasticsearch.yml配置文件中加入:

通过boosting可以人为控制某个字段的在评分过程中的比重,有两种类型:

通过在mapping中设置boost参数,可以在索引期间改变字段的评分权重:

需要注意的是:在索引期间修改的文档boosting是存储在索引中的,要想修改boosting必须重新索引该篇文档。

一旦映射建立完成,那么所有name字段都会自动拥有一个boost值,并且是以降低精度的数值存储在Lucene内部的索引结构中。只有一个字节用于存储浮点型数值(存不下就损失精度了),计算文档的最终得分时可能会损失精度。

另外,boost是应用与词条的。因此,再被boost的字段中如果匹配上了多个词条,就意味着计算多次的boost,这将会进一步增加字段的权重,可能会影响最终的文档得分。

查询期间的boosting可以避免上述问题。

几乎所有的查询类型都支持boost,例如:

就对于最终得分而言,加了boost的name查询更有影响力。也只有在bool查询中,boost更有意义。

boost也可以用于multi_match查询。

除此之外,我们还可以使用特殊的语法,只为特定的字段指定一个boost。通过在字段名称后添加一个^符号和boost的值。告诉ES只需对那个字段进行boost:

上例中,title字段被boost了3倍。

需要注意的是:在使用boost的时候,无论是字段或者词条,都是按照相对值来boost的,而不是乘以乘数。如果对于所有的待搜索词条boost了同样的值,那么就好像没有boost一样。因为Lucene会标准化boost的值。如果boost一个字段4倍,不是意味着该字段的得分就是乘以4的结果。

ES背后的评分过程比我们想象的要复杂,有时候某个查询结果可能跟我们的预期不太一样,这时候可以通过explain让ES解释一下评分细节。

由于结果太长,我们这里对结果进行了过滤("size": 1返回一篇文档),只查看指定的字段("_source": "name"只返回name字段)。

在新增的_explanation字段中,可以看到value值是0.9331132,那么是怎么算出来的呢?

分词spring在描述字段(name)出现了1次,所以TF的综合得分经过"description" : "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:"计算,得分是0.43243244。

那么逆文档词频呢?根据"description" : "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:"计算得分是0.98082924。

需要注意的是,explain的特性会给ES带来额外的性能开销,一般只在调试时使用。

搜索的时候,要依靠倒排索引;排序的时候,需要依靠正排索引,看到每个document的每个field,然后进行排序,所谓的正排索引,其实就是doc values。

在建立索引的时候,一方面会建立倒排索引,以供搜索用;一方面会建立正排索引,也就是doc values,以供排序,聚合,过滤等操作使用。

doc values是被保存在磁盘上的,此时如果内存足够,os会自动将其缓存在内存中,性能还是会很高;如果内存不足够,os会将其写入磁盘上。

DocValues默认是启用的,可以在创建索引的时候关闭,如果后面要开启DocValues,需要做reindex操作。

参考:
https://www.elastic.co/guide/cn/elasticsearch/guide/current/scoring-theory.html

https://blog.csdn.net/qq_29860591/article/details/109574595

https://www.jianshu.com/p/2624f61f1d02

http://www.dtmao.cc/news_show_378736.shtml

https://blog.csdn.net/molong1208/article/details/50623948

https://www.cnblogs.com/Neeo/articles/10721071.html

https://www.cnblogs.com/jpfss/p/10775376.html

https://zhuanlan.zhihu.com/p/27951938

基于匹配token比率的Elasticsearch评分

【中文标题】基于匹配token比率的Elasticsearch评分【英文标题】:Elasticsearch scoring based on the ratio of the matched tokens 【发布时间】:2016-04-19 09:07:26 【问题描述】:

我正在使用 Elasticsearch(v 1.7.3,带有 Java 传输客户端)来搜索人名数据库。我正在利用一堆可用的语音算法(DoubleMetaphone、RefinedSoundex 等)来索引我的姓名字段并存储它们。但是,我需要的评分算法是计算输入标记与索引中的标记的接近百分比。

例如:

使用语音算法对以下文档进行索引时:


  "FullName": "Christopher Cruickshank"

扩展为(使用分析 api 获取的输出):


  "tokens": [
    
      "token": "C3090360109",
      "start_offset": 0,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    ,
    
      "token": "christopher",
      "start_offset": 0,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    ,
    
      "token": "K3936",
      "start_offset": 0,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    ,
    
      "token": "KRST",
      "start_offset": 0,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    ,
    
      "token": "C3903083",
      "start_offset": 12,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 2
    ,
    
      "token": "cruickshank",
      "start_offset": 12,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 2
    ,
    
      "token": "K3935",
      "start_offset": 12,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 2
    ,
    
      "token": "KRKX",
      "start_offset": 12,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 2
    
  ]

现在在搜索期间,当我查询:


              "match": 
                "FullName": 
                  "query": "Cristopher Krukshank",
                  "boost": 10.0
                
              
            

我想做的是根据索引中匹配标记的数量对结果进行评分。

即:

(Number of matched tokens per term / Total number of expanded tokens per term) * Boost

虽然这在概念上可行,但我想知道是否有更好的方法来实现同样的效果。

另外,我倾向于在索引期间增加很多复杂性和逻辑(通过将总标记数存储在字段中),因此我的搜索逻辑会更简单。如果这是一种合理的方法,那么我想知道在索引过程中使用分析 api 是否有任何技术含义,尤其是在对数百万个名称使用批量索引时。 我猜会为每个原始令牌和每个扩展令牌(可能很大!)调用分析 API。

如果这根本不是一个合理的方法,那么请有人指点或分享一些经验吗?

我也在考虑的另一个选项是在查询期间调用分析 api,并使用“解释”选项将查询发送到 elasticsearch,然后在解释部分进行字符串匹配以计算出匹配的令牌数。

【问题讨论】:

你的意思是不是和我在***.com/questions/39100218/…中描述的类似 你能解决这个问题吗?我不想在搜索之前通过调用分析 API 添加另一个网络调用 【参考方案1】:

我们以间接的方式做到了这一点。我正在努力寻找更好的方法并看到了您的帖子。

解决方案是在搜索“Cristopher Krukshank”时,例如,第一个命中是:

Cristopher Krukshank Jr.”,得分为 10.0

然后您获取第一个结果“Cristopher Krukshank Jr.”并再次搜索。当然,第一个结果将是“Cristopher Krukshank Jr.”,但得分更高,例如“20.0”。

所以你知道最高分数是20,那么对于部分匹配,最终分数是“first score/max score”,即10/ 20 = 0.5。最终得分将是一个介于 0~1 之间的值。 1 表示完全匹配。

一个问题是输入可能是一个标记任何东西。例如,对于“Cristopher Krukshank XXXXX”,XXXXX 可能不是索引上的标记。所以为了让它正确,我们必须使用令牌的数量来重新计算分数。

【讨论】:

以上是关于Elasticsearch——评分机制详解的主要内容,如果未能解决你的问题,请参考以下文章

elasticsearch 配置详解

Elasticseach的评分机制

lucene 的评分机制

干货 | 一步步拆解 Elasticsearch BM25 模型评分细节

ElasticSearch 8 学习笔记总结

Elasticsearch如何在Elasticsearch中查找相似的术语