【Elasticsearch】相关度控制

一.数据导入

  • 本人测试数据集下载https://download.csdn.net/download/qq_30285985/12645770
  • 更多数据集下载https://www.cluebenchmarks.com/dataSet_search.html

二.创建索引

#GET http://192.168.16.128:9200/es_news
{
    "settings": {
        "number_of_shards" :   1,
        "number_of_replicas" : 0
    },
  "mappings":{
    "properties":{
      "id":{"type":"long"},
      "title":{"type":"text","analyzer":"ik_max_word", 
      "search_analyzer": "ik_smart" },
      "content":{"type":"text","analyzer":"ik_smart"},
      "shortname":{"type":"text","analyzer":"ik_max_word"},
      "location":{"type":"geo_point"}
    }
  }
    
}

数据导入->数据导入指引

自定义添加数据。

POST http://192.168.16.128:9200/es_news/_doc
{
  "title": "我中了一个奖品",
  "content":  "奖品内容是苹果电脑",
  "location": "88.884874,29.263792",
   "shortname": "日喀则"
}

三.什么是相关性?

每个文档都有相关性评分,用一个正浮点数字段 _score 来表示 。 _score 的评分越高,相关性越高。换句话说,就是_score 越高,就离我们想要搜索到的结果越相近。

Elasticsearch 的相似度算法被定义为检索词频率/反向文档频率, TF/IDF ,包括以下内容:

  • 检索词频率

    检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。如:检索词 honeymoon 在这个文档的 tweet 字段中的出现次数。

  • 反向文档频率

    每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。如:检索词 honeymoon 在索引上所有文档的 tweet 字段中出现的次数。

  • 字段长度准则

    字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。如:在这个文档中, tweet 字段内容的长度 -- 内容越长,值越小。

通过explain为true可查看分数是如何计算的。

扫描二维码关注公众号,回复: 11452742 查看本文章
#GET http://192.168.16.128:9200/es_news/_search
{
    "explain":"true",
    "query":{
        "match":{
            "content":"奖品"
        }
    }
}

查询结果如下:

{
    "took":5,
    "timed_out":false,
    "_shards":{
        "total":1,
        "successful":1,
        "skipped":0,
        "failed":0
    },
    "hits":{
        "total":{
            "value":3,
            "relation":"eq"
        },
        "max_score":9.761058,
        "hits":[
            {
                "_shard":"[es_news][0]",
                "_node":"VkN6WwZZQISzsHaUZA5EBw",
                "_index":"es_news",
                "_type":"_doc",
                "_id":"BFY7b3MB_Kk3jjQO7RPr",
                "_score":9.761058,
                "_source":{
                    "title":"我中了一个奖品",
                    "content":"奖品内容是苹果电脑",
                    "location":"88.884874,29.263792",
                    "shortname":"日喀则"
                },
                "_explanation":{
                    "value":9.761058,
                    "description":"weight(content:奖品 in 1400) [PerFieldSimilarity], result of:",
                    "details":[
                        {
                            "value":9.761058,
                            "description":"score(freq=1.0), computed as boost * idf * tf from:",
                            "details":[
                                {
                                    "value":2.2,
                                    "description":"boost",
                                    "details":[

                                    ]
                                },
                                {
                                    "value":5.9928923,
                                    "description":"idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
                                    "details":[
                                        {
                                            "value":3,
                                            "description":"n, number of documents containing term",
                                            "details":[

                                            ]
                                        },
                                        {
                                            "value":1401,
                                            "description":"N, total number of documents with field",
                                            "details":[

                                            ]
                                        }
                                    ]
                                },
                                {
                                    "value":0.7403511,
                                    "description":"tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
                                    "details":[
                                        {
                                            "value":1,
                                            "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,
                                            "description":"dl, length of field",
                                            "details":[

                                            ]
                                        },
                                        {
                                            "value":53.243397,
                                            "description":"avgdl, average length of field",
                                            "details":[

                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            } 
        ]
    }
}

_explanation.details字段中说明了分数是如何计算的。score(freq=1.0), computed as boost * idf * tf from:说明分数是由 boost,idf,tf三个字段相乘得到的。boost2.2分,idf为5.9928923分,tf为0.7403511分,最后相乘为9.761058分。

四.打分控制

1.字段权重修改

在实际的搜索过程中title字段和content字段的权重一定是不一样的,就比如一篇作文,标题的权重一定比内容高。
可在^2,将权重放大2倍。

{
    "query":{
        "multi_match":{
            "query":  "奖品",
            "fields": [ "content", "title^2" ] 
        }
    }
}

2.点赞数打分

​ 遇到复杂的情况时,简单粗暴的权重翻倍就无法满足我们的需求了。
可以通过function-score来进行更详细的评分设置。
比如只按照点赞数进行打分呢?

##PUT /blogposts/post/1
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   6
}
##GET /blogposts/post/_search
{
  "query": {
    "function_score": { 
      "query": { 
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": { 
        "field": "votes" 
		"missing":"1"  ## 表示没有votes字段的时候默认为1
      }
    }
  }
}

3.点赞数更加平滑

​ 按照点赞来评分排序的话,看似是一个很好的选择,但是0个赞和1个赞对比是有很大区别的,但是100个赞和101个赞对比呢?显得就没有那么大的区别了。

​ 所以可以通过modifier以平滑的方式来处理votes的值。换句话说,我们希望最开始的一些赞更重要,但是其重要性会随着数字的增加而降低。
0 个赞与 1 个赞的区别应该比 10 个赞与 11 个赞的区别大很多。

点赞数打分算法:

new_score = old_score * number_of_votes

平滑点赞算法

new_score = old_score * log(1 + number_of_votes)

在这里插入图片描述
在这里插入图片描述

请求如下:

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

修饰语 modifier 的值可以为: none (默认状态)、 loglog1plog2plnln1pln2psquaresqrt 以及 reciprocal 。想要了解更多信息请参照:feild_value_factor

4.点赞平滑程度调节

​ 前面说modifier可以使得点赞的分数变得更加平滑,但是这个平滑的程度如何控制呢?看图!

在这里插入图片描述

默认情况下分数计算方式:

new_score = old_score * log(1 + number_of_votes)

通过factor来调节幅度:

new_score = old_score * log(1 + factor * number_of_votes)

请求方式

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

5.越近越好

​ 点赞评分先告一段落,在大部分场景位置评分的权重相对会高一点,比如搜索酒店,正常来说会搜索附近的小于100块钱一晚酒店。

function_score 提供了一组衰减函数。让我们有能力在两个滑动标准,如地点和价格,之间权衡。

有三种衰减函数—— linearexpgauss (线性、指数和高斯函数),它们可以操作数值、时间以及经纬度地理坐标点这样的字段。所有三个函数都能接受以下参数:

  • origin

    中心点 或字段可能的最佳值,落在原点 origin 上的文档评分 _score 为满分 1.0

  • scale

    衰减率,即一个文档从原点 origin 下落时,评分 _score 改变的速度。(例如,每 £10 欧元或每 100 米)。

  • decay

    从原点 origin 衰减到 scale 所得的评分 _score ,默认值为 0.5

  • offset

    以原点 origin 为中心点,为其设置一个非零的偏移量 offset 覆盖一个范围,而不只是单个原点。在范围 -offset <= origin <= +offset 内的所有评分 _score 都是 1.0

在这里插入图片描述

​ 原点 origin (即中心点)的值都是 40offset5 ,也就是在范围 40 - 5 <= value <= 40 + 5 内的所有值都会被当作原点 origin 处理——所有这些点的评分都是满分 1.0

​ 在此范围之外,评分开始衰减,衰减率由 scale 值(此例中的值为 5 )和 衰减值 decay (此例中为默认值 0.5 )共同决定。结果是所有三个曲线在 origin +/- (offset + scale) 处的评分都是 0.5 ,即点 3050 处。

linearexpgauss (线性、指数和高斯)函数三者之间的区别在于范围( origin +/- (offset + scale) )之外的曲线形状:

  • linear 线性函数是条直线,一旦直线与横轴 0 相交,所有其他值的评分都是 0.0
  • exp 指数函数是先剧烈衰减然后变缓。
  • gauss 高斯函数是钟形的——它的衰减速率是先缓慢,然后变快,最后又放缓。

选择曲线的依据完全由期望评分 _score 的衰减速率来决定,即距原点 origin 的值。

如此的话搜索酒店大概可这样请求:

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

但是明明想是钱是小于100块钱酒店?为什么price的origin要50呢?在这里暂且认为用户喜欢的是越低越好。如果是设置成100的话,和100偏差较大的酒店评分就会变得很低。所以设置成50,这样50左右的,都可以进行比较友好的评分。

参考文档:官方文档

猜你喜欢

转载自blog.csdn.net/qq_30285985/article/details/107569662