Elasticsearch全文搜索與TF/IDF

  • 2019 年 10 月 6 日
  • 筆記

一、TF/IDF

1. TF

TF:Term Frequency,即詞頻。它表示一個詞在內容(如某文章)中出現的次數。為了消除文檔本身大小的影響,通常,它的定義是:

TF = 某個詞在文檔中出現的次數 / 文檔的總詞數

也有其他表示方法,在Elasticsearch (lucene)中的使用的方法是

tf(t in d) = √frequency , 即 (某個詞t在文檔d中出現的次數) 的 平方跟

某個詞出現越多,表示它約重要。比如某篇新聞中,「劍術」出現了5次,「電視」出現了1次,很可能這是一個劍術賽事報道。

如果這篇新聞中,「中國」和「劍術」出現的次數一樣多,是不是表示兩者同等重要呢?答案是否定的,因為中國這個詞很常見,它難以表達文檔的特性。而劍術很少見,更能表達文章的特性。

某個詞越少見,就越能表達一篇文章的特性,反之則越不能。像「的」、「了」這些詞,在所有文檔中出現的頻率都特別高,以至於失去了表達文章特性的意義。人們乾脆稱它們為「停用詞」,直接從統計中忽略掉。

2. IDF

IDF(Inverse Document Frequency),即逆文檔頻率,它是一個表達詞語重要性的指標。通常,它的計算方法是:

IDF=log(語料庫中的文檔數/(包含該詞的文檔數+1))

如果所有文章都包涵某個詞,那個詞的IDF=log(1)=0, 即重要性為零。停用詞的IDF約等於0。

如果某個詞只在很少的文章中出現,則IDF很大,其重要性也越高。

為了避免分母為0,所以+1.

在Elasticsearch (lucene)中的計算方法是

idf(t) = 1 + log ( numDocs / (docFreq + 1)) , 即 1 + log ( 索引中的文檔總數 / (包含該詞的文檔數 + 1))

上述公式是文檔中給的,但實際中用的是 log(1 + (docCount – docFreq + 0.5) / (docFreq + 0.5))

TF-IDF值

TF-IDF = TF X IDF

在Elasticsearch中,還有一個概念叫 欄位長度的歸一化,Field-Length Norm.

欄位內容越短,權重越大。如果一個關鍵詞出現在較短的欄位中,比如title,就比它出現在長欄位(如簡介)中更能表達文章的特性。

norm(d) = 1 / √numTerms 即: 1 / 詞出現次數的平方根

二、elasticsearch的全文搜索

elasticsearh的全文搜索涉及到兩個重要的方面:相關性(Relevance)和分析(Analysis)

相關性(Relevance)

它是評價查詢與其結果間的相關程度,並根據這種相關程度對結果排名的一種能力,這種計算方式可以是 TF/IDF 方法(參見 相關性的介紹)、地理位置鄰近、模糊相似,或其他的某些演算法。本文只介紹TF/IDF方法。

TF/IDF 相關性方法分析

做一次搜索,帶explain,elasticsearch會返回如何匹配。比如在title欄位中進行全文搜索,關鍵詞為'python'

GET course/_search?explain  {    "query": {      "multi_match" : {        "query":    "python",        "fields": [ "title" ]      }    },"size":10  }

返回內容中,第一條匹配的結果如下

{          "_shard": "[course][0]",          ...          "_score": 6.1884723,          "_source": {            "title": "Python 語句",            ...          },          "_explanation": {            "value": 6.1884723,            "description": "weight(title:python in 1363) [PerFieldSimilarity], result of:",            "details": [              {                "value": 6.1884723,                "description": "score(doc=1363,freq=1.0 = termFreq=1.0n), product of:",                "details": [                  {                    "value": 4.4812255,                    "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",                    "details": [                      {                        "value": 17,                        "description": "docFreq",                        "details": []                      },                      {                        "value": 1545,                        "description": "docCount",                        "details": []                      }                    ]                  },                  {                    "value": 1.3809776,                    "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": 7.861489,                        "description": "avgFieldLength",                        "details": []                      },                      {                        "value": 2.56,                        "description": "fieldLength",                        "details": []                      }                    ]                  }                ]              }            ]          }        }

解釋

"value": 4.4812255,  "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",

上面是求idf值的值。idf=ln(1 + (docCount – docFreq + 0.5) / (docFreq + 0.5))=ln(1+(1545-17+0.5)/(17+0.5)) = ln(88.34)=4.4812255

//返回內容里用了log(以10為底的對數), 實際是ln (以e為底的對數)

"value": 1.3809776,  "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",

上面是求tfNorm(歸一化後的TF)的值。根據描述tfNorm = (freq * (k1 + 1)) / (freq + k1 * (1 – b + b * fieldLength / avgFieldLength))

其中termFreq=1,k1=1.2, b=0.75, avgFieldLength=7.861489, fieldLength=2.56。

為什麼要用這樣的公式,以及k1和b的值是怎麼來的,我也不清楚。

計算最終結果,tfNorm=1.38.

"value": 6.1884723,  "description": "score(doc=1363,freq=1.0 = termFreq=1.0n), product of:",

最終得分 TF-IDF = TF * IDF =4.4812255 * 1.3809776 = 6.1884723