ES打分机制

打分机制

文档打分的运作机制:TF-IDF

Lucenees 的打分机制是一个公式。将查询作为输入,使用不同的手段来确定每一遍文档的得分,将每一个因素最后通过公式综合起来,返回该文档的最终得分。这个综合考量的过程,就是我们希望相关的文档被优先返回的考量过程。在Lucenees中这种相关性称为得分。

在开始计算得分之前,es使用了被搜索词条的频率和它有多常见来影响得分,从两个方面理解:

  • 一个词条在某篇文档中出现的次数越多,该文档就越相关
  • 一个词条如果在不同的文档中出现的次数越多,它就越不相关

词频:TF

考虑一篇文档得分的首要方式,是查看一个词条在文档中出现的次数,比如某篇围绕es的打开展开的,那么文章中肯定会多次出现相关字眼,当查询时,我们认为该文章更符合,所以,该篇文档的得分会更高。

逆文档频率:IDF

相比于词频,逆文档频率稍微显复杂,如果一个词条在索引中的不同文档出现的次数越多,那么它就越不重要

例子:

The rules-which require employees to work from 9 am to 9 pm
In the weeks that followed the creation of 996.ICU in March
The 996.ICU page was soon blocked on multiple platforms including the messaging tool WeChat and the UC Browser.

假如es索引中,有上述3篇文档:

  • 词条ICU的文档频率是2,因为它出现在2篇文章中,文档的的逆源自得分乘以1/DF,DF是该词条的文档频率,这就意味着,由于ICU 词条拥有更高的文档频率,所以,它的权重会降低。
  • 词条the的文档频率是3,它在3篇文章都出现了,注意:尽管the在后两篇文档都出现两次,但是它的词频还是3,因为,逆文档词频只检查词条是否出现在某篇文档中,而不检查它在这篇文档中出现了多少次,那是词频该干的事。

逆文档词频是一个重要的因素,用来平衡词条的词频。比如我们搜索the 996.ICU。单词the几乎出现在所有的文档中(中文中比如),如果这个不被均衡一下,那么the的频率将完全淹没996.ICU。所以,逆文档词频就有效的均衡了the这个常见词的相关性影响。以达到实际的相关性得分将会对查询的词条有一个更准确地描述。

当词频和逆文档词频计算完成,就可以使用TF-IDE公式来计算文档的得分了。

Lucene评分公

之前讨论Lucene默认评分公式被称为TF-IDF,一个基于词频和逆文档词频的公式。Lucene实用评分公式如下:
在这里插入图片描述

词条的词频越高,得分越高;相似地,索引中词条越罕见,逆文档频率越高,其中在加上商调和因子查询标准化,调和因子考虑了搜索过多少文档以及发现了多少词条;查询标准化,是试图让不同的查询结果具有可比性。我们称这种默认的打分方法时TF-IDF和向量空间模型(vector space model)的结合。

其他打分方法

除了TF-IDF结合向量空间模型的实用评分模式,是esLucene最为主流的评分机制,但这并不是唯一的,除了TF-IDF这种实用模型之外,其他的模型包括:

  • Okapi BM25
  • 随机性分歧(Divergence from randomness),即DFR相似度
  • LM Dirichlet相似度
  • LM Jelinek Mercer相似度。

这里简要的介绍BM25几种主要设置,即k1bdiscount_overlaps:

  • k1和b是数值的设置,用于调整得分是如何计算的。
  • k1控制对于得分而言词频(TF)的重要性。
  • b是介于0 ~ 1之间的数值,它控制了文档篇幅对于得分的影响程度。
  • 默认情况下,k1设置为1.2,而b则被设置为0.75
  • discount_overlaps的设置用于告诉es,在某个字段中,多少个分词出现在同一位置,是否应该影响长度的标准化,默认值是true

配置打分模型

简要配置BM25打分模型

BM25是一种基于概率的打分框架。

PUT w2
{
  "mappings": {
    "doc": {
      "properties": {
        "title": {
          "type": "text",
          "similarity": "BM25"
        }
      }
    }
  }
}

PUT w2/doc/1
{
  "title":"The rules-which require employees to work from 9 am to 9 pm"
}

PUT w2/doc/2
{
  "title":"In the weeks that followed the creation of 996.ICU in March"
}

PUT w2/doc/3
{
  "title":"The 996.ICU page was soon blocked on multiple platforms including the messaging tool WeChat and the UC Browser."
}

GET w2/doc/_search
{
  "query": {
    "match": {
      "title": "the 996"
    }
  }
}

上例是通过similarity参数来指定打分模型。只有查询,还是当数据比较大的时候

为BM25配置高级的settings

PUT w3
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer":"ik_smart"
      }
    },
    "similarity": {
      "my_custom_similarity": {
        "type": "BM25",
        "k1": 1.2,
        "b": 0.75,
        "discount_overlaps": false
      }
    }
  },
  "mappings": {
    "doc": {
      "properties": {
        "title": {
          "type": "text",
          "similarity":"my_custom_similarity"
        }
      }
    }
  }
}

PUT w3/doc/1
{
  "title":"The rules-which require employees to work from 9 am to 9 pm"
}

PUT w3/doc/2
{
  "title":"In the weeks that followed the creation of 996.ICU in March"
}

PUT w3/doc/3
{
  "title":"The 996.ICU page was soon blocked on multiple platforms including the messaging tool WeChat and the UC Browser."
}

GET w3/doc/_search
{
  "query": {
    "match": {
      "title": "the 996"
    }
  }
}

配置全局打分模型

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

index.similarity.default.type: BM25

boosting

boosting是一个用来修改文档相关性的程序。boosting有两种类型:

  • 索引的时候,比如我们在定义mappings的时候
  • 查询一篇文章的时候

这两种方式都可以提升一篇文档的得分。需要注意的是:在索引期间修改文档的boosting是存储在索引中的,要想修改boosting必须重写索引该篇文档。

索引期间的boosting

PUT w4
{
  "mappings": {
    "doc": {
      "properties": {
        "name": {
          "boost": 2.0,
          "type": "text"
        },
        "age": {
          "type": "long"
        }
      }
    }
  }
}

一般不建议这样写,因为一旦映射建立完成,那么所有的name字段都会自动拥有一个boost值。要想修改这个值,那么必须重新索引文档;另外一个原因是:boost值是以降低精准度的数值存储在Lucene内部的索引结构中。只有一个字节用于存储浮点型数值(存不下就损失精度了),所以,计算文档的最终得分时可能会损失精度。最后,boost是应用与词条的。因此,再被boost的字段中如果匹配上了多个词条,就意味着计算多次的boost,这将会进一步增加字段的权重,可能会影响最终的文档的得分。

查询期间的boosting

es中,几乎所有的查询类型都支持boost,如下,在查询期间,使用match查询进行boosting

PUT w5
{
  "mappings":{
    "doc":{
      "properties": {
        "title": {
          "type": "text",
          "analyzer": "ik_max_word"
        },
        "content": {
          "type": "text",
          "analyzer": "ik_max_word"
        }
      }
    }
  }
}

PUT w5/doc/1
{
  "title":"Lucene is cool",
  "content": "Lucene is cool"
}

PUT w5/doc/2
{
  "title":"Elasticsearch builds on top of lucene",
  "content":"Elasticsearch builds on top of lucene"
}

PUT w5/doc/3
{
  "title":"Elasticsearch rocks",
  "content":"Elasticsearch rocks"
}

查询:

GET w5/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title":{
              "query": "elasticserach rocks",
              "boost": 2.5
            }
          }
        },
        {
          "match": {
            "content": "elasticserach rocks"
          }
        }
      ]
    }
  }
}

就对应最终得分而言,content字段,加了boosttitle查询更有影响力。也只有在bool查询中,boost更有意义。

跨越多个字段的查询

boost也可以用于multi_match查询

GET w5/doc/_search
{
  "query": {
    "multi_match": {
      "query": "elasticserach rocks",
      "fields": ["title", "content"],
      "boost": 2.5
    }
  }
}

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

GET w5/doc/_search
{
  "query": {
    "multi_match": {
      "query": "elasticserach rocks",
      "fields": ["title^3", "content"]
    }
  }
}

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值