Elasticsearch從入門到放棄:淺談算分

今天來聊一個 Elasticsearch 的另一個關鍵概念——相關性算分。在查詢 API 的結果中,我們經常會看到 _score 這個字段,它就是用來表示相關性算分的字段,而相關性就是描述一個文檔和查詢語句的匹配程度。

打分的本質其實就是排序,Elasticsearch 會把最符合用戶需求的文檔排在最前面。

在 Elasticsearch 5.0 之前,相關性算分算法採用的是 TF-IDF 算法,而在5.0之後採用的是 BM 25 算法。聽到這也許你會比較疑惑,想知道這兩個算法到底是怎麼樣的。別急,下面我們來具體了解一下。

TF-IDF

首先來看字面意思,TF 是 Term Frequency 的縮寫,也就是詞頻。IDF 是 Inverse Document Frequency 的縮寫,也就是逆文檔頻率。

詞頻

詞頻比較好理解,就是要搜索的目標單詞在文檔中出現的頻率。算式為檢索詞出現的次數除以文檔的總字數。最簡單的相關性算法就是將檢索詞進行分詞後對他們的詞頻進行相加。例如,我要搜索「我的算法」,其相關性就可以表示為:

TF(我) + TF(的) + TF(算法)

但這裡也有些問題,像「的」這樣的詞,雖然出現的次數很多,但是對貢獻的相關度幾乎沒有用處。所以在考慮相關度時不應該考慮他們,對於這類詞,我們統稱為 Stop Word。

逆文檔頻率

聊完了 TF,我們再來看看 IDF,在了解逆文檔頻率之前,首先需要知道什麼是文檔頻率,也就是 DF。

DF 其實是檢索詞在所有文檔中出現的頻率。例如,「我」在較多的文檔中出現,「的」在非常多的文檔中都會出現,而「算法」只會在較少的文檔中出現。這就是文檔頻率,那逆文檔頻率,簡單理解就是:

log(全部文檔數 / 檢索詞出現過的文檔總數)

針對上面的例子,我們將它更具體的呈現一下。假設我們文檔總數為1億,出現「我」字的文檔有5000萬,那麼它的 IDF 就是 log(2) = 1 。「的」在1億文檔中都有出現,IDF 就是 log(1) = 0,而算法只在20萬個文檔中出現,那麼它的 IDF 就是 log(500) ,大約是8.96。

由此可見,IDF 越大的單詞越重要。

好了,現在各位 TF 和 IDF 應該都有一定的了解了,那麼 TF-IDF 本質上就是對 TF 進行一個加權求和。

TF(我) * IDF(我) + TF(的) * IDF(的) + TF(算法) * IDF(算法)

BM 25

BM25可以看作是對 TF-IDF 的一個優化,其優化的效果是,當 TF 無限增加時, TF-IDF 的結果會隨之增加,而 BM 25 的結果會趨近於一個數值。這就限制了一個 term 對於檢索詞整體相關性的影響。

BM25算法的公式如下:

BM25

想要詳細了解BM25算法的同學可以參考這篇文章BM25 The Next Generation of Lucene Relevance

Explain API

如果想要了解一個查詢是如何進行打分的,我們可以使用 Elasticsearch 提供的 Explain API,其用法非常簡單,只需要在參數中增加

"explain": true

也可以在 path 中增加 _explain,例如:

curl -X GET "localhost:9200/my-index-000001/_explain/0?pretty" -H 'Content-Type: application/json' -d'
{
  "query" : {
    "match" : { "message" : "elasticsearch" }
  }
}
'

這時,返回結果中就會有一個 explanation 字段,用來描述具體的算分過程。

小結

關於 Elasticsearch 的算分,相信各位也有一個初步的認識了,如果感興趣的話也可以自己進行更加深入的研究,也歡迎各位和我一起交流。