Elasticsearch 定製評分(自定義評分)

Elasticsearch 的相似度算法被定義爲檢索詞頻率/反向文檔頻率, TF/IDF 。

一. 相關概念:

  1. 檢索詞頻率:tf

詞 t 在文檔 d 的詞頻( tf )是該詞在文檔中出現次數的平方根。

tf(t in d) = √frequency

檢索詞在該字段出現的頻率?出現頻率越高,相關性也越高。 字段中出現過 5 次要比只出現過 1 次的相關性高。

  1. 反向文檔頻率:idf

詞 t 的逆向文檔頻率( idf )是:索引中文檔數量除以所有包含該詞的文檔數,然後求其對數。

idf(t) = 1 + log ( numDocs / (docFreq + 1))

每個檢索詞在索引中出現的頻率?頻率越高,相關性越低。檢索詞出現在多數文檔中會比出現在少數文檔中的權重更低。

二、計算公式爲:

score(q,d)  =  queryNorm(q)  · coord(q,d)  · ∑ (tf(t,d) · idf(t)² · t.getBoost() · norm(t,d))  

其它參數定義:

  1. 字段長度準則:norm

字段長度歸一值( norm )是字段中詞數平方根的倒數。

norm(d) = 1 / √numTerms

字段的長度是多少?長度越長,相關性越低。 檢索詞出現在一個短的 title 要比同樣的詞出現在一個長的 content 字段權重更大。

  1. 查詢歸一因子

查詢歸一因子queryNorm )試圖將查詢 歸一化 ,這樣就能將兩個不同的查詢結果相比較。

這個因子是在查詢過程的最前面計算的,具體的計算依賴於具體查詢

queryNorm = 1 / √sumOfSquaredWeights 

sumOfSquaredWeights 是查詢裏每個詞的 IDF 的平方和。

以上是對於一個詞項檢索時的相關度計算,當查詢多個詞項時,得出多個相關度,則需要按照向量空間模型來計算整體的相似度:

向量空間模型:vector

向量空間模型(vector space model) 提供一種比較多詞查詢的方式,單個評分代表文檔與查詢的匹配程度

在向量空間模型裏,向量空間模型裏的每個數字都代表一個詞的 權重 ,與 詞頻/逆向文檔頻率(term frequency/inverse document frequency) 計算方式類似。

3、控制相關度

一般來說,控制相關度的需求,分爲兩種:

  1. 忽略TF/IDF
    即不需要評分,可以使用constant_score來達成,在 constant_score 查詢中,它可以包含查詢或過濾,爲任意一個匹配的文檔指定評分 1 ,忽略 TF/IDF 信息。

  2. 定製評分
    function_score 查詢 是用來控制評分過程的終極武器,它允許爲每個與主查詢匹配的文檔應用一個函數,以達到改變甚至完全替換原始查詢評分 _score 的目的。本文主要介紹使用script_score函數。

使用腳本計算評
script_score
自定義腳本可以完全控制評分計算:

{
    "function_score": {
        "functions": {
            "script_score": {
                "script": "doc['price'].value + doc['margin'].value"
            }
        }
    }
}

4、Painless

es腳本引擎,簡單安全,無痛使用,Painless使用白名單來限制函數與字段的訪問,針對es的場景來進行優化,只做es數據的操作,更加輕量級。

Painless中變量可以聲明爲基本數據類型、引用類型、字符串、void(不返回值)、數組以及動態類型。其支持下面基本類型:

byte, short, char, int, long, float, double, boolean.聲明變量與java類似:

int i = 0; double a; boolean g = true;

數組類型支持一維和多維,初始值爲null。與引用類型一樣,使用new關鍵字,併爲每個維度設置中括號

int[] x = new int[2];  
x[0] = 3;  
x[1] = 4;  

int[] b = new int[] {1,2,3,4,5};  

painless支持動態類型,elasticsearch會自動推斷類型

def a = 1;  
def b = "foo";  
def[][] h = new def[2][2];  

條件語句和運算符

Painless包含完整的操作符列表,除了它們的優先級和結合性之外,這些操作符與其他高級語言幾乎兼容。

if (doc['foo'].value = 5) {  
    doc['foo'].value *= 10;
} 
else {  
    doc['foo'].value += 10;
}

Painless支持if else,但不支持else ifswitch

循環

def total = 0;  
for (def i = 0; i < doc['scores'].length; i++) {  
    total += doc['scores'][i];
}
return total;  

5、控制相關度實踐

該實例中將使用script_score,將評分設置爲:
doc['download_cnt'].value * 2.5 +doc['replication_cnt'].value * 1.2

{
    "query": {
        "function_score": {
            "query": {
                "match": {
                    "name": "1"
                }
            },
            "functions": [
                {
                    "script_score": {
                        "script": {
                            "params": {
                                "download_ratio": 2.5,
                                "replication_ratio": 1.2
                            },
                            "lang": "painless",
                            "inline": "doc['download_cnt'].value * params.download_ratio + doc['replication_cnt'].value * params.replication_ratio"
                        }
                    }
                }
            ]
        }
    }
}

_search操作中所有的返回值,都可以通過一個map類型變量doc獲取。和所有其他腳本語言一樣,用[]獲取map中的值。這裏要強調的是,doc只可以在_search中訪問到

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