美文网首页
Elasticsearch 7.x 深入【7】文档打分

Elasticsearch 7.x 深入【7】文档打分

作者: 孙瑞锴 | 来源:发表于2020-05-13 21:13 被阅读0次

    1. 借鉴

    极客时间 阮一鸣老师的Elasticsearch核心技术与实战
    ElasticSearch7+Spark构建高相关性搜索服务&千人千面推荐系统
    Lucene学习总结之六:Lucene打分公式的数学推导
    【Elasticsearch】打分策略详解与explain手把手计算
    官网 Theory Behind Relevance Scoring
    对数计算器

    2. 开始

    数据准备:Elasticsearch 7.x 深入 数据准备

    自定义打分

    使用function score自定义打分

    在查询结束后,对匹配的每个文档进行重新算分并排序

    • 如何来构建一个function_score的查询呢?其实就是将我们平时在query里面的部分移到function_score的query里面,加上一些余外的算法。我们来举个栗子,然后一步一步了解其算法规则,因为在functions中可以指定多个规则,我们先以一个规则的为例
    GET /notes/_search
    {
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch hadoop canal"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "grade"
              }
            }
          ]
        }
      }
    }
    
    • 可以看到返回的结果中的分数已经被grade取代了
    {
      "took" : 2,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 98.08292,
        "hits" : [
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 98.08292,
            "_source" : {
              "title" : "Elasticsearch",
              "content" : "About Elasticsearch",
              "grade" : 100
            }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 3.9233167,
            "_source" : {
              "title" : "Hadoop",
              "content" : "About Hadoop",
              "grade" : 4.5
            }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 3.9233167,
            "_source" : {
              "title" : "Canal",
              "content" : "About Canal",
              "grade" : 4.7
            }
          }
        ]
      }
    }
    
    • 可以看到目前function_score的算分规则就是:match匹配的相关的分数*grade的数值

    可以看到如果grade差距非常大时,分数差距特别大,有哪些参数可以调整它的离散程度呢?

    变更分数1
    • 使用modifier:我们可以添加modifier属性来使分数更加平滑。它有以下备选值
    释义
    none 不使用
    log 以10为底,新分值=老分值*log(filed的值)
    log1p 以10为底,新分值=老分值*log(1 + filed的值)
    log2p 以10为底,新分值=老分值*log(2 + filed的值)
    ln 以e为底,新分值=老分值*ln( filed的值)
    ln1p 以e为底,新分值=老分值*ln(1 + filed的值)
    ln2p 以e为底,新分值=老分值*ln(2 + filed的值)
    square 平方
    sqrt 开方
    reciprocal 倒数
    • 在说modifier之前,我们先说下上面所谓的新老分数。

      1. 老分数:说的是function_score的query部分的分数,也就是相关度分数,查看这一部分算分跟我们直接写query看到的分数是一样的。
      2. 新分数:就是说function_score返回的分数。
    • 我们使用modifier,来看看

    GET /notes/_search
    {
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch hadoop canal"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "grade",
                "modifier": "log1p"
              }
            }
          ]
        }
      }
    }
    
    • 可以看到,就算grade的分支差别特别大,最后算的分数的离散程度不会特别高
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 1.9658968,
        "hits" : [
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.9658968,
            "_source" : {
              "title" : "Elasticsearch",
              "content" : "About Elasticsearch",
              "grade" : 100
            }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 0.6855702,
            "_source" : {
              "title" : "Hadoop",
              "content" : "About Hadoop",
              "grade" : 4.5
            }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 0.6855702,
            "_source" : {
              "title" : "Canal",
              "content" : "About Canal",
              "grade" : 4.7
            }
          }
        ]
      }
    }
    
    • 嗯,我们来以这个例子的id为1,title是Elasticsearch的文档分数为例,手动计算一下分数到底是不是我们上面说的那一回事。
    - - 补充说明
    老分数 0.9808292 通过以下查询得来的分数:
    GET /notes/_search
    {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
          }
        }
    }
    计算公式为 新分值=老分值*log(1 + filed的值) 我们使用的modifier为log1p
    我们计算的新分数的值: 0.9808292 * log(1 + 100) = 0.9808292 * 2.0043 = 1.96587597 -
    es通过function_score查询出来的值: 1.9658968 -
    • 通过计算可以看到,跟我们计算的结果基本一致。(●´∀`●)ノ
    变更分数2
    • 使用factor:我们可以添加factor属性来在modifier的基础上使其增加相应的权重,我们以modifier为log1p为例,同时设置factor为3,则最终得分的计算公式为:新分数=老分数*log(1 + factor * grade的分数)
    GET /notes/_search
    {
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch hadoop canal"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "grade",
                "modifier": "log1p",
                "factor": 2
              }
            }
          ]
        }
      }
    }
    
    • 结果如下,ID为1的文档我计算的分数结果是2.25904581,可以看到分数也基本一致
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 2.2590418,
        "hits" : [
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 2.2590418,
            "_source" : {
              "title" : "Elasticsearch",
              "content" : "About Elasticsearch",
              "grade" : 100
            }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 0.9359489,
            "_source" : {
              "title" : "Hadoop",
              "content" : "About Hadoop",
              "grade" : 4.5
            }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 0.9359489,
            "_source" : {
              "title" : "Canal",
              "content" : "About Canal",
              "grade" : 4.7
            }
          }
        ]
      }
    }
    
    变更分数3
    • 使用score_mode: 控制functions内部的计算规则。这是什么意思呢?因为functions是一个数组,里面可以有多个函数。
    释义 是否默认
    Multiply match匹配的相关的分数*grade的数值
    Sum match匹配的相关的分数+grade的数值
    Max/Min 最大/最小;【取(match匹配的相关的分数)或(grade的数值)中的最大/最小值】
    Replace 使用函数值【grade的数值】替换算分
    • 这里我们以sum为例
    GET /notes/_search
    {
      "_source": "", 
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch hadoop canal"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "grade",
                "modifier": "log2p",
                "factor": 10
              }
            },
            {
              "field_value_factor": {
                "field": "grade",
                "modifier": "log2p",
                "factor": 5
              }
            }
          ],
          "score_mode": "sum"
        }
      }
    }
    
    • 返回结果:
    {
      "took" : 1,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 5.5922675,
        "hits" : [
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 5.5922675,
            "_source" : { }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 2.9088175,
            "_source" : { }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 2.9088175,
            "_source" : { }
          }
        ]
      }
    }
    
    • 嗯,我们来以这个例子的id为1,title是Elasticsearch的文档分数为例,我们再来手动计算一下。
    - - 补充说明
    老分数 0.9808292 通过以下查询得来的分数:
    GET /notes/_search
    {
      "query": {
        "match": {
          "title": "elasticsearch hadoop canal"
          }
        }
    }
    计算公式为 新分值=老分值 * (log(2 + 5* filed的值) + log(2 + 10* filed的值)) 我们使用的modifier为log2p
    我们计算的新分数的值: 0.9808292 * (log(2 + 5 *100) + log(2 + 10 *100)) = 0.9808292 * (2.7007 + 3.0009) = 5.59229577 -
    es通过function_score查询出来的值: 5.5922675 -
    • 通过计算可以看到,跟我们计算的结果基本一致。(●´∀`●)ノ
    变更分数4
    • 使用boost_mode:控制query和functions部分的计算规则在上面我们看到分值的计算是使用相乘的方式:match匹配的相关的分数*grade的数值,使用boost mode则可以控制计算方式。
    释义 是否默认
    Multiply match匹配的相关的分数*grade的数值
    Sum match匹配的相关的分数+grade的数值
    Max/Min 最大/最小;【取(match匹配的相关的分数)或(grade的数值)中的最大/最小值】
    Replace 使用函数值【grade的数值】替换算分
    • 这里我们以sum为例
    GET /notes/_search
    {
      "_source": "", 
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "grade"
              }
            }
          ],
          "boost_mode": "sum"
        }
      }
    }
    
    • 结果如下,可以看到score已经变为相加了:
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 1,
          "relation" : "eq"
        },
        "max_score" : 100.98083,
        "hits" : [
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 100.98083,
            "_source" : { }
          }
        ]
      }
    }
    
    • 这里我就不算了,如果不设置,默认为multiply,可以参照我们本篇前面的那个计算一下。

    max_boost属性

    • 使用max_boost:设置分数在某个范围以内
    GET /notes/_search
    {
      "_source": "", 
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch hadoop canal"
            }
          },
          "functions": [
            {
              "field_value_factor": {
                "field": "grade"
              }
            }
          ],
          "max_boost": 10
        }
      }
    }
    
    • 结果如下,我们使用max_boost将分数规定在了10分以内
    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 3,
          "relation" : "eq"
        },
        "max_score" : 9.808291,
        "hits" : [
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 9.808291,
            "_source" : { }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 3.9233167,
            "_source" : { }
          },
          {
            "_index" : "notes",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 3.9233167,
            "_source" : { }
          }
        ]
      }
    }
    

    random_score属性

    • random_score:一致性随机函数。设置seed属性,如果seed值保持一致,那查询出来的结果排序就是相同的。
    GET /notes/_search
    {
      "_source": "", 
      "query": {
        "function_score": {
          "query": {
            "match": {
              "title": "elasticsearch hadoop canal"
            }
          },
          "functions": [
            {
              "random_score": {
                "seed": 1231
              }
            }
          ]
        }
      }
    }
    
    • 计算分值的函数
    函数 释义
    weight 设置权重
    field value factor 使用该值来修改score
    random 随机算分
    衰减函数 以某个字段的值为标准,距离某个值越接近,得分越高
    script 自定义脚本实现算分

    查看打分详情

    1. 在查询中添加_explain参数

    GET /tmdb_movies/_search
    {
      "explain": true, 
      "query": {
        "multi_match": {
          "query": "steve",
          "fields": ["title"]
        }
      }
    }
    
    • 我们以查询结果的第一篇文档做例子


      得分分析

    2. 使用_validate/query?explain

    GET /tmdb_movies/_validate/query?explain
    {
      "query": {
        "multi_match": {
          "query": "steve",
          "fields": ["title"]
        }
      }
    }
    
    • 查询结果
    {
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "failed" : 0
      },
      "valid" : true,
      "explanations" : [
        {
          "index" : "tmdb_movies",
          "valid" : true,
          "explanation" : "title:steve"
        }
      ]
    }
    

    3. 大功告成

    相关文章

      网友评论

          本文标题:Elasticsearch 7.x 深入【7】文档打分

          本文链接:https://www.haomeiwen.com/subject/kpgfghtx.html