實戰 | Elasticsearch自定義評分的N種方法

1、期望Elasticsearch搜索結果更準確,不可迴避的三個問題

問題1:用戶真正的需求是什麼? 

如果不能獲得用戶的搜索意圖,搜索的準確性無從談起。

比如:同樣輸入“錘子”,工匠期望的是釘子對應的“錘子”,老羅的粉絲期望的是“錘子科技”、“錘子便籤”、“錘子手機”等。

即使同一用戶發出的同一個查詢,也可能因爲用戶所處場景不同,其期望結果也存在很大差異。

問題2:哪些信息是和用戶需求真正相關的?

搜索引擎本質是一個匹配過程,即從海量的數據中找到匹配用戶需求的內容。

判定內容與用戶查詢的相關性(relevance,如上圖),一直是搜索引擎領域核心研究課題之一。

Relevance is a search engine’s holy grail. People want results that are closely connected to their queries.— Marc Ostrofsky

https://medium.com

問題3:哪些信息是最值得用戶信賴的?

衡量信息滿足用戶需求的兩個核心屬性。

  • 其一:如前所述的相關性。

  • 其二:信息是否值得信賴。

舉例:疫情環境下,新華網、人民網發佈文章的可信性遠大於某公衆號大V發佈的。

2、Elasticsearch相關性是如何控制的?

結構化數據庫如Mysql,只能查詢結果與數據庫中的row的是否匹配?回答往往是“是”、“否”。

如:

select title from hbinfos where title like ‘%發熱%’。

而全文搜索引擎Elasticsearch中不僅需要找到匹配的文檔,還需根據它們相關度的高低進行排序。

實現相關度排序的核心概念是評分

_score就是Elasticsearch檢索返回的評分。該得分衡量每個文檔與查詢的匹配程度。

 "hits" : [
      {
        "_index" : "kibana_sample_data_flights",
        "_type" : "_doc",
        "_id" : "FHLWlHABl_xiQyn7bHe2",
        "_score" : 3.4454226,

每個文檔都有與之相關的評分,該得分由正浮點數表示。文檔分數越高,則文檔越相關。

分數與查詢匹配成正比。查詢中的每個子句都將有助於文檔的得分。

3、Elasticsearch  如何計算評分?

官方文檔相關度評分背後的理論解讀如下:

Lucene(或 Elasticsearch)使用 布爾模型查找匹配文檔,並用一個名爲 實用評分函數的公式來計算相關度。這個公式借鑑了 詞頻/逆向文檔頻率和 向量空間模型,同時也加入了一些現代的新特性,如協調因子(coordination factor),字段長度歸一化(field length normalization),以及詞或查詢語句權重提升。

Elasticsearch 5 之前的版本,評分機制或者打分模型基於 TF-IDF實現。

注意:從Elasticsearch 5之後, 缺省的打分機制改成了Okapi BM25

BM25 的 BM 是縮寫自 Best Match, 25 貌似是經過 25 次迭代調整之後得出的算法,它也是基於 TF/IDF 進化來的。

3.1 TF-IDF與BM25 的相同點

TF-IDF 和 BM25 同樣使用 逆向文檔頻率 來區分普通詞(不重要)和非普通詞(重要),同樣認爲:

  • 文檔裏的某個詞出現次數越頻繁,文檔與這個詞就越相關,得分越高。

  • 某個詞在集合所有文檔裏出現的頻率是多少?頻次越高,權重 越低,得分越低 。某個詞在集合中所有文檔中越罕見,得分越高。

3.2 TF-IDF與BM25 的不同點

BM25在傳統TF-IDF的基礎上增加了幾個可調節的參數,使得它在應用上更佳靈活和強大,具有較高的實用性。

  • 傳統的TF值理論上是可以無限大的。而BM25與之不同,它在TF計算方法中增加了一個常量k,用來限制TF值的增長極限。下面是兩者的公式:

傳統 TF Score = sqrt(tf)
BM25的 TF Score = ((k + 1) * tf) / (k + tf)
  • BM25還引入了平均文檔長度的概念,單個文檔長度對相關性的影響力與它和平均長度的比值有關係。BM25的TF公式裏,除了常量k外,引入另外兩個參數:L和b。

(1)L是文檔長度與平均長度的比值。如果文檔長度是平均長度的2倍,則L=2。

(2)b是一個常數,它的作用是規定L對評分的影響有多大。加了L和b的公式變爲:

TF Score = ((k + 1) * tf) / (k * (1.0 - b + b * L) + tf)

更多細節原理推薦:

https://blog.mimacom.com/elasticsearch-scoring-algorithm-changes/

https://blog.mimacom.com/bm25-got/

4、Elasticsearch 哪些查詢影響相關性評分?

布爾查詢中的每個must,should和must_not元素稱爲查詢子句。

  • 文檔滿足must或 should條款的標準的程度有助於文檔的相關性得分。分數越高,文檔就越符合您的搜索條件。

  • must_not子句中的條件被視爲過濾器。它會影響文檔是否包含在結果中,但不會影響文檔的評分方式。在must_not裏還可以顯式指定任意過濾器,以基於結構化數據包括或排除文檔。

  • filter:必須 匹配,但它以不評分、過濾模式來進行。filter內部語句對評分沒有貢獻,只是根據過濾標準來排除或包含文檔。

一句話概括:filter、must_not不影響評分,其他影響評分

5、Elasticsearch 如何自定義評分?

這裏說是自定義評分,核心還是通過修改評分修改文檔相關性,在最前面返回用戶最期望的結果。

5.1 Index Boost 索引層面修改相關性

5.1.1 原理說明

允許在跨多個索引搜索時爲每個索引配置不同的級別。

5.1.2 適用場景

索引級別調整評分。

5.1.3 實戰舉例:

一批數據裏,有不同的標籤,數據結構一致,不同的標籤存儲到不同的索引(A、B、C),最後要嚴格按照標籤來分類展示的話,用什麼查詢比較好?

要求:先展示A類,然後B類,然後C類

elasticsearch.cn

PUT index_a/_doc/1
{
  "subject": "subject 1"
}
PUT index_b/_doc/1
{
  "subject": "subject 1"
}
PUT index_c/_doc/1
{
  "subject": "subject 1"
}


GET index_*/_search
{
  "indices_boost": [
    {
      "index_a": 1.5
    },
    {
      "index_b": 1.2
    },
    {
      "index_c": 1
    }
  ],
  "query": {
    "term": {
      "subject.keyword": {
        "value": "subject 1"
      }
    }
  }
}

5.2 boosting 修改文檔相關性

boosting分爲兩種類型:

  • 第一種:索引期間修改文檔的相關性。

PUT my_index
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "boost": 2
      }
    }
  }
}

索引期間修改相關性的弊端非常明顯:修改boost值的唯一方式是重建索引,reindex數據,成本太高了

  • 第二種:查詢的時候修改文檔的相關性。

本小節着重講解:查詢時候修改文檔相關性。

5.2.1 原理說明

通過boosting修改文檔相關性。

  • boost取值:0 - 1 之間的值,如:0.2,代表降低評分;

  • boost取值:> 1, 如:1.5,代表提升評分。

5.2.2 適用場景

自定義修改滿足某個查詢條件的評分。

5.2.3 實戰一把

POST _search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "pingpang best",
            "fields": [
              "title^3",
              "content"
            ]
          }
        }
      ],
      "should": [
        {
          "term": {
            "user": {
              "value": "Kimchy",
              "boost": 0.8
            }
          }
        },
        {
          "match": {
            "title": {
              "query": "quick brown fox",
              "boost": 2
            }
          }
        }
      ]
    }
  }
}

5.3 negative_boost 降低相關性

5.3.1 原理說明

  • negative_boost 對 negative部分query生效,

  • 計算評分時,boosting部分評分不修改,negative部分query 乘以 negative_boost值。

  • negative_boost 取值:0-1.0,舉例:0.3。

5.3.2 適用場景

對某些返回結果不滿意,但又不想排除掉(must_not),可以考慮boosting query的negative_boost。

5.3.3 實戰一把

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

5.4 function_score 自定義評分

5.4.1 原理說明

支持用戶自定義一個或多個查詢或者腳本,達到精細化控制評分的目的。

5.4.2 適用場景

支持針對複雜查詢的自定義評分業務場景。

5.4.3 實戰一把

實戰問題1:如何同時根據 銷量和瀏覽人數進行相關度提升?

問題來源https://elasticsearch.cn/question/4345

問題描述:針對商品,例如有

商品銷量瀏覽人數
A1010
B2020
C3030

想要有一個提升相關度的計算,同時針對銷量和瀏覽人數

例如oldScore*(銷量+瀏覽人數) field_value_factor好像只能支持單個field 求大神解答?

解答,可以藉助:script_score實現。

實戰如下:

PUT product_test/_bulk
{"index":{"_id":1}}
{"name":"A","sales":10,"visitors":10}
{"index":{"_id":2}}
{"name":"B","sales":20,"visitors":20}
{"index":{"_id":3}}
{"name":"C","sales":30,"visitors":30}

POST product_test/_search
{
  "query": {
    "function_score": {
      "query": {
        "match_all": {}
      },
      "script_score": {
        "script": {
          "source": "_score * (doc['sales'].value+doc['visitors'].value)"
        }
      }
    }
  }
}

實戰問題2:基於文章點贊數計算評分。以下:title代表文章標題;like:代表點贊數。

期望評分標準:基於點贊數評分,且最終評分相對平滑。

核心原理:field_value_factor函數使用文檔中的字段來影響得分。

DELETE news_index
POST news_index/_bulk
{"index":{"_id":1}}
{"title":"ElasticSearch原理- 神一樣的存在"}
{"index":{"_id":2}}
{"title":"Elasticsearch 快速開始","like":5}
{"index":{"_id":3}}
{"title":"開源搜索與分析· Elasticsearch","like":10}
{"index":{"_id":4}}
{"title":"銘毅天下 死磕Elasticsearch", "like":1000}

GET news_index/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "Elasticsearch"
        }
      },
      "field_value_factor": {
        "field": "like",
        "modifier": "log1p",
        "factor": 0.1,
        "missing": 1
      },
      "boost_mode": "sum"
    }
  }
}

注意:

  • 評分計算公式解讀:new_score = old_score + log(1 + 0.1 * like值)

  • missing含義:使用 field_value_factor 時要注意,有的文檔可能會缺少這個字段,加上 missing 來個這些缺失字段的文檔一個缺省值

5.4.4 實戰常見問題

星球提問:有沒有辦法讓同一個索引裏面對固定的查詢返回的相關性評分是在固定的範圍之內的?比如0-100分這樣的?

這樣就可以知道對某些詞語或文檔的搜索,在索引裏面是否有滿足相關性的文檔了。

回答:

  • 參數1:"modifier": "log1p",使得評分結果平滑。

  • 參數2:max_boost

通過設置max_boost參數,可以將新分數限制爲不超過特定限制。

max_boost的默認值爲FLT_MAX。

#define FLT_MAX 3.402823466e+38F

5.5 查詢後二次打分rescore_query

5.5.1 原理說明

二次評分是指重新計算查詢返回結果文檔中指定個數文檔的得分,Elasticsearch會截取查詢返回的前N個,並使用預定義的二次評分方法來重新計算他們的得分。

5.5.2 適用場景

對查詢語句的結果不滿意,需要重新打分的場景。

但,如果對全部有序的結果集進行重新排序的話勢必開銷會很大,使用rescore_query只對結果集的子集進行處理。

5.5.3 實戰一把

在5.4基礎上實戰

GET news_index/_search
{
  "query": {
    "exists": {
      "field": "like"
    }
  },
  "rescore": {
    "window_size": 50,
    "query": {
      "rescore_query": {
        "function_score": {
          "script_score": {
            "script": {
              "source": "doc.like.value"
            }
          }
        }
      }
    }
  }
}

window_size含義

query rescorer僅對query和 post_filter階段返回的前K個結果執行第二個查詢。

每個分片上要檢查的文檔數量可由window_size參數控制,默認爲10。

6、小結

本文主要探討了Elasticsearch相關性、打分機制、不同自定義評分的原理、適用場景,並結合實戰業務進行解讀。

更多的自定義評分機制的細節需要大家參閱官方文檔詳細解讀。


參考:

[1] Elasticsearch官方文檔

[2] Elastic中文社區

[3] 死磕Elasticsearch知識星球

[4] https://medium.com/@nschsravanthi/customize-relevance-with-elasticsearch-7735a9ac550e

[5] https://livebook.manning.com/book/elasticsearch-in-action

[6] 《這就是搜索引擎》

轉自:銘毅天下

作者:銘毅天下

推薦下我的開源項目:

https://github.com/yinjihuan/kitty-cloud

熱文推薦

RedisTemplate:我不背鍋,是你用錯了

得虧了它,我才把潛藏那麼深的Bug挖出來

驚訝!緩存剛Put再Get居然獲取不到?

好機會,我要幫女同事解決Maven衝突問題

上線前一個小時,dubbo這個問題可把我折騰慘了

爲了控制Bean的加載我使出了這些殺手鐗

如有收穫,點個在看,誠摯感謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章