zoukankan      html  css  js  c++  java
  • 有关Lucene的问题(3): 向量空间模型与Lucene的打分机制

    问题:

    在你的文章中提到了:

    于是我们把所有此文档中词(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)   =   queryNorm(sumOfSquaredWeights)   = 1/sumOfSquaredWeights½

    sumOfSquaredWeights   =   q.getBoost() 2  ·  ( idf(t)  ·  t.getBoost() ) 2

                                                                        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)   =   doc.getBoost()  ·  lengthNorm(field)  ·  f.getBoost()

                                                                             field f in d named as t

    其中文档的boost和field的boost在nrm文件中的一节已经讲过,表示某篇文章的某个域可能更重要一些,也可能更不重要一些。

    而lengthNorm(field)正是文档长度的归一化的体现。

    默认的DefaultSimilarity的实现中,lengthNorm如下计算:

    public float lengthNorm(String fieldName, int numTerms) {
      return (float)(1.0 / Math.sqrt(numTerms));
    }

    我们会发现,lengthNorm的计算并不是按照经典理论,是向量长度分之一,而是文档长度开方分之一,也即忽略了每个term的权重,认为每个term的权重都是1,方才能够得出上述的公式。Lucene之所以这样做可能主要考虑两点:

    • 首先,不想完全抛弃文件长度的影响,否则又对长文档不公平,毕竟它是包含了更多的信息。我们可以简单的做个试验可以得知,即便是现在这个公式,lucene还是偏向于首先返回短小的文档的,这样在实际应用中使得搜索结果很难看。
    • 其次,此接口是开放出来,在Similarity中的,用户可以根据自己应用的需要,改写lengthNorm的计算公式。比如我想做一个经济学论文的搜索系统,经过一定时间的调研,发现大多数的经济学论文的长度在8000到10000词,因而lengthNorm的公式应该是一个倒抛物线型的,8000到10000词的论文分数最高,更短或更长的分数都应该偏低,方能够返回给用户最好的数据。
  • 相关阅读:
    SGU 271 Book Pile (双端队列)
    POJ 3110 Jenny's First Exam (贪心)
    HDU 4310 Hero (贪心)
    ZOJ 2132 The Most Frequent Number (贪心)
    POJ 3388 Japanese Puzzle (二分)
    UVaLive 4628 Jack's socks (贪心)
    POJ 2433 Landscaping (贪心)
    CodeForces 946D Timetable (DP)
    Android Studio教程从入门到精通
    Android Tips – 填坑手册
  • 原文地址:https://www.cnblogs.com/forfuture1978/p/1664916.html
Copyright © 2011-2022 走看看