Relevance

作者: 潘大的笔记 | 来源:发表于2019-12-06 20:00 被阅读0次

    控制相关度

    ES使用布尔模型(Boolean model)查找匹配文档,并用一个实用评分函数(practical scoring function)的公式来计算相关度。这个公式借鉴了词频/逆向文档频率(term frequency/inverse document frequency)和向量空间模型(vector space model),同时加入了一些现代的新特性,如协调因子(coordination factor),字段长度归一化(field length normalization),以及词或查询语句权重提升。
    1、布尔模型
    只是在查询中使用AND/OR/NOT这样的条件来插好匹配的文档

    full AND text AND search AND (elasticsearch OR lucene)
    

    会将所有包括词 full 、 text 和 search ,以及 elasticsearch 或 lucene 的文档作为结果集。
    2、词频/逆向文档频率(TF/IDF)
    词的权重由三个因素决定
    --词频,词在文档中出现的“频度”越高,权重越高
    如果不在意刺在某个字段中出现的频度,只在意是否出现过,可以禁用词频统计。(index_options设置为docs,not_analyzed的默认值就是这个)
    --逆向文档频率,词在集合所用文档里出现的“频率”是多少,频次越高,权重越低。
    --字段长度归一值,字段的长度是多少?字段越短,字段的权重越高。
    ("norms": { "enabled": false }禁用归一值,not_analyzed的默认值就是这个)
    以上三个因素,是在索引时计算并存储的
    3、向量空间模型
    查询通常不止一个词,所以需要一种合并多词权重的方式,也就是向量空间模型
    向量空间模型提供一种比较多词查询的方式,单个评分代表文档与查询的匹配程度,为了做到这点,这个模型将文档和查询都以向量的形式表示:
    向量实际上就是包含多个数的一堆数组(每个数都代表一个词的权重),例如:

    [1,2,5,22,3,8]
    

    4、查询归一化
    试图将查询“归一化”,这样就能将两个不同的查询结果相比较
    5、查询协调
    协调因子(coord),可以为那些查询词包含度高的文档提供奖励,文档里出现的查询词越多,越有机会成为好的匹配结果。
    例如查询 quick brown fox

    文档里有 fox → 评分: 1.5
    文档里有 quick fox → 评分: 3.0
    文档里有 quick brown fox → 评分: 4.5
    

    协调因子将评分与文档里匹配词的数量相乘,然后除以查询所有词的数量。

    文档里有 fox → 评分: 1.5 * 1 / 3 = 0.5
    文档里有 quick fox → 评分: 3.0 * 2 / 3 = 2.0
    文档里有 quick brown fox → 评分: 4.5 * 3 / 3 = 4.5
    

    6、索引时字段层权重提升
    ……

    查询时权重提升

    boost参数,用来影响权重
    当在多个索引中搜索时,可以使用indices_boost来提升整个索引的权重

    使用查询结构修改相关度

    越深的查询层级,对相关度的影响就越小

    Not Quite Not

    假如我们检索Apple,是想搜索苹果公司,就需要把一些例如水果的东西排查掉(must_not),但是排除掉这些词之后,可能丢失了与苹果公司相关的文档。
    权重提升查询能解决这个问题(包含结果,但是降低排名)

    GET /_search
    {
      "query": {
        "boosting": {
          "positive": {
            "match": {
              "text": "apple"
            }
          },
          "negative": {
            "match": {
              "text": "pie tart fruit crumble tree"
            }
          },
          "negative_boost": 0.5
        }
      }
    }
    

    boosting查询接受positive和negative查询。只有匹配positive查询的文档罗列出来,还匹配negative查询的文档通过文档的原始_score月negative_boost相乘的方式降级

    忽略TF/IDF

    有时候我们不关心TF/IDF,只想知道一个词是否在某个字段出现过。
    constant_score查询可以包含查询或过滤,为任意一个匹配的文档指定评分1,也可以使用boost提升权重,但是协调因子和查询归一化因子仍然会被考虑在内。
    --
    默认情况下not_analyzed字段会禁用字段长度归一值,并将index_options设为docs,禁用词频。但是每个词的倒排文档频率仍然会被考虑。同样可以使用constant_score来解决这个问题

    function_score查询

    允许为每个与主查询匹配的文档应用一个函数,以达到改变甚至完全替换原始查询评分_score的目的。
    也能用过滤器对结果的子集应用不同的函数,这样既能高效评分,又能利用过滤器缓存
    ES预定义函数:
    weight:当weight为2时,最终结果为2*_score;
    field_value_factor:使用这个值来修改_score
    random_score:为每个用户都使用一个不同的随机评分对结果排序,但对某一具体用户来说,看到的顺序始终是一致的。
    衰减函数:linear、exp、gauss
    将浮动值结合到_score中,例如结合publist_date
    如果需求超出以上范围时,用自定义脚本可以完全控制评分计算。(script_score)

    按受欢迎度提升权重

    function_score可以与field_value_factor结合使用,例如:

    GET /blogposts/post/_search
    {
      "query": {
        "function_score": { (1)
          "query": { (2)
            "multi_match": {
              "query":    "popularity",
              "fields": [ "title", "content" ]
            }
          },
          "field_value_factor": { (3)
            "field": "votes" (4)
          }
        }
      }
    }
    

    1、function_score查询将主查询和函数包括在内。
    2、主查询优先执行
    3、field_value_factor函数会被应用到每个与主查询匹配的文档
    4、每个文档的votes字段都必须有值供function_score计算。如果没有值就必须指定missing
    新的评分:new_score = _score * votes
    然而_score通常处于0到10之间,有votes为10的会掩盖掉全文评分,为0的评分会被置0
    一种更好的方式是使用modifier,例如:

    GET /blogposts/post/_search
    {
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query":    "popularity",
              "fields": [ "title", "content" ]
            }
          },
          "field_value_factor": {
            "field":    "votes",
            "modifier": "log1p" (1)
          }
        }
      }
    }
    

    此时新的评分:new_score = old_score * log(1 + number_of_votes)
    modifier可以为:none (默认状态)、 log 、 log1p 、 log2p 、 ln 、 ln1p 、 ln2p 、 square 、 sqrt 以及 reciprocal
    还可以使用factor参数
    new_score = old_score * log(1 + factor * number_of_votes)
    boost_mode可以控制函数与查询评分_score合并后的结果
    接受的参数值:

    multiply、sum、min、max、replace
    

    参数max_boost可以限制一个函数的最大效果

    GET /blogposts/post/_search
    {
      "query": {
        "function_score": {
          "query": {
            "multi_match": {
              "query":    "popularity",
              "fields": [ "title", "content" ]
            }
          },
          "field_value_factor": {
            "field":    "votes",
            "modifier": "log1p",
            "factor":   0.1
          },
          "boost_mode": "sum",
          "max_boost":  1.5 (1)
        }
      }
    }
    

    无论field_value_factor 函数的结果如何,最大就是1.5

    过滤集提升权重

    过滤器可以将结果划分为多个子集(每个特性一个过滤器),可以为每个子集使用不同的函数。

    GET /_search
    {
      "query": {
        "function_score": {
          "filter": { (1)
            "term": { "city": "Barcelona" }
          },
          "functions": [ (2)
            {
              "filter": { "term": { "features": "wifi" }}, (3)
              "weight": 1
            },
            {
              "filter": { "term": { "features": "garden" }}, (3)
              "weight": 1
            },
            {
              "filter": { "term": { "features": "pool" }}, (3)
              "weight": 2 (4)
            }
          ],
          "score_mode": "sum", (5)
        }
      }
    }
    

    1、function_score查询有个filter过滤器而不是query查询
    2、functions关键字存储着一个将被应用的函数列表
    3、函数会被应用于和filter过滤器匹配的文档
    4、pool比其他特性更重要,所以它有更高weight
    5、score_mode指定各个函数的值进行组合运算的方式。

    随机评分

    相同_score的文档每次都会以相同次序出现,此时需要引入一些随机性(只针对_score为整数这样的情况)。
    random_score函数会输出一个0到1之间的数,当种子seed值相同时,生成的随机结果是一致的。

    GET /_search
    {
      "query": {
        "function_score": {
          "filter": {
            "term": { "city": "Barcelona" }
          },
          "functions": [
            {
              "filter": { "term": { "features": "wifi" }},
              "weight": 1
            },
            {
              "filter": { "term": { "features": "garden" }},
              "weight": 1
            },
            {
              "filter": { "term": { "features": "pool" }},
              "weight": 2
            },
            {
              "random_score": { (1)
                "seed":  "the users session id" (2)
              }
            }
          ],
          "score_mode": "sum"
        }
      }
    }
    

    1、random_score没用任何过滤器filter,所以会被应用到所有文档
    2、将用户的ID作为种子seed,会让该用户的随机始终保持一致。

    越近越好

    function_score衰减函数linear、exp、gauss(线性、指数和高斯函数),他们可以操作数值、时间以及经纬度地理坐标点这样的字段,能接受以下参数:
    origin:中心点或字段可能的最佳值,与origin相同的文档_score为满分1.0
    scale:衰减率,即一个文档从origin下落时,_score改变的速度
    decay:从origin衰减到scale所得的评分_score,默认0.5
    offset:以origin为中心点,为其设置一个非0的偏移量offset覆盖一个范围,而不只是单个原点。在范围内origin±offset内的所有_score都是1.0
    样例:户希望租一个离伦敦市中心近( { "lat": 51.50, "lon": 0.12} )且每晚不超过 £100 英镑的度假屋

    GET /_search
    {
      "query": {
        "function_score": {
          "functions": [
            {
              "gauss": {
                "location": { (1)
                  "origin": { "lat": 51.5, "lon": 0.12 },
                  "offset": "2km",
                  "scale":  "3km"
                }
              }
            },
            {
              "gauss": {
                "price": { (2)
                  "origin": "50", (3)
                  "offset": "50",
                  "scale":  "20"
                }
              },
              "weight": 2 (4)
            }
          ]
        }
      }
    }
    

    脚本评分

    function_score内置函数无法满足应用场景,可以使用script_score函数自行实现逻辑
    ES5之后推荐脚本语言:painless

    可插拔的相似度算法

    较为高深……无法直视

    更改相似度

    ……

    调试相关度是最后10%要做的事情

    ……

    相关文章

      网友评论

          本文标题:Relevance

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