Practical BM25 - Part 1: 分片是如何影響Elasticseach相關性評分的
《Practical BM25》系列文章來自於elastic官方部落格,共分為三部分,講解了Elasticsearch的預設相似度演算法BM25的原理。本篇為第一部分的中文翻譯,原文連結ofollow,noindex" target="_blank">Practical BM25 - Part 1: How Shards Affect Relevance Scoring in Elasticsearch
背景
我們在Elasticsearch 5.0 中,把預設的相似度演算法 換成了Okapi_BM25 ,用它來計算某條查詢結果的得分。我不想在這篇文章中對BM25與其他的備選演算法做深入的對比,如果你想學習BM25的理論,可以看看Elastic{ON} 2016上的BM25 Demystified 這個演講。在本文中,我希望能從實踐的角度來介紹BM25,包括哪些引數是可用的,以及什麼會影響評分。
記住,這篇文章主要關於對文字進行打分。也就是說,我們關注的是用搜索功能的使用者。如果你只是索引一些日誌或效能指標,並在查詢結果時只是按照特定的元資料/數字順序(如時間戳)排序,那麼這篇文章大概只能用來滿足你的好奇心。
理解分片如何影響評分
為了讓你能跟上我的節奏,我們首先要理解的是,分片數大於1是如何影響打分的,因為Elasticsearch在每個索引上預設使用5個主分片。我們先建立一個叫做"people"的索引。我這裡給出的settings其實是預設的情形(因此無需特意去定義),我這裡這樣定義了一下是為了進行演示。我將用我名字的變體(“Shane Connelly”) 來進行演示,你在嘗試時,可以用任意的名字來進行替換。
PUT people { "settings": { "number_of_shards": 5, "index" : { "similarity" : { "default" : { "type" : "BM25" } } } } }
現在,讓我新增一篇文件進去,然後進行搜尋,首先,只把我的名字加進去。
PUT /people/_doc/1 { "title": "Shane" } GET /people/_doc/_search { "query": { "match": { "title": "Shane" } } }
你會獲得一條結果,它的得分是0.2876821。我們稍後會研究這個得分是怎麼來的。但我們先來看看,當我們新增一些我的名字的不同變體為內容的文件進去時會發生什麼。
PUT /people/_doc/2 { "title": "Shane C" } PUT /people/_doc/3 { "title": "Shane Connelly" } PUT /people/_doc/4 { "title": "Shane P Connelly" }
現在,再次執行和剛才相同的查詢。
GET /people/_doc/_search { "query": { "match": { "title": "Shane" } } }
這時,你應該能得到4條結果,但如果你看看各條結果的得分,可能會一臉懵逼。文件1和3的分數都是0.2876821,但文件2的得分是0.19856805,文件4的得分是0.16853254。這簡直是在勸退新人。我們能看到,文件2和3非常相似——它們都有2個term,並且都匹配了"shane",但是文件2的分數更低。你可能開始假設,"C"和"Connelly"的打分可能有什麼區別,但事實上,這與文件們如何寫入各個分片有關。
提醒一下,Elasticsearch把文件分別寫入不同的分片 中,每個分片儲存了一部分資料(文件)。
GET /_cat/shards/people?v
如果你執行它,你會看到分片2中有2篇文件,而分片3和4都只有1篇文件。(分片0和1中還沒有文件)這意味著,對於"shane"這個term,在這幾個分片中的出現次數是不同的,而這就是造成得分差異的根本原因。預設情況下,Elasticsearch以每個分片為基準計算評分 。
有人剛把幾篇文件寫入索引中,就開始問“為什麼文件A的得分比文件B的得分更高/低呢?”之類的問題,有些時候問題的答案就是使用者的分片數與文件數比率相對較高,導致了不同分片之間的評分偏差嚴重。有幾種方法可以讓不同的分片評分更一致:
- 你寫入索引的文件越多,分片的term統計就更一般化。當文件足夠多時,你可能就難以察覺到不同分片term統計及得分之間細微的差異了。
-
你可以用更少的分片數來減少詞頻的統計偏差。例如,如果我們將索引settings中
number_of_shards
設定為1
,我們就能得到完全不同的分數。我們會看到文件1的分數是0.13245322,文件2和3的分數都是0.105360515,文件4的分數是0.0874691。對於不同的主分片數量,我們有一些折中處理,這個問題在我們的quantitative cluster sizing webinar 討論過。 -
你可以在請求中新增
?search_type=dfs_query_then_fetch
引數,它會先收集分佈詞頻(DFS = Distributed Frequency Search 分佈頻度搜索),然後用它們來計算得分。事實上,這樣得出的結果與只有1個分片時是一樣的。我們觀察一下有和沒有"search_type"引數時,結果有何不同:
GET /people/_doc/_search?search_type=dfs_query_then_fetch { "query": { "match": { "title": "Shane" } } }
它的結果與設定了number_of_shards=1
時相同。你可能要問,“這樣能生成更精確的評分,為什麼它不是預設開啟的呢?”答案是在收集所有統計資訊的過程中,它增加了額外的一次請求往返,在某些場景中(即分數的精確性沒有速度重要時),這樣多一次請求是沒有必要的。另外,當分片中有足夠的資料是,統計數字將非常接近其他分片,進行多次請求就更無必要。如果你有足夠多的資料,search_type=dfs_query_then_fetch
這個引數只有在分片間資料持續性地不均勻分佈時才有用,例如在一些自定義路由
的場景中。
現在,我們知道了分片是如何影響評分的(也知道了如何調整它)。下一步,我們將會研究BM25演算法,看看不同的變數是如何起作用的。