版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明。

一、预热

TFIDFSimilarity曾经是Lucene/Solr默认评分公式,但是从lucene-6.0开始已经改成BM25Similary了(详见Lucene-6789)。但我们今天看的依然是TFIDFSimilarity,因为它相对简单一些,对我们理解评分过程有好处。

首先假定你知道怎么把一篇文档转化成一个空间向量,并且知道空间向量模型。

接下来先来统一一下术语和记号

q : query,表示一个查询

d : document,表示一篇文档

V(q) : q⃗ \vec{q}" role="presentation">q⃗ q⃗ \vec{q}q​ 表示Query的向量

V(d) : d⃗ \vec{d}" role="presentation">d⃗ d⃗ \vec{d}d 表示Document的向量

|V(q)| : ∣q⃗∣ |\vec{q}|" role="presentation">∣q⃗ ∣∣q⃗∣ |\vec{q}|∣q​∣ 表示Query向量的归一化

|V(d)| : ∣d⃗∣ |\vec{d}|" role="presentation">∣d⃗ ∣∣d⃗∣ |\vec{d}|∣d∣ 表示Document向量的归一化

在看TFIDFSimilarity之前,我们先看简单复习几个简单的公式。

  1. 余弦定理

    cosine_similarity(q,d)=V(q)⋅V(q)∣V(q)∣×∣V(d)∣ cosine\_similarity(q,d) = \frac{V(q) · V(q)}{|V(q)| \times |V(d)|}" role="presentation">cosine_similarity(q,d)=V(q)⋅V(q)∣V(q)∣×∣V(d)∣cosine_similarity(q,d)=V(q)⋅V(q)∣V(q)∣×∣V(d)∣ cosine\_similarity(q,d) = \frac{V(q) · V(q)}{|V(q)| \times |V(d)|}cosine_similarity(q,d)=∣V(q)∣×∣V(d)∣V(q)⋅V(q)​,用余弦定理通过计算两向量的夹角来表示两文本的相似,这是一切的基础。

这里沿*Lucene Docs的写法,cosine_similarity(q,d) cosine\_similarity(q,d)" role="presentation">cosine_similarity(q,d)cosine_similarity(q,d) cosine\_similarity(q,d)cosine_similarity(q,d)而不是用score(q,d) score(q,d)" role="presentation">score(q,d)score(q,d) score(q,d)score(q,d)的原因是相似度不是每个最终得分,相似度只是评分过程比较重要的一个因素而已。*建议你还是先看看TFIDFSimilarty的官方文档,它讲得相当完整也很细**。

  1. tf-idf公式

    tf-idf算法是一种非常常见算法,用来计算文本每个权重的。

    tf-idf算法的原理如果词条在文档出频率越高,则词条权重越高;如词条在越多篇文档出现,而词条的权重越低。具体计算如下:

    tfidf(t)=tf(t)∗idf(t) tfidf(t) = tf(t) * idf(t)" role="presentation">tfidf(t)=tf(t)∗idf(t)tfidf(t)=tf(t)∗idf(t) tfidf(t) = tf(t) * idf(t)tfidf(t)=tf(t)∗idf(t)

    tf(t)=frequency tf(t) = \sqrt{frequency}" role="presentation">tf(t)=frequency−−−−−−−−√tf(t)=frequency tf(t) = \sqrt{frequency}tf(t)=frequency​

    idf(t)=1+log⁡doc_count+1doc_freq+1 idf(t) = 1 + \log{\frac{doc\_count+1}{doc\_freq+1}}" role="presentation">idf(t)=1+logdoc_count+1doc_freq+1idf(t)=1+log⁡doc_count+1doc_freq+1 idf(t) = 1 + \log{\frac{doc\_count+1}{doc\_freq+1}}idf(t)=1+logdoc_freq+1doc_count+1​

    tfidf(t)=frequency×(1+log⁡doc_count+1doc_freq+1) tfidf(t) = \sqrt{frequency} \times (1 + \log{\frac{doc\_count+1}{doc\_freq+1}})" role="presentation">tfidf(t)=frequency−−−−−−−−√×(1+logdoc_count+1doc_freq+1)tfidf(t)=frequency×(1+log⁡doc_count+1doc_freq+1) tfidf(t) = \sqrt{frequency} \times (1 + \log{\frac{doc\_count+1}{doc\_freq+1}})tfidf(t)=frequency​×(1+logdoc_freq+1doc_count+1​)

对于VSM而言,tf-idf算法并不是必须,甚至权重的引入也不是必须。也就是只需要把每个词转化为一个数值即可,可以用词条的HashCode、词包的下标等等。

当然tf-idf算法也不是计算权重的唯一算法,比tf-idf效果更的还有BM25(BM25Similarity)。

故名思义,TFIDFSimilarity即是用TFIDF作为VSM向量的权重来计算相似度的打分器。

二、开始

原想从两个字符串的相似计算开始来推导我们Lucene的评分公式的,但这样的话篇幅太长太啰嗦太复杂。因此选择从Lucene的公式出发来看全公式每个细节的含义,一步步变化和计算最终推导出实践公式。

2.1 理论公式

我们先看一下,Lucene TFIDFSimilarity给出的理论评分公式:

score(q,d)=coord_factor(q,d)×query_boost(q)×V(q)⋅V(d)∣V(q)∣×doc_len_norm(d)×doc_boost(d) score(q,d) = coord\_factor(q,d) \times query\_boost(q) \times \frac{V(q)·V(d)}{|V(q)|} \times doc\_len\_norm(d) \times doc\_boost(d) " role="presentation">score(q,d)=coord_factor(q,d)×query_boost(q)×V(q)⋅V(d)∣V(q)∣×doc_len_norm(d)×doc_boost(d)score(q,d)=coord_factor(q,d)×query_boost(q)×V(q)⋅V(d)∣V(q)∣×doc_len_norm(d)×doc_boost(d) score(q,d) = coord\_factor(q,d) \times query\_boost(q) \times \frac{V(q)·V(d)}{|V(q)|} \times doc\_len\_norm(d) \times doc\_boost(d) score(q,d)=coord_factor(q,d)×query_boost(q)×∣V(q)∣V(q)⋅V(d)​×doc_len_norm(d)×doc_boost(d)

为什么说它是理论公式呢,因为它由VSM直接推导出来的公式。然则它在实际运用上并不是很方便和高效,因此我们对它进行一系列的变转使得它的计算得到一个简化的式子。

2.1.1 细说相似度部分

前面提到过Lucene相似度是通过VSM来计算(当然*TFIDFSimilarty的官方文档*也是提及的),相似度similarity通过如下公式计算的:cosine_similarity(q,d)=V(q)⋅V(q)∣V(q)∣×∣V(d)∣ cosine\_similarity(q,d) = \frac{V(q) · V(q)}{|V(q)| \times |V(d)|}" role="presentation">cosine_similarity(q,d)=V(q)⋅V(q)∣V(q)∣×∣V(d)∣cosine_similarity(q,d)=V(q)⋅V(q)∣V(q)∣×∣V(d)∣ cosine\_similarity(q,d) = \frac{V(q) · V(q)}{|V(q)| \times |V(d)|}cosine_similarity(q,d)=∣V(q)∣×∣V(d)∣V(q)⋅V(q)​

a. 如何计算俩向量的内积

由于我们研究的是TFIDFSimilarity的评分公式,我们知道TFIDFSimilarity评分过程是采用了tf-idf算法作为向量的权重(weight)。

因此有

q⃗=(w1,w2,...,wn) \vec{q}=(w_1, w_2, ..., w_n)" role="presentation">q⃗ =(w1,w2,...,wn)q⃗=(w1,w2,...,wn) \vec{q}=(w_1, w_2, ..., w_n)q​=(w1​,w2​,...,wn​),且wi=tf(ti)×idf(ti,q) w_i=tf(t_i)\times idf(t_i, q)" role="presentation">wi=tf(ti)×idf(ti,q)wi=tf(ti)×idf(ti,q) w_i=tf(t_i)\times idf(t_i, q)wi​=tf(ti​)×idf(ti​,q);

d⃗=(d1,d2,...,dn) \vec{d}=(d_1, d_2, ..., d_n)" role="presentation">d⃗ =(d1,d2,...,dn)d⃗=(d1,d2,...,dn) \vec{d}=(d_1, d_2, ..., d_n)d=(d1​,d2​,...,dn​),且di=tf(ti)×idf(ti,d) d_i=tf(t_i)\times idf(t_i, d)" role="presentation">di=tf(ti)×idf(ti,d)di=tf(ti)×idf(ti,d) d_i=tf(t_i)\times idf(t_i, d)di​=tf(ti​)×idf(ti​,d)

通常来说每个Query的每个词条的出现次数都是1,因此 tf(t1)=tf(t2)=...=tf(ti)=a,a∈(0,1] tf(t_1) = tf(t_2) = ... = tf(t_i) = a, a\in(0, 1]" role="presentation">tf(t1)=tf(t2)=...=tf(ti)=a,a∈(0,1]tf(t1)=tf(t2)=...=tf(ti)=a,a∈(0,1] tf(t_1) = tf(t_2) = ... = tf(t_i) = a, a\in(0, 1]tf(t1​)=tf(t2​)=...=tf(ti​)=a,a∈(0,1]。

V(q)⋅V(d)=q⃗⋅d⃗=∑t in q(wt×dt) V(q)·V(d) = \vec{q} · \vec{d} = \sum_{t\ in\ q}{(w_t \times d_t)}" role="presentation">V(q)⋅V(d)=q⃗ ⋅d⃗ =∑t in q(wt×dt)V(q)⋅V(d)=q⃗⋅d⃗=∑t in q(wt×dt) V(q)·V(d) = \vec{q} · \vec{d} = \sum_{t\ in\ q}{(w_t \times d_t)}V(q)⋅V(d)=q​⋅d=t in q∑​(wt​×dt​)

=∑t in q(tf(t,q)×idf(t,q)×tf(t,d)×idf(t,d)) = \sum_{t\ in\ q}{(tf(t,q)\times idf(t,q)\times tf(t,d)\times idf(t,d))}" role="presentation">=∑t in q(tf(t,q)×idf(t,q)×tf(t,d)×idf(t,d))=∑t in q(tf(t,q)×idf(t,q)×tf(t,d)×idf(t,d)) = \sum_{t\ in\ q}{(tf(t,q)\times idf(t,q)\times tf(t,d)\times idf(t,d))}=t in q∑​(tf(t,q)×idf(t,q)×tf(t,d)×idf(t,d))

=tf(t,q)×∑t in qidf(t,q)×tf(t,d)×idf(t,d)) = tf(t,q) \times \sum_{t\ in\ q}{idf(t,q)\times tf(t,d)\times idf(t,d))}" role="presentation">=tf(t,q)×∑t in qidf(t,q)×tf(t,d)×idf(t,d))=tf(t,q)×∑t in qidf(t,q)×tf(t,d)×idf(t,d)) = tf(t,q) \times \sum_{t\ in\ q}{idf(t,q)\times tf(t,d)\times idf(t,d))}=tf(t,q)×t in q∑​idf(t,q)×tf(t,d)×idf(t,d))

=a×∑t in qidf(t,q)×tf(t,d)×idf(t,d)) = a \times \sum_{t\ in\ q}{idf(t,q)\times tf(t,d)\times idf(t,d))}" role="presentation">=a×∑t in qidf(t,q)×tf(t,d)×idf(t,d))=a×∑t in qidf(t,q)×tf(t,d)×idf(t,d)) = a \times \sum_{t\ in\ q}{idf(t,q)\times tf(t,d)\times idf(t,d))}=a×t in q∑​idf(t,q)×tf(t,d)×idf(t,d))

由上式易知a对同一个Query而言是一个常量,因此上式可以进一步简化为

V(q)⋅V(d)=∑t in qtf(t,d)×idf(t,d)×idf(t,q) V(q)·V(d) = \sum_{t\ in\ q}{tf(t,d)\times idf(t,d) \times idf(t,q)}" role="presentation">V(q)⋅V(d)=∑t in qtf(t,d)×idf(t,d)×idf(t,q)V(q)⋅V(d)=∑t in qtf(t,d)×idf(t,d)×idf(t,q) V(q)·V(d) = \sum_{t\ in\ q}{tf(t,d)\times idf(t,d) \times idf(t,q)}V(q)⋅V(d)=t in q∑​tf(t,d)×idf(t,d)×idf(t,q)

那么又是怎么计算Query的idf(t,q) idf(t,q)" role="presentation">idf(t,q)idf(t,q) idf(t,q)idf(t,q)呢?

已知道idf(t,d)=1+log⁡doc_count+1doc_freq+1 idf(t,d) = 1 + \log{\frac{doc\_count+1}{doc\_freq+1}}" role="presentation">idf(t,d)=1+logdoc_count+1doc_freq+1idf(t,d)=1+log⁡doc_count+1doc_freq+1 idf(t,d) = 1 + \log{\frac{doc\_count+1}{doc\_freq+1}}idf(t,d)=1+logdoc_freq+1doc_count+1​,同时我们可以把Query也当作一篇文档。因此对于idf(t,q) idf(t,q)" role="presentation">idf(t,q)idf(t,q) idf(t,q)idf(t,q)有doc_countq=doc_countd+1;doc_freqq=doc_freqd+1 doc\_count_q=doc\_count_d + 1; doc\_freq_q = doc\_freq_d+1" role="presentation">doc_countq=doc_countd+1;doc_freqq=doc_freqd+1doc_countq=doc_countd+1;doc_freqq=doc_freqd+1 doc\_count_q=doc\_count_d + 1; doc\_freq_q = doc\_freq_d+1doc_countq​=doc_countd​+1;doc_freqq​=doc_freqd​+1(即直接把原文档总数以及对应的文档频率都加上1)。整理可得

idf(t,q)=1+log⁡1+(doc_count+1)1+(doc_freq+1) idf(t,q) = 1 + \log{\frac{1 + (doc\_count+1)}{1 + (doc\_freq+1)}}" role="presentation">idf(t,q)=1+log1+(doc_count+1)1+(doc_freq+1)idf(t,q)=1+log⁡1+(doc_count+1)1+(doc_freq+1) idf(t,q) = 1 + \log{\frac{1 + (doc\_count+1)}{1 + (doc\_freq+1)}}idf(t,q)=1+log1+(doc_freq+1)1+(doc_count+1)​

当文档数量比较大的时候,我们可以认为idf(t,q)=idf(t,d) idf(t,q) = idf(t,d)" role="presentation">idf(t,q)=idf(t,d)idf(t,q)=idf(t,d) idf(t,q) = idf(t,d)idf(t,q)=idf(t,d),因此上式可以进一步简化为

V(q)⋅V(d)=∑t in qtf(t,d)×idf2(t,q)) V(q)·V(d) = \sum_{t\ in\ q}{tf(t,d)\times idf^2(t,q))}" role="presentation">V(q)⋅V(d)=∑t in qtf(t,d)×idf2(t,q))V(q)⋅V(d)=∑t in qtf(t,d)×idf2(t,q)) V(q)·V(d) = \sum_{t\ in\ q}{tf(t,d)\times idf^2(t,q))}V(q)⋅V(d)=t in q∑​tf(t,d)×idf2(t,q))

中间的计算过程不能理解也没关系,你只需要知道并记住最终结果就可以了。

b. 为什么|V(d)|不见了

先看一下Lucene Docs对∣V(d)∣ |V(d)|" role="presentation">∣V(d)∣∣V(d)∣ |V(d)|∣V(d)∣的解释吧。

Normalizing V(d) to the unit vector is known to be problematic in that it removes all document length information. For some documents removing this info is probably ok, e.g. a document made by duplicating a certain paragraph 10 times, especially if that paragraph is made of distinct terms. But for a document which contains no duplicated paragraphs, this might be wrong. To avoid this problem, a different document length normalization factor is used, which normalizes to a vector equal to or larger than the unit vector: doc-len-norm(d).

大意是说|V(d)|是一个单位向量,因此它并不带文档的长度信息。如果文档是由一段文本重复十次组成的,尤其这段文本由完全不一样词条组成时,文档长度信息对于这类文档可能并不会有影响。但是文档由多段不一样文本构成的,那么文档长度信息可能会有影响。为此,使用不同文档长度归一化因子,可实现一个向量大于或等于它的单位向量doc-len-norm(d)。

我猜你没看懂。简单说,如你所知文档长度信息是会影响相似度,即文档长度越大可能更有优势。为了解决这个问题我们引用**doc-len-norm(d)**来代替∣V(d)∣ |V(d)|" role="presentation">∣V(d)∣∣V(d)∣ |V(d)|∣V(d)∣。

到此为止,并没有解释∣V(d)∣ |V(d)|" role="presentation">∣V(d)∣∣V(d)∣ |V(d)|∣V(d)∣为什么可以不要,只是说明了doc-len-norm(d)的作用。

至于为什么不要∣V(d)∣ |V(d)|" role="presentation">∣V(d)∣∣V(d)∣ |V(d)|∣V(d)∣呢?虽然∣V(d)∣ |V(d)|" role="presentation">∣V(d)∣∣V(d)∣ |V(d)|∣V(d)∣是文档归一化因子,理论上它是可以评价文档的重要性的。但是它计算比较复杂(又有开文又有平方),关键还没什么用。下面是∣V(d)∣ |V(d)|" role="presentation">∣V(d)∣∣V(d)∣ |V(d)|∣V(d)∣的计算公式:

∣V(d)∣=∑t in q(tf(t,d)×idf(t,d))2 |V(d)| = \sqrt{\sum_{t\ in\ q}{(tf(t,d)\times idf(t,d))^2}}" role="presentation">∣V(d)∣=∑t in q(tf(t,d)×idf(t,d))2−−−−−−−−−−−−−−−−−−−−−√∣V(d)∣=∑t in q(tf(t,d)×idf(t,d))2 |V(d)| = \sqrt{\sum_{t\ in\ q}{(tf(t,d)\times idf(t,d))^2}}∣V(d)∣=t in q∑​(tf(t,d)×idf(t,d))2​

上式易知它的计算复杂同时并没有涉及文档的长度信息,因此Lucene选择另一种方式来表示,它能把文档的长度信息考虑进来,这就是我们前面提到doc-len-norm(d)。

c. 什么是 doc-len-norm(d)

故名思义,它是文档长度归一化因子,即是弱化文档长度对最终评分的影响。那我们怎么在搜索过程引用文档的长度对评分的影响呢? 首先lucene文档是可以有多个字段,而且搜索时被指定字段. 由此可以用搜索时指定搜索字段f f" role="presentation">ff ff的归一化因子来表示文档的归一化因子,所以就把问题转化算字段f f" role="presentation">ff ff的归一化因子的问题了。设norm(t,d) norm(t,d)" role="presentation">norm(t,d)norm(t,d) norm(t,d)norm(t,d)表示文档d的搜索字段f f" role="presentation">ff ff的归一化因子。

doc_len_norm(d)=∑t in qnorm(t,d) doc\_len\_norm(d) = \sum_{t\ in\ q}{norm(t,d)}" role="presentation">doc_len_norm(d)=∑t in qnorm(t,d)doc_len_norm(d)=∑t in qnorm(t,d) doc\_len\_norm(d) = \sum_{t\ in\ q}{norm(t,d)}doc_len_norm(d)=t in q∑​norm(t,d)

用什么来表示norm(t,d) norm(t,d)" role="presentation">norm(t,d)norm(t,d) norm(t,d)norm(t,d)呢?Lucene用了下面的式子来表示:

norm(t,d)=lengthNorm∏f in df.getBoost() norm(t,d) = lengthNorm \prod_{f\ in\ d}{f.getBoost()}" role="presentation">norm(t,d)=lengthNorm∏f in df.getBoost()norm(t,d)=lengthNorm∏f in df.getBoost() norm(t,d) = lengthNorm \prod_{f\ in\ d}{f.getBoost()}norm(t,d)=lengthNormf in d∏​f.getBoost()

lengthNorm=1length(f)−numOverlap(f) lengthNorm = \frac{1}{\sqrt{length(f) - numOverlap(f)}} " role="presentation">lengthNorm=1length(f)−numOverlap(f)√lengthNorm=1length(f)−numOverlap(f) lengthNorm = \frac{1}{\sqrt{length(f) - numOverlap(f)}} lengthNorm=length(f)−numOverlap(f)​1​

length : 字段的所有词条的数量

numOverlap : 字段的所有词条中出现过2次及以上词条的数量

lengthNorm是索引时计算好跟其它字段信息一样存储的,因此在搜索时它的“计算”是比较高效的。同时它并不是所有字段类型都会存储的,例如数值类型就不会计算和存储。当然对文本类型也可以关闭norm的计算,同时如果开启norm的计算的话,还能选择是否压缩。为了减小索引的体积,默认情况下是带用压缩的。

这里lengthNorm只是一般情况下的计算公式,并非所有情况都是这个公式

说到这里,我们顺便把norm(d)编码和解码两过程出现误差的情况解释一下吧。

其实前面已经解释过了,即是lucene为了减小索引的体积,对norm进行压缩。即是把原本应该用float表示norm数值,压缩成一个byte造成精度缺失,这就是decodeNormValue(encodeNormValue(norm))不一定等于norm的原因。

Lucene docs对LengthNorm的说明是这样的:

computed when the document is added to the index in accordance with the number of tokens of this field in the document, so that shorter fields contribute more to the score. LengthNorm is computed by the Similarity class in effect at indexing.

d. |V(q)|是什么呢

细心的你可能已经知道了|V(q)|是怎么回事,对的没错它就是queryNorm的别名。看一下QueryNorm的公式

queryNorm(q)=1q.getBoost()2×∑t in q(idf(t,q)×t.getBoost())2 queryNorm(q)=\frac{1}{\sqrt{q.getBoost()^2 \times \sum_{t\ in\ q}{(idf(t,q)\times t.getBoost())}^2}}" role="presentation">queryNorm⎛⎝⎜q⎞⎠⎟=1q.getBoost()2×∑t in q(idf(t,q)×t.getBoost())2√queryNorm(q)=1q.getBoost()2×∑t in q(idf(t,q)×t.getBoost())2 queryNorm(q)=\frac{1}{\sqrt{q.getBoost()^2 \times \sum_{t\ in\ q}{(idf(t,q)\times t.getBoost())}^2}}queryNorm(q)=q.getBoost()2×∑t in q​(idf(t,q)×t.getBoost())2​1​

我们先假定完全不设boost,即先假定boost=1。因此原式可以变成queryNorm(q)=1∑t in qidf2(t,q) queryNorm(q)=\frac{1}{\sqrt{\sum_{t\ in\ q}{idf^2(t,q)}}}" role="presentation">queryNorm(q)=1∑t in qidf2(t,q)√queryNorm(q)=1∑t in qidf2(t,q) queryNorm(q)=\frac{1}{\sqrt{\sum_{t\ in\ q}{idf^2(t,q)}}}queryNorm(q)=∑t in q​idf2(t,q)​1​。

再回顾一下∣V(q)∣ |V(q)|" role="presentation">∣V(q)∣∣V(q)∣ |V(q)|∣V(q)∣的公式,∣V(q)∣=1∑t in qidf2(t,q)×tf2(t,q) |V(q)|=\frac{1}{\sqrt{\sum_{t\ in\ q}{idf^2(t,q) \times tf^2(t,q)}}}" role="presentation">∣V(q)∣=1∑t in qidf2(t,q)×tf2(t,q)√∣V(q)∣=1∑t in qidf2(t,q)×tf2(t,q) |V(q)|=\frac{1}{\sqrt{\sum_{t\ in\ q}{idf^2(t,q) \times tf^2(t,q)}}}∣V(q)∣=∑t in q​idf2(t,q)×tf2(t,q)​1​,因为一般情况下Query中每个词出现的频率都是1。因此原式可简写成∣V(q)∣=1∑t in qidf2(t,q) |V(q)|=\frac{1}{\sqrt{\sum_{t\ in\ q}{idf^2(t,q)}}}" role="presentation">∣V(q)∣=1∑t in qidf2(t,q)√∣V(q)∣=1∑t in qidf2(t,q) |V(q)|=\frac{1}{\sqrt{\sum_{t\ in\ q}{idf^2(t,q)}}}∣V(q)∣=∑t in q​idf2(t,q)​1​,对于这点我们前面解释过了。然后再把各种boost考虑进去,你会发现∣V(q)∣ |V(q)|" role="presentation">∣V(q)∣∣V(q)∣ |V(q)|∣V(q)∣的式子跟queryNorm(q) queryNorm(q)" role="presentation">queryNorm(q)queryNorm(q) queryNorm(q)queryNorm(q)一模一样。

我们知道打分是对文档打分,因此是每个被检索到的文档都要套这个公式来计算它的分值。简而言之,评分公式是文档级别的。
  • 1

它使得两个不同Queries的搜索结果的每个文档的得分可以比较,可以比较意思是它们的比较是有意义的。

QueryNorm不影响排名,也就是不影响评分,就是对于同一个Query来说queryNorm是唯一的。好吧,这说法并不准确,应该是对于同一个Index的同一个Query它的queryNorm是唯一的。如果不能理解就看公式吧,作为查询归一化,那么它作用就是缩小同一个Query在不同Index上产生的影响。这使得分布式查询(同一个Query用在不同的Index上并计算文档的分数)的评分变得有意义和有可比性。

不影响排名,也就是不影响评分,有两层意思:

  1. 在单个Index而queryNorm是没意义,不会影响评分和排序。
  2. 对于多个Index,queryNorm实际上是会影响的评分和排序。只是这影响是降低不同的index之间在搜索时的影响,因此可以认为没有影响。

2.2 coord因子

coord相关性,overlap与maxoverlap比值 coord(q,d)=overlapmaxOverlap coord(q,d) = \frac{overlap}{maxOverlap}" role="presentation">coord(q,d)=overlapmaxOverlapcoord(q,d)=overlapmaxOverlap coord(q,d) = \frac{overlap}{maxOverlap}coord(q,d)=maxOverlapoverlap​

maxOverlap:是Query中有效的词条数

overlap:表示文档中出现词条数

2.3 boost的作用

boost的作用就是在索引时或搜索时,改变字段或者词条的重要,从而影响文档的最终得分。

  1. 两个时间点:搜索时和索引时,都能设置
  2. 四个级别:Query条件、文档、字段和词条

三、实践公式即将出现

我们先上面推导出来最终式子罗列一下:

V(q)⋅V(d)=∑t in qtf(t,d)×idf2(t,q)) V(q)·V(d) = \sum_{t\ in\ q}{tf(t,d)\times idf^2(t,q))}" role="presentation">V(q)⋅V(d)=∑t in qtf(t,d)×idf2(t,q))V(q)⋅V(d)=∑t in qtf(t,d)×idf2(t,q)) V(q)·V(d) = \sum_{t\ in\ q}{tf(t,d)\times idf^2(t,q))}V(q)⋅V(d)=∑t in q​tf(t,d)×idf2(t,q))

doc_len_norm(d)=∑t in qnorm(t,d) doc\_len\_norm(d) = \sum_{t\ in\ q}{norm(t,d)}" role="presentation">doc_len_norm(d)=∑t in qnorm(t,d)doc_len_norm(d)=∑t in qnorm(t,d) doc\_len\_norm(d) = \sum_{t\ in\ q}{norm(t,d)}doc_len_norm(d)=∑t in q​norm(t,d)

∣V(q)∣=queryNorm(q)=1q.getBoost()2×∑t in q(idf(t,q)×t.getBoost())2 |V(q)| = queryNorm(q) = \frac{1}{\sqrt{q.getBoost()^2 \times \sum_{t\ in\ q}{(idf(t,q)\times t.getBoost())}^2}}" role="presentation">∣V⎛⎝⎜q⎞⎠⎟∣=queryNorm⎛⎝⎜q⎞⎠⎟=1q.getBoost()2×∑t in q(idf(t,q)×t.getBoost())2√∣V(q)∣=queryNorm(q)=1q.getBoost()2×∑t in q(idf(t,q)×t.getBoost())2 |V(q)| = queryNorm(q) = \frac{1}{\sqrt{q.getBoost()^2 \times \sum_{t\ in\ q}{(idf(t,q)\times t.getBoost())}^2}}∣V(q)∣=queryNorm(q)=q.getBoost()2×∑t in q​(idf(t,q)×t.getBoost())2​1​

把上面三个式子代入下式

score(q,d)=coord(q,d)×queryNorm(q)×∑t in q(idf2(t,q)×tf(t,d))×doc_len_norm(d) score(q,d) = coord(q,d)\times queryNorm(q) \times \sum_{t\ in\ q}{(idf^2(t,q)\times tf(t,d))} \times doc\_len\_norm(d) " role="presentation">score(q,d)=coord(q,d)×queryNorm(q)×∑t in q(idf2(t,q)×tf(t,d))×doc_len_norm(d)score(q,d)=coord(q,d)×queryNorm(q)×∑t in q(idf2(t,q)×tf(t,d))×doc_len_norm(d) score(q,d) = coord(q,d)\times queryNorm(q) \times \sum_{t\ in\ q}{(idf^2(t,q)\times tf(t,d))} \times doc\_len\_norm(d) score(q,d)=coord(q,d)×queryNorm(q)×t in q∑​(idf2(t,q)×tf(t,d))×doc_len_norm(d)

整理可得

=coord(q,d)×queryNorm(q)×∑t in q(idf2(t,q)×tf(t,d)×norm(t,d)) = coord(q,d) \times queryNorm(q) \times \sum_{t\ in\ q}{(idf^2(t,q)\times tf(t,d) \times norm(t,d))}" role="presentation">=coord(q,d)×queryNorm(q)×∑t in q(idf2(t,q)×tf(t,d)×norm(t,d))=coord(q,d)×queryNorm(q)×∑t in q(idf2(t,q)×tf(t,d)×norm(t,d)) = coord(q,d) \times queryNorm(q) \times \sum_{t\ in\ q}{(idf^2(t,q)\times tf(t,d) \times norm(t,d))}=coord(q,d)×queryNorm(q)×t in q∑​(idf2(t,q)×tf(t,d)×norm(t,d))

四、结语

之前在学习lucene的时候就想写这么一篇博客,但是一直没能实现。如今也是能花好几个小时才整理出来,当第一次把所有内容都罗列出来的时候,篇幅是那么那么的长。经过几番裁剪才如今这个样子,肯定是有一些地方没说明白的,若问题可以一起论,这么复杂的博文难免会出错误,若有发现错误望不吝指教

                                </div>

原文地址:https://blog.csdn.net/zteny/article/details/57366074


Lucene TFIDFSimilarity评分公式详解的更多相关文章

  1. ElasticSearch7.3学习(二十四)----相关度评分机制详解

    1.算法介绍 relevance score(相关性分数) 算法,简单来说,就是计算出,一个索引中的文本,与搜索文本,他们之间的关联匹配程度.Elasticsearch使用的是 term freque ...

  2. Lucene的Vint类型详解

    Lucene Vint压缩策略是,用每个字节的最高位做标志位,后7位为有效算术位,如果标志位为1,则说明后一个字节和当前字节是同一个数字,为0说明后一个字节是一个新的数字 Lucene源代码中进行存储 ...

  3. 贝塞尔曲线 WPF MVVM N阶实现 公式详解+源代码下载

    源代码下载 效果图: 本程序主要实现: N阶贝塞尔曲线(通用公式) 本程序主要使用技术 MVVM InterAction 事件绑定 动态添加Canvas的Item 第一部分公式: n=有效坐标点数量 ...

  4. lucene 的评分机制

    lucene 的评分机制 elasticsearch是基于lucene的,所以他的评分机制也是基于lucene的.评分就是我们搜索的短语和索引中每篇文档的相关度打分. 如果没有干预评分算法的时候,每次 ...

  5. Lucene Scoring 评分机制

    原文出处:http://blog.chenlb.com/2009/08/lucene-scoring-architecture.html Lucene 评分体系/机制(lucene scoring)是 ...

  6. Lucene打分规则与Similarity模块详解

    搜索排序结果的控制 Lucnen作为搜索引擎中,应用最为广泛和成功的开源框架,它对搜索结果的排序,有一套十分完整的机制来控制:但我们控制搜索结果排序的目的永远只有一个,那就是信息过滤,让用户快速,准确 ...

  7. Lucene系列六:Lucene搜索详解(Lucene搜索流程详解、搜索核心API详解、基本查询详解、QueryParser详解)

    一.搜索流程详解 1. 先看一下Lucene的架构图 由图可知搜索的过程如下: 用户输入搜索的关键字.对关键字进行分词.根据分词结果去索引库里面找到对应的文章id.根据文章id找到对应的文章 2. L ...

  8. lucene、lucene.NET详细使用与优化详解

    lucene.lucene.NET详细使用与优化详解 2010-02-01 13:51:11 分类: Linux 1 lucene简介1.1 什么是luceneLucene是一个全文搜索框架,而不是应 ...

  9. Lucene系列五:Lucene索引详解(IndexWriter详解、Document详解、索引更新)

    一.IndexWriter详解 问题1:索引创建过程完成什么事? 分词.存储到反向索引中 1. 回顾Lucene架构图: 介绍我们编写的应用程序要完成数据的收集,再将数据以document的形式用lu ...

随机推荐

  1. MAC OpenGL 环境搭建

    MAC OpenGL 环境搭建 基础库介绍 先要安装两个库一个是GLEW(OpenGL Extension Wrangler Library),另外一个是GLFW(Graphics Library F ...

  2. 剑指offer——43数据流中的中位数

    题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值.我们 ...

  3. java 8 bug

    jpa保存实体的时候,不能用{{}}初始化对象,否则会报异常 org.springframework.dao.InvalidDataAccessApiUsageException: Unknown e ...

  4. 31-Ubuntu-用户权限-02-ls输出信息介绍

    ls -l 查看文件夹下文件或目录的详细信息 1 2 3 4 5 6 7 8 9 10 d/- rwx rwx r-x 2 summmer summmer 12288 2月 25 13:34 Ente ...

  5. 关于windows一些常用的快捷键使用说明

    犹由于经常使用linux和windows,所以有时候就会觉得windows一点需要点击好多下才能够完成的设定非常的麻烦,这里总结一些常用到的快捷键功能,会随着本小白的工作经验而添加. 1.ctrl+a ...

  6. 数据转换--替换值(replace函数)

    替换值 replace函数 data=Series([1,-999,2,-999,-1000,3]) data Out[34]: 0 1 1 -999 2 2 3 -999 4 -1000 5 3 d ...

  7. WSGI——python-Web框架基础

    1. 简介 WSGI ​ WSGI:web服务器网关接口,这是python中定义的一个网关协议,规定了Web Server如何跟应用程序交互.可以理解为一个web应用的容器,通过它可以启动应用,进而提 ...

  8. 29. StringBuilder

    1.字符串变量.StringBuffer.StringBulid的区别:           字符串是一个常量,不能被修改   字符串一旦被修改,那么会再创建一个对象,浪费空间           而 ...

  9. WPF 模仿 UltraEdit 文件查看器系列一 用户控件

    WPF 模仿 UltraEdit 文件查看器系列一 用户控件 运行环境:Win10 x64, NetFrameWork 4.8, 作者:乌龙哈里,日期:2019-05-10 章节: 起步 添加用户控件 ...

  10. ps去除元素的三种常用方法

    1.仿制图章工具,alt+鼠标左键进行选取复制区域,然后左键点击需要覆盖的区域. 2.套锁工具--选择区域--右键填充--内容识别.     3.修补工具,选中区域--拖动适配.     附带另一份较 ...