在你的文章中提到了:
于是我们把所有此文档中词(term)的权重(term weight) 看作一个向量。
Document = {term1, term2, …… ,term N}
Document Vector = {weight1, weight2, …… ,weight N}
同样我们把查询语句看作一个简单的文档,也用向量来表示。
Query = {term1, term 2, …… , term N}
Query Vector = {weight1, weight2, …… , weight N}
于是我们把所有此文档中词(term)的权重(term weight) 看作一个向量。Document = {term1, term2, …… ,term N} Document Vector = {weight1, weight2, …… ,weight N}同样我们把查询语句看作一个简单的文档,也用向量来表示。 Query = {term1, term 2, …… , term N}Query Vector = {weight1, weight2, …… , weight N}
其中查询语句的term的权重如何定义的呢?
在这里,既然要放到相同的向量空间,自然维数是相同的,不同时,取二者的并集,如果不含某个词(Term)时,则权重(Term Weight)为0。
为什么要取二者的并集,直接用查询语句向量的维度应该就可以吧,毕竟其他的字段也没有实际的用途哈,我是这么想的,不知道行得通不?
对于第一个问题,文章中既然说,查询语句也看成一个很短很短的文档,则按理论来讲,term的权重也是应用tf * idf进行计算的。
但是由于query语句是一个特殊的文档,首先对于tf来讲,一般来说不会有人在查询中将一个词输入两次,因而tf一般认为是1
而对于idf,是包含此term的文档总数,本应该包括query语句这篇很短的文档,但当文档数量达到一定的数目的时候,这一篇对分数的影响也可忽略不计,因而idf为索引中包含此term的文档总数。
所以对于score计算公式的分子部分来讲:
设query向量为:<q(i), q(j)>,而document的向量<d(1), .. , d(i), .. , d(j), .., d(n)>,两者取点积后为q(i)*d(i) + q(j)*d(j),其他项都是0.
到这里似乎看起来,和你感觉的一样,向量空间是n维同向量空间是2维是一样的。
这里说一些题外话,有利于理解lucene score的公式的由来和推理:
将其分解为df*idf后为,tf(i in q)*idf(i in q) * tf(i in d) * idf(i in d) + tf(j in q) * idf(j in q) * tf(j in d) * idf(j in d)
由上面的分析我们知道,对于query,tf为1,所以tf(i in q) = tf(j in q) = 1,而idf对query和document都是一样的,都是不计算query这个短文档的数目的,因而idf(i in q) = idf(i in d),idf(j in q) = idf(j in d),
于是上面的公式变成,tf(i in d)*idf(i)^2 + tf(j in d)* idf(j)^2,这就是为什么Lucene的score计算公式里有tf乘以idf平方的样子:
score(q,d) = coord(q,d) · queryNorm(q) · ∑( tf(t in d) · idf(t)2 · t.getBoost() · norm(t,d) ) t in q |
然而在score的计算部分,除了分子部分,还有分母部分,也即两个向量都要除以自己的长度,从这个角度来讲,向量空间是n维和2维算出来的向量长度差别就很大了,所以必须要取两者的并集。
那么为什么要除以向量的长度呢?在这里叫做归一化处理。因为在索引中,不同的文档长度不一样,很显然,对于任意一个term,在长的文档中的tf要大的多,因而分数也越高,这样对小的文档不公平,举一个极端的例子,在一篇1000万个词的鸿篇巨著中,"lucene"这个词出现了11次,而在一篇12个词的短小文档中,"lucene"这个词出现了10次,如果不考虑长度在内,当然鸿篇巨著应该分数更高,然而显然这篇小文档才是真正关注"lucene"的。
在Lucene的score公式中,query语句长度的归一化是在queryNorm中体现的:
queryNorm(q) = |
t in q |
除了boost之外,queryNorm = 1/sqrt(q(i) ^ 2 + q(j) ^2),而q(x) = df(x) * idf(x),对于query来讲,df(x) = 1,因而queryNorm成了查询词的idf值的平方和的开方后的值分之一。
document的长度归一化是在 norm(t,d)中体现的:
norm(t,d) = field f in d named as t |
其中文档的boost和field的boost在nrm文件中的一节已经讲过,表示某篇文章的某个域可能更重要一些,也可能更不重要一些。
而lengthNorm(field)正是文档长度的归一化的体现。
默认的DefaultSimilarity的实现中,lengthNorm如下计算:
public float lengthNorm(String fieldName, int numTerms) { |
我们会发现,lengthNorm的计算并不是按照经典理论,是向量长度分之一,而是文档长度开方分之一,也即忽略了每个term的权重,认为每个term的权重都是1,方才能够得出上述的公式。Lucene之所以这样做可能主要考虑两点: