理解Lucene得分计算公式(转)

转自:http://blog.youkuaiyun.com/shirdrn/article/details/6785385

Lucene通过计算文档的得分来确定查询结果文档的相似度。如果你希望通过干预Lucene查询来改变查询结果的排序,你就需要对Lucene的得分计算有所理解。Lucene得分计算公式如下所示:

[html]  view plain copy
  1. score(q,d)   =   coord(q,d)·queryNorm(q)·∑( tf(t in d)·idf(t)^2·t.getBoost()·norm(t,d) )  

其中,t in q。

下面详细解释公式乘积的每个因子的含义,以及是如何计算的,这样能够加深对Lucene得分计算的理解,才能在实际应用中根据需要调整各个参数,从而制定满足应用的排序策略。

 

coord(q,d)因子

 

coord(q,d)能够影响到检索结果文档的得分,它的计算公式如下:

[html]  view plain copy
  1. coord(q,d) = overlap / maxOverlap  

例如,查询关键词中经过解析(QueryParser)处理,得到两个Term分别为title:search和content:lucene,那么对应于公式中有,maxOverlap=2。

例如,对于下面这个索引的Document,代码如下:

[java]  view plain copy
  1. Document doc = new Document();  
  2. doc.add(new Field("title""search engine", Field.Store.YES, Field.Index.ANALYZED));  
  3. doc.add(new Field("content""good lucene luke lucene search server", Field.Store.YES, Field.Index.ANALYZED));  
  4. indexWriter.addDocument(doc);  
我们看到,检索title:search和content:lucene这两个Term,在title这个Field中( "search engine" )匹配上title:search,

在content这个Field中("good lucene luke lucene search server")匹配上了content:lucene,所以对应于公式中有,overlap=2,所以coord(q,d)=2/2=1。

queryNorm(q)因子

 

queryNorm(q)是查询权重对得分的影响,它的计算公式如下:

[html]  view plain copy
  1. queryNorm(q) = queryNorm(sumOfSquaredWeights)=1/(sumOfSquaredWeights^(1/2))  


sumOfSquaredWeights

继续看一下,在BooleanQuery中sumOfSquaredWeights的计算:

[java]  view plain copy
  1. sumOfSquaredWeights = q.getBoost()^2·∑( idf(t)·t.getBoost() )^2  

因为这是在计算查询的权重,所以上式求和部分中出现的t都是在q里面出现的Term(t in q)。

q.getBoost()

上式中q.getBoost()是一个查询子句被赋予的boost值,因为Lucene中任何一个Query对象是可以通过setBoost(boost)方法设置一个boost值的,下面我们通过一个相对比较复杂的例子来说明一下:

[java]  view plain copy
  1. BooleanQuery bq1 = new BooleanQuery(); // 第一个BooleanQuery查询子句  
  2. TermQuery tq1 = new TermQuery(new Term("title""search"));  
  3. tq1.setBoost(2.0f);  
  4. bq1.add(tq1, Occur.MUST);   
  5.   
  6. TermQuery tq2 = new TermQuery(new Term("content""lucene"));  
  7. tq2.setBoost(5.0f);  
  8. bq1.add(tq2, Occur.MUST);  
  9. bq1.setBoost(0.1f); // 给第一个查询子句乘上0.1,实际是减弱了其贡献得分的重要性  
  10.   
  11. BooleanQuery bq2 = new BooleanQuery(); // 第二个BooleanQuery查询子句  
  12. TermQuery tq3 = new TermQuery(new Term("title""book"));  
  13. tq3.setBoost(8.0f);  
  14. bq2.add(tq3, Occur.MUST);  
  15.   
  16. TermQuery tq4 = new TermQuery(new Term("content""lucene"));  
  17. tq4.setBoost(5.0f);  
  18. bq2.add(tq4, Occur.MUST);  
  19. bq2.setBoost(10.0f); // 给第二个查询子句乘上10.0,该子句更重要  
  20.   
  21. BooleanQuery bq = new BooleanQuery(); // 对上述两个BooleanQuery查询子句再进行OR运算  
  22. bq.add(bq1, Occur.SHOULD);  
  23. bq.add(bq2, Occur.SHOULD);  


上述代码可以这样理解:“我想要查询包含Lucene的文章,但标题最好是含有book的”,也就是说“我想查找介绍Lucene的书籍,如果没有没有关于Lucene的书籍,包含介绍Lucene查询search的文章也可以”。

所以上述两个布尔查询子句设置的boost值(0.1<<10.0),就对应于我们上述公式中的q.getBoost()。

idf(t)

idf(t)就是反转文档频率,含义是如果文档中出现Term的频率越高显得文档越不重要,Lucene中计算该值的公式如下:

[html]  view plain copy
  1. idf(t) = 1.0 + log(numDocs/(docFreq+1))  


其中,numDocs表示索引中文档的总数,docFreq表示查询中Term在多个文档中出现。

t.getBoost()

t.getBoost()表示查询中的Term给予的boost值,例如上面代码中:

[java]  view plain copy
  1. TermQuery tq3 = new TermQuery(new Term("title""book"));  
  2. tq3.setBoost(8.0f);  

title中包含book的Term,对匹配上的文档,通过上面公式计算,乘上t.getBoost()的值。

 

∑( tf(t in d)·idf(t)^2·t.getBoost()·norm(t,d) )因子

 

上面t还是在q中出现的Term即t in q。

norm(t,d)

这里,解释一下norm(t,d)的含义,计算公式如下所示:

[html]  view plain copy
  1. norm(t,d) = doc.getBoost()· lengthNorm· ∏ f.getBoost()  

norm(t,d)是在索引时(index-time)进行计算并存储的,在查询时(search-time)是无法再改变的,除非再重建索引。另外,Lucene在索引时存储norm值,而且是被压缩存储的,在查询时取出该值进行文档相关度计算,即文档得分计算。

需要注意的是,norm在进行codec的过程中,是有精度损失的,即不能保证decode(encode(x)) = x永远成立,例如 decode(encode(0.89)) = 0.75。

如果你在相关度调优过程中,发现norm的值破坏了文档相关性,严重的话,可以通过Field.setOmitNorms(true)方法来禁用norm,同时减少了该norm的存储开销,在一定程度上加快了查询过程中文档得分的计算。是否使用norm,需要根据你的应用来决定,例如,如果一个Field只存储一个Term,或者Field很短(包含的Term很少),一般是不需要存储norm的。

doc.getBoost()

这个就是Document的boost值,在索引的时候可以通过setBoost(boost)方法设置,例如我们一般认为title会比content更重要,所以在索引时可以对title进行boost(大于1.0)。

lengthNorm

lengthNorm是一个与Field长度(包含Term数量)有关的因子,Lucene中计算公式如下:

[html]  view plain copy
  1. lengthNorm = 1.0 / Math.sqrt(numTerms)  

其中,numTerms表示一个Field中Term的数量。

一般来说,一个Term在越短的Field中出现,表示该Term更重要,有点类似idf的含义。

∏ f.getBoost()

Lucene索引时,一个Document实例中,可以多次添加具有同一个Field名称的Field对象,但是值不相同,如下代码:

[java]  view plain copy
  1. Document doc = new Document();  
  2. doc.add(new Field("title""search engine", Field.Store.YES, Field.Index.ANALYZED));  
  3. Field fcontent1 = new Field("content""nutch solr lucene lucene search server", Field.Store.YES, Field.Index.ANALYZED);  
  4. fcontent1.setBoost(2.0f);  
  5. doc.add(fcontent1);  
  6. Field fcontent2 = new Field("content""good lucene luke lucene index server", Field.Store.YES, Field.Index.ANALYZED);  
  7. fcontent2.setBoost(5.0f);  
  8. doc.add(fcontent2);  
  9. indexWriter.addDocument(doc);  


我们在doc里面添加了同名content的两个字符串,对与这种情况,在计算得分的时候,是通过 ∏ f.getBoost()连乘积来计算得到的。

例如,我们查询content:lucene,上面Document doc中两个content的Field都匹配上了,在计算的时候有: ∏ f.getBoost() = 2.0 * 5.0 = 10.0。如果查询content:solr,则只有一个Field匹配上了,则 ∏ f.getBoost()=2.0。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值