在進(jìn)行Lucene的搜索過程解析之前,有必要單獨(dú)的一張把Lucene score公式的推導(dǎo),各部分的意義闡述一下。因?yàn)長(zhǎng)ucene的搜索過程,很重要的一個(gè)步驟就是逐步的計(jì)算各部分的分?jǐn)?shù)。
Lucene的打分公式非常復(fù)雜,如下:
在推導(dǎo)之前,先逐個(gè)介紹每部分的意義:
- t:Term,這里的Term是指包含域信息的Term,也即title:hello和content:hello是不同的Term
- coord(q,d):一次搜索可能包含多個(gè)搜索詞,而一篇文檔中也可能包含多個(gè)搜索詞,此項(xiàng)表示,當(dāng)一篇文檔中包含的搜索詞越多,則此文檔則打分越高。
- queryNorm(q):計(jì)算每個(gè)查詢條目的方差和,此值并不影響排序,而僅僅使得不同的query之間的分?jǐn)?shù)可以比較。其公式如下:

- tf(t in d):Term t在文檔d中出現(xiàn)的詞頻
- idf(t):Term t在幾篇文檔中出現(xiàn)過
- norm(t, d):標(biāo)準(zhǔn)化因子,它包括三個(gè)參數(shù):
- Document boost:此值越大,說明此文檔越重要。
- Field boost:此域越大,說明此域越重要。
- lengthNorm(field) = (1.0 / Math.sqrt(numTerms)):一個(gè)域中包含的Term總數(shù)越多,也即文檔越長(zhǎng),此值越小,文檔越短,此值越大。
- 各類Boost值
- t.getBoost():查詢語句中每個(gè)詞的權(quán)重,可以在查詢中設(shè)定某個(gè)詞更加重要,common^4 hello
- d.getBoost():文檔權(quán)重,在索引階段寫入nrm文件,表明某些文檔比其他文檔更重要。
- f.getBoost():域的權(quán)重,在索引階段寫入nrm文件,表明某些域比其他的域更重要。
以上在Lucene的文檔中已經(jīng)詳細(xì)提到,并在很多文章中也被闡述過,如何調(diào)整上面的各部分,以影響文檔的打分,請(qǐng)參考有關(guān)Lucene的問題(4):影響Lucene對(duì)文檔打分的四種方式一文。
然而上面各部分為什么要這樣計(jì)算在一起呢?這么復(fù)雜的公式是怎么得出來的呢?下面我們來推導(dǎo)。
首先,將以上各部分代入score(q, d)公式,將得到一個(gè)非常復(fù)雜的公式,讓我們忽略所有的boost,因?yàn)檫@些屬于人為的調(diào)整,也省略coord,這和公式所要表達(dá)的原理無關(guān)。得到下面的公式:
然后,有Lucene學(xué)習(xí)總結(jié)之一:全文檢索的基本原理中的描述我們知道,Lucene的打分機(jī)制是采用向量空間模型的:
我們把文檔看作一系列詞(Term),每一個(gè)詞(Term)都有一個(gè)權(quán)重(Term weight),不同的詞(Term)根據(jù)自己在文檔中的權(quán)重來影響文檔相關(guān)性的打分計(jì)算。
于是我們把所有此文檔中詞(term)的權(quán)重(term weight) 看作一個(gè)向量。
Document = {term1, term2, …… ,term N}
Document Vector = {weight1, weight2, …… ,weight N}
同樣我們把查詢語句看作一個(gè)簡(jiǎn)單的文檔,也用向量來表示。
Query = {term1, term 2, …… , term N}
Query Vector = {weight1, weight2, …… , weight N}
我們把所有搜索出的文檔向量及查詢向量放到一個(gè)N維空間中,每個(gè)詞(term)是一維。
我們認(rèn)為兩個(gè)向量之間的夾角越小,相關(guān)性越大。
所以我們計(jì)算夾角的余弦值作為相關(guān)性的打分,夾角越小,余弦值越大,打分越高,相關(guān)性越大。
余弦公式如下:
下面我們假設(shè):
查詢向量為Vq = <w(t1, q), w(t2, q), ……, w(tn, q)>
文檔向量為Vd = <w(t1, d), w(t2, d), ……, w(tn, d)>
向量空間維數(shù)為n,是查詢語句和文檔的并集的長(zhǎng)度,當(dāng)某個(gè)Term不在查詢語句中出現(xiàn)的時(shí)候,w(t, q)為零,當(dāng)某個(gè)Term不在文檔中出現(xiàn)的時(shí)候,w(t, d)為零。
w代表weight,計(jì)算公式一般為tf*idf。
我們首先計(jì)算余弦公式的分子部分,也即兩個(gè)向量的點(diǎn)積:
Vq*Vd = w(t1, q)*w(t1, d) + w(t2, q)*w(t2, d) + …… + w(tn ,q)*w(tn, d)
把w的公式代入,則為
Vq*Vd = tf(t1, q)*idf(t1, q)*tf(t1, d)*idf(t1, d) + tf(t2, q)*idf(t2, q)*tf(t2, d)*idf(t2, d) + …… + tf(tn ,q)*idf(tn, q)*tf(tn, d)*idf(tn, d)
在這里有三點(diǎn)需要指出:
- 由于是點(diǎn)積,則此處的t1, t2, ……, tn只有查詢語句和文檔的并集有非零值,只在查詢語句出現(xiàn)的或只在文檔中出現(xiàn)的Term的項(xiàng)的值為零。
- 在查詢的時(shí)候,很少有人會(huì)在查詢語句中輸入同樣的詞,因而可以假設(shè)tf(t, q)都為1
- idf是指Term在多少篇文檔中出現(xiàn)過,其中也包括查詢語句這篇小文檔,因而idf(t, q)和idf(t, d)其實(shí)是一樣的,是索引中的文檔總數(shù)加一,當(dāng)索引中的文檔總數(shù)足夠大的時(shí)候,查詢語句這篇小文檔可以忽略,因而可以假設(shè)idf(t, q) = idf(t, d) = idf(t)
基于上述三點(diǎn),點(diǎn)積公式為:
Vq*Vd = tf(t1, d) * idf(t1) * idf(t1) + tf(t2, d) * idf(t2) * idf(t2) + …… + tf(tn, d) * idf(tn) * idf(tn)
所以余弦公式變?yōu)椋?/p>
下面要推導(dǎo)的就是查詢語句的長(zhǎng)度了。
由上面的討論,查詢語句中tf都為1,idf都忽略查詢語句這篇小文檔,得到如下公式
所以余弦公式變?yōu)椋?/p>
下面推導(dǎo)的就是文檔的長(zhǎng)度了,本來文檔長(zhǎng)度的公式應(yīng)該如下:
這里需要討論的是,為什么在打分過程中,需要除以文檔的長(zhǎng)度呢?
因?yàn)樵谒饕校煌奈臋n長(zhǎng)度不一樣,很顯然,對(duì)于任意一個(gè)term,在長(zhǎng)的文檔中的tf要大的多,因而分?jǐn)?shù)也越高,這樣對(duì)小的文檔不公平,舉一個(gè) 極端的例子,在一篇1000萬個(gè)詞的鴻篇巨著中,"lucene"這個(gè)詞出現(xiàn)了11次,而在一篇12個(gè)詞的短小文檔中,"lucene"這個(gè)詞出現(xiàn)了10 次,如果不考慮長(zhǎng)度在內(nèi),當(dāng)然鴻篇巨著應(yīng)該分?jǐn)?shù)更高,然而顯然這篇小文檔才是真正關(guān)注"lucene"的。
然而如果按照標(biāo)準(zhǔn)的余弦計(jì)算公式,完全消除文檔長(zhǎng)度的影響,則又對(duì)長(zhǎng)文檔不公平(畢竟它是包含了更多的信息),偏向于首先返回短小的文檔的,這樣在實(shí)際應(yīng)用中使得搜索結(jié)果很難看。
所以在Lucene中,Similarity的lengthNorm接口是開放出來,用戶可以根據(jù)自己應(yīng)用的需要,改寫lengthNorm的計(jì)算 公式。比如我想做一個(gè)經(jīng)濟(jì)學(xué)論文的搜索系統(tǒng),經(jīng)過一定時(shí)間的調(diào)研,發(fā)現(xiàn)大多數(shù)的經(jīng)濟(jì)學(xué)論文的長(zhǎng)度在8000到10000詞,因而lengthNorm的公 式應(yīng)該是一個(gè)倒拋物線型的,8000到 10000詞的論文分?jǐn)?shù)最高,更短或更長(zhǎng)的分?jǐn)?shù)都應(yīng)該偏低,方能夠返回給用戶最好的數(shù)據(jù)。
在默認(rèn)狀況下,Lucene采用DefaultSimilarity,認(rèn)為在計(jì)算文檔的向量長(zhǎng)度的時(shí)候,每個(gè)Term的權(quán)重就不再考慮在內(nèi)了,而是全部為一。
而從Term的定義我們可以知道,Term是包含域信息的,也即title:hello和content:hello是不同的Term,也即一個(gè)Term只可能在文檔中的一個(gè)域中出現(xiàn)。
所以文檔長(zhǎng)度的公式為:
代入余弦公式:
再加上各種boost和coord,則可得出Lucene的打分計(jì)算公式。