影响 Lucene对文档打分的四种方式

1.在索引阶段设置  Document Boost 和Field Boost,存储在(.nrm)文件中

           如果希望某些文档和某些域比其他的域更重要,如果此文档和此域包含所要查询的词则应该得分较高,则可以在索引阶段设定文档的 boost和域的 boost值。 这些值是在索引阶段就写入索引文件的,存储在标准化因子(.nrm)文件中,一旦设定,除非删除此文档,否则无法改变。 如果不进行设定,则 Document Boost和 Field Boost默认为 1。

两者是如何影响 Lucene 的文档打分的呢? 让我们首先来看一下 Lucene 的文档打分的公式: 
score(q,d)   =    coord(q,d)  ·   queryNorm(q)  ·  ∑( tf(t in d)  ·   idf(t)2·   t.getBoost() ·  norm(t,d) ) 
                                                                                  t in q 
Document Boost和 Field Boost影响的是 norm(t, d),其公式如下: 
norm(t,d)   =    doc.getBoost()  ·   lengthNorm(field)  ·  ∏f.getBoost()  
                                                                             field f in d named as t 
它包括三个参数: 
  1)Document boost:此值越大,说明此文档越重要。 

    2)Field boost:此域越大,说明此域越重要。  

  3)lengthNorm(field) = (1.0 / Math.sqrt(numTerms)):一个域中包含的 Term总数越多,也即文档越长,此值越小,文档越短,此值越大。  其中第三个参数可以在自己的 Similarity中影响打分。 

根据 Lucene的注释,没有norms意味着索引阶段禁用了文档 boost和域的 boost及长度标准化。好处在于节省内存,不用在搜索阶段为索引中的每篇文档的每个域都占用一个字节来保存 norms 信息了。但是对 norms 信息的禁用是必须全部域都禁用的,一旦有一个域不禁用,则其他禁用的域也会存放默认的norms 值。因为为了加快 norms 的搜索速度,Lucene 是根据文档号乘以每篇文档的 norms信息所占用的大小来计算偏移量的,中间少一篇文档,偏移量将无法计算。也即 norms 信息要么都保存,要么都不保存。


2.搜索语句中设置 Query Boost. 

那 Query Boost是如何影响文档打分的呢? 

根据 Lucene的打分计算公式: 
score(q,d)   =    coord(q,d)  ·   queryNorm(q)  · ∑( tf(t in d)  ·   idf(t)2·   t.getBoost() ·   norm(t,d) ) 
                                                                                t in q 
3. 继承并实现自己的 Similarity 
Similariy 是计算 Lucene打分的最主要的类,实现其中的很多借口可以干预打分的过程。 
(1) float computeNorm(String field, FieldInvertState state) 
(2) float lengthNorm(String fieldName, int numTokens) 
(3) float queryNorm(float sumOfSquaredWeights) 
(4) float tf(float freq) 
(5) float idf(int docFreq, int numDocs) 
(6) float coord(int overlap, int maxOverlap) 
(7) float scorePayload(int docId, String fieldName, int start, int end, byte [] payload, int offset, int 
length) 
它们分别影响 Lucene打分计算的如下部分: 
score(q,d)   =   (6)coord(q,d)  ·  (3)queryNorm(q)  · ∑( (4)tf(t in d)  ·  (5)idf(t)2·   t.getBoost() ·  (1)norm(t,d) ) 
                                                                                            t in q 
norm(t,d)   =    doc.getBoost()  ·  (2)lengthNorm(field)  ·  ∏f.getBoost()  
                                                                                      field f in d named as t 
下面逐个进行解释: 
(1) float computeNorm(String field, FieldInvertState state) 
影响标准化因子的计算,如上述,他主要包含了三部分:文档 boost,域 boost,以及文档长度归一化。此函数一般按照上面 norm(t, d)的公式进行计算。 
(2) float lengthNorm(String fieldName, int numTokens) 
主要计算文档长度的归一化,默认是 1.0 / Math.sqrt(numTerms)。 因为在索引中,不同的文档长度不一样,很显然,对于任意一个 term,在长的文档中的 tf要大的多,因而分数也越高,这样对小的文档不公平,举一个极端的例子,在一篇 1000 万个词的鸿篇巨著中,"lucene"这个词出现了11 次,而在一篇 12个词的短小文档中,"lucene"这个词出现了 10 次,如果不考虑长度在内,当然鸿篇巨著应该分数更高,然而显然这篇小文档才是真正关注"lucene"的。 因而在此处是要除以文档的长度,从而减少因文档长度带来的打分不公。 而现在这个公式是偏向于首先返回短小的文档的,这样在实际应用中使得搜索结果也很难看。 于是在实践中,要根据项目的需要,根据搜索的领域,改写lengthNorm 的计算公式。比如我想做一个经济学论文的搜索系统,经过一定时间的调研,发现大多数的经济学论文的长度在 8000 到 10000 词,因而 lengthNorm 的公式应该是一个倒抛物线型的,8000 到 10000 词的论文分数最高,更短或更长的分数都应该偏低,方能够返回给用户最好的数据。 
(3) float queryNorm(float sumOfSquaredWeights) 
这是按照向量空间模型,对 query 向量的归一化。此值并不影响排序,而仅仅使得不同的query之间的分数可以比较。 
(4) float tf(float freq) 
freq 是指在一篇文档中包含的某个词的数目。tf 是根据此数目给出的分数,默认为Math.sqrt(freq)。也即此项并不是随着包含的数目的增多而线性增加的。 
(5) float idf(int docFreq, int numDocs) 
idf 是根据包含某个词的文档数以及总文档数计算出的分数,默认为(Math.log(numDocs/(double)(docFreq+1)) + 1.0)。 由于此项计算涉及到总文档数和包含此词的文档数,因而需要全局的文档数信息,这给跨索引搜索造成麻烦。 

         用 MultiSearcher 来一起搜索两个索引和分别用 IndexSearcher来搜索两个索引所得出的分数是有很大差异的。 究其原因是MultiSearcher的docFreq(Term term)函数计算了包含两个索引中包含此词的总文档数,而 IndexSearcher 仅仅计算了每个索引中包含此词的文档数。当两个索引包含的文档总数是有很大不同的时候,分数是无法比较的。
如果几个索引都是在一台机器上,则用 MultiSearcher 或者 MultiReader就解决问题了,然而有时候索引是分布在多台机器上的,虽然 Lucene也提供了 RMI,或用NFS保存索引的方法,然而效率和并行性一直是一个问题。 一个可以尝试的办法是在 Similarity 中,idf 返回 1,然后多个机器上的索引并行搜索,在汇总结果的机器上,再融入 idf 的计算。
(6) float coord(int overlap, int maxOverlap)
一次搜索可能包含多个搜索词,而一篇文档中也可能包含多个搜索词,此项表示,当一篇文档中包含的搜索词越多,则此文档则打分越高。
 (7) float scorePayload(int docId, String fieldName, int start, int end, byte [] payload, int offset, int length) 
由于 Lucene引入了 payload,因而可以存储一些自己的信息,用户可以根据自己存储的信息,来影响 Lucene 的打分。 一次搜索可能包含多个搜索词,而一篇文档中也可能包含多个搜索词,此项表示,当一篇文档中包含的搜索词越多,则此文档则打分越高。 

4.继承并实现自己的collector

           以上各种方法,已经把 Lucene score 计算公式的所有变量都涉及了,如果这还不能满足您的要求,还可以继承实现自己的 collector。 在 Lucene 2.4中,HitCollector 有个函数 public abstract void collect(int doc, float score),用来收集搜索的结果。
          此函数将 docid和 score插入一个 PriorityQueue中,使得得分最高的文档先返回。 我们可以继承HitCollector,并在此函数中对 score进行修改,然后再插入PriorityQueue,或者插入自己的数据结构

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值