Elasticsearch系列---相關性評分算法及正排索引

概要

上一篇中多次提到了按相關性評分,本篇我們就來簡單瞭解一下相關性評分的算法,以及正排索引排序的優勢。

評分算法

Elasticsearch進行全文搜索時,Boolean Model是匹配的基礎,先用boolean model將匹配的文檔挑選出來,然後再運用評分函數計算相關度,參與的函數如我們提到的TF/IDF、Length Norm等,再加上一些控制權重的參數設置,得到最後的評分。

Boolean Model

作爲全文搜索的基礎,Boolean Model的結果決定文檔是否要進行下一步的評分操作,使用AND、OR、NOT這種邏輯操作符來判斷查找的文檔,整個過程不評分,非常快速地將匹配的文檔篩選出來。

由於Elastisearch各個版本相關度評分算法有異同,我們以6.3.1版本的BM25算法爲準。

TFNORM/IDF

由Boolean Model之後得到的文檔,我們開始計算文檔的評分,每個文檔的評分取決於每個關鍵詞在文檔中的權重,權重我們會從以下幾個方面考慮:

TFNORM

即詞頻長度(Term Frequency Norm),單個term在文檔中出現的頻率,並結合字段長度,出現次數越高,字段長度越低,分越高,計算公式:

tfNorm(t in d) = (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength))

詞t在文檔d的詞頻(tf):freq表示出現頻率,k1、b爲ES參數,fieldLength爲該字段長度,avgFieldLength爲平均字段長度,公式瞭解一下即可。

IDF

即逆向文檔頻率(inversed document frequency),單個term在所有文檔裏出現的頻率是多少?出現次數越高,分越低,計算公式:

idf(t) = log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5))

詞 t 的逆向文檔頻率(idf):索引中文檔數量與該詞的文檔數比率,然後求其對數

例如:"and"、"the","的"、"了"、"呢"這種詞在文檔裏到處都是,出現的頻率特別高,反倒是不常出現的詞"Elastic","成都"可以幫助我們快速縮小範圍找到感興趣的文檔。

結果合併

一個term經過上面兩個算法計算後,會得到兩個不同的值,這兩個得分相乘得到一個綜合性的分數,這個分數就是最終的_score

我們用explain來看一下這個綜合分數的詳細信息:

搜索請求:

GET /music/children/_search
{
  "explain": true,
  "query": {
    "term": {
      "name": "teeth"
    }
  }
}

響應結果:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.73617005,
    "hits": [
      {
        "_explanation": {
          "value": 0.7361701,
          "description": "weight(name:teeth in 1) [PerFieldSimilarity], result of:",
          "details": [
            {
              "value": 0.7361701,
              "description": "score(doc=1,freq=1.0 = termFreq=1.0\n), product of:",
              "details": [
                {
                  "value": 0.6931472,
                  "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
                  "details": [
                    {
                      "value": 1,
                      "description": "docFreq",
                      "details": []
                    },
                    {
                      "value": 2,
                      "description": "docCount",
                      "details": []
                    }
                  ]
                },
                {
                  "value": 1.0620689,
                  "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
                  "details": [
                    {
                      "value": 1,
                      "description": "termFreq=1.0",
                      "details": []
                    },
                    {
                      "value": 1.2,
                      "description": "parameter k1",
                      "details": []
                    },
                    {
                      "value": 0.75,
                      "description": "parameter b",
                      "details": []
                    },
                    {
                      "value": 3.5,
                      "description": "avgFieldLength",
                      "details": []
                    },
                    {
                      "value": 3,
                      "description": "fieldLength",
                      "details": []
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    ]
  }
}

_explanation節點,裏面包含了description、value、details字段,從這裏我們可以知道計算的類型,計算結果和任何我們需求的計算細節。從示例中我們可以看到IDF和TF的計算公式和計算結果。

我們可以看到最終得分是0.73617005,其中tfNorm得分1.0620689,idf得分0.6931472,經過相乘1.0620689 * 0.6931472 = 0.73617005

多個term查詢

上面的案例是隻有一個term查詢的,如果有多個term查詢,如:

GET /music/children/_search
{
  "explain": true,
  "query": {
    "match": {
      "name": "your teeth"
    }
  }
}

我們可以看到,總的_score就是將每個term查詢的_score求和。

Lucene公式

這裏我們先看query score的計算公式:

我們從左往右看,公式依次的含義如下:

  • score(D,Q):這個公式最終的結果,Q表示query,D表示doc,表示一個query對一個doc的最終的總得分。
  • IDF(qi):idf算法對一個term的值。
  • f(xxx)/xxx:這一大串公式,即tf norm的計算公式。
  • ∑ 求和符號:idf和tf norm結果相乘,最後求和。

這個求相關性分數的公式瞭解一下即可,可以結合上面的案例去理解這個公式。

這個公式可以參見wiki: [Okapi BM25]https://en.wikipedia.org/wiki/Okapi_BM25

文檔是如何被匹配到的

如果對搜索的結果有異議,我們可以指定文檔ID進行查看,該文檔爲什麼能被匹配上,也是使用explain參數,示例如下:

GET /music/children/4/_explain
{
  "query": {
    "match": {
      "content": "wake up morning"
    }
  }
}

4爲文檔ID,此請求的含義表示針對如下的搜索條件,文檔ID爲4的記錄是爲何能匹配上的,響應的結果也是非常長,節選一部分:

{
  "_index": "music",
  "_type": "children",
  "_id": "4",
  "matched": true,
  "explanation": {
    "value": 0.9549814,
    "description": "sum of:",
    "details": [
      {
        "value": 0.62577873,
        "description": "weight(content:wake in 0) [PerFieldSimilarity], result of:",
        "details": [
          {
            "value": 0.62577873,
            "description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
            "details": [
              {
                "value": 0.6931472,
                "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
                "details": [
                  {
                    "value": 1,
                    "description": "docFreq",
                    "details": []
                  },
                  {
                    "value": 2,
                    "description": "docCount",
                    "details": []
                  }
                ]
              },

注意關鍵屬性matched,如果是true,則explanation會有非常詳細的信息,每個分詞後的關鍵詞,相關性得分是多少,都會詳細列舉出來,很多匹配過程的細節,都能在上面找到證據。如果是false,響應報文則是這樣:

{
  "_index": "music",
  "_type": "children",
  "_id": "1",
  "matched": false,
  "explanation": {
    "value": 0,
    "description": "No matching clauses",
    "details": []
  }
}

這是一個非常實用的工具,研發過程中出現讓人困惑的搜索結果,都可以用它進行分析。

正排索引

ES在索引文檔時,會建立倒排索引,倒排索引的檢索性能非常高,但對排序來說,卻不是理想的結構。

因此ES索引文檔時,還會建立正排索引,即Doc Values,這是一種列式存儲結構,默認情況下每個字段都會存儲到Doc Values裏。

所以整個搜索排序過程中,正排搜索和倒排搜索是這樣配合的:

  • 倒排索引負責關鍵詞的檢索,快速得到匹配的結果集。
  • 正排索引完成排序、過濾、聚合的功能,得到期望的文檔順序。
應用場景

Elasticsearch中的Doc Values常被應用到以下場景:

  • 對某個字段排序
  • 對某個字段聚合
  • 對某個字段過濾
  • 某些與字段相關的計算、腳本執行等
性能

Doc Values是被保存到磁盤上的,如果os cache內存足夠,整個working set將自動緩存到內存中,性能非常高,如果內存不充裕,Doc Values會將其寫入到磁盤上。
整體來說,性能還是可以的,合理的os cache設置,可以極大地提高查詢的性能。

小結

本篇主要介紹了相關性評分算法的基礎知識,能夠使用工具查看評分的詳細過程,可以輔助解釋一些困惑的現象,最後簡單介紹了一下正排索引的應用場景。

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