Lucene的评分(score)机制研究

首先,需要学习Lucene的评分计算公式——

分值计算方式为查询语句q中每个项t与文档d的匹配分值之和,当然还有权重的因素。其中每一项的意思如下表所示:


表3.5


评分公式中的因子


评分因子


描 述


tf(t in d)


项频率因子——文档(d)中出现项(t)的频率


idf(t)


项在倒排文档中出现的频率:它被用来衡量项的“唯一”性.出现频率较高的term具有较低的idf,出现较少的term具有较高的idf


boost(t.field in d)


域和文档的加权,在索引期间设置.你可以用该方法 对某个域或文档进行静态单独加权


lengthNorm(t.field in d)


域的归一化(Normalization)值,表示域中包含的项数量.该值在索引期间计算,并保存在索引norm中.对于该因子,更短的域(或更少的语汇单元)能获得更大的加权


coord(q,d)


协调因子(Coordination factor),基于文档中包含查询的项个数.该因子会对包含更多搜索项的文档进行类似AND的加权


queryNorm(q)


每个査询的归一化值,指毎个查询项权重的平方和

通过Searcher.explain(Query query, int doc)方法可以查看某个文档的得分的具体构成。 示例:

public class ScoreSortTest {
    public final static String INDEX_STORE_PATH = "index";
    public static void main(String[] args) throws Exception {
        IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
        writer.setUseCompoundFile(false);

        Document doc1 = new Document();
        Document doc2 = new Document();
        Document doc3 = new Document();

        Field f1 = new Field("bookname","bc bc", Field.Store.YES, Field.Index.TOKENIZED);
        Field f2 = new Field("bookname","ab bc", Field.Store.YES, Field.Index.TOKENIZED);
        Field f3 = new Field("bookname","ab bc cd", Field.Store.YES, Field.Index.TOKENIZED);

        doc1.add(f1);
        doc2.add(f2);
        doc3.add(f3);

        writer.addDocument(doc1);
        writer.addDocument(doc2);
        writer.addDocument(doc3);

        writer.close();

        IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
        TermQuery q = new TermQuery(new Term("bookname", "bc"));
        q.setBoost(2f);
        Hits hits = searcher.search(q);
        for(int i=0; i<hits.length();i++){
            Document doc = hits.doc(i);
            System.out.print(doc.get("bookname") + "\t\t");
            System.out.println(hits.score(i));
            System.out.println(searcher.explain(q, hits.id(i)));//
        }
    }
}

运行结果:

bc bc    0.629606
0.629606 = (MATCH) fieldWeight(bookname:bc in 0), product of:
  1.4142135 = tf(termFreq(bookname:bc)=2)
  0.71231794 = idf(docFreq=3, numDocs=3)
  0.625 = fieldNorm(field=bookname, doc=0) 

ab bc    0.4451987
0.4451987 = (MATCH) fieldWeight(bookname:bc in 1), product of:
  1.0 = tf(termFreq(bookname:bc)=1)
  0.71231794 = idf(docFreq=3, numDocs=3)
  0.625 = fieldNorm(field=bookname, doc=1) 

ab bc cd    0.35615897
0.35615897 = (MATCH) fieldWeight(bookname:bc in 2), product of:
  1.0 = tf(termFreq(bookname:bc)=1)
  0.71231794 = idf(docFreq=3, numDocs=3)
  0.5 = fieldNorm(field=bookname, doc=2) 

涉及到的源码:

idf的计算

idf是项在倒排文档中出现的频率,计算方式为

  1. /** Implemented as <code>log(numDocs/(docFreq+1)) + 1</code>. */
  2. @Override
  3. public float idf(long docFreq, long numDocs) {
  4. return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);
  5. }

docFreq是根据指定关键字进行检索,检索到的Document的数量,我们测试的docFreq=14;numDocs是指索引文件中总共的Document的数量,我们测试的numDocs=1453。用计算器验证一下,没有错误,这里就不啰嗦了。

queryNorm的计算

queryNorm的计算在DefaultSimilarity类中实现,如下所示:

  1. /** Implemented as <code>1/sqrt(sumOfSquaredWeights)</code>. */
  2. public float queryNorm(float sumOfSquaredWeights) {
  3. return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
  4. }

这里,sumOfSquaredWeights的计算是在org.apache.lucene.search.TermQuery.TermWeight类中的sumOfSquaredWeights方法实现:

  1. public float sumOfSquaredWeights() {
  2. queryWeight = idf * getBoost();             // compute query weight
  3. return queryWeight * queryWeight;          // square it
  4. }

其实默认情况下,sumOfSquaredWeights = idf * idf,因为Lucune中默认的boost = 1.0。

fieldWeight的计算

在org/apache/lucene/search/similarities/TFIDFSimilarity.java的explainScore方法中有:

  1. // explain field weight
  2. Explanation fieldExpl = new Explanation();
  3. fieldExpl.setDescription("fieldWeight in "+doc+
  4. ", product of:");
  5. Explanation tfExplanation = new Explanation();
  6. tfExplanation.setValue(tf(freq.getValue()));
  7. tfExplanation.setDescription("tf(freq="+freq.getValue()+"), with freq of:");
  8. tfExplanation.addDetail(freq);
  9. fieldExpl.addDetail(tfExplanation);
  10. fieldExpl.addDetail(stats.idf);
  11. Explanation fieldNormExpl = new Explanation();
  12. float fieldNorm = norms != null ? decodeNormValue(norms.get(doc)) : 1.0f;
  13. fieldNormExpl.setValue(fieldNorm);
  14. fieldNormExpl.setDescription("fieldNorm(doc="+doc+")");
  15. fieldExpl.addDetail(fieldNormExpl);
  16. fieldExpl.setValue(tfExplanation.getValue() *
  17. stats.idf.getValue() *
  18. fieldNormExpl.getValue());
  19. result.addDetail(fieldExpl);

重点是这一句:

  1. fieldExpl.setValue(tfExplanation.getValue() *
  2. stats.idf.getValue() *
  3. fieldNormExpl.getValue());

使用计算式表示就是

fieldWeight = tf * idf * fieldNorm

tf和idf的计算参考前面的,fieldNorm的计算在索引的时候确定了,此时直接从索引文件中读取,这个方法并没有给出直接的计算。如果使用DefaultSimilarity的话,它实际上就是lengthNorm,域越长的话Norm越小,在org/apache/lucene/search/similarities/DefaultSimilarity.java里面有关于它的计算:

  1. public float lengthNorm(FieldInvertState state) {
  2. final int numTerms;
  3. if (discountOverlaps)
  4. numTerms = state.getLength() - state.getNumOverlap();
  5. else
  6. numTerms = state.getLength();
  7. return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));
  8. }

参考文献:

【1】http://www.hankcs.com/program/java/lucene-scoring-algorithm-explained.html

【2】http://grantbb.iteye.com/blog/181802

时间: 2024-11-10 11:41:07

Lucene的评分(score)机制研究的相关文章

Lucene Scoring 评分机制

原文出处:http://blog.chenlb.com/2009/08/lucene-scoring-architecture.html Lucene 评分体系/机制(lucene scoring)是 Lucene 出名的一核心部分.它对用户来说隐藏了很多复杂的细节,致使用户可以简单地使用 lucene.但个人觉得:如果要根据自己的应用调节评分(或结构排序),十分有必须深入了解 lucene 的评分机制. Lucene scoring 组合使用了 信息检索的向量空间模型 和 布尔模型 . 首先来

【研究课题】高校特殊学生的发现及培养机制研究

依据关于征集2014年省教育科学研究重大课题选题的通知,要求推荐选题限制在300字介绍,内容例如以下: 选题:高校特殊学生的发现及培养机制研究 简要说明: 特殊学生主要有:A类(有目标,主动学习):B类(无目标,以逃课打游戏等方式度日):C类(认真但学习成绩中等及偏下). 宽松或严格的课堂考勤对学生有不同影响,A类需宽松,B类需严格,C类需单独引导. 研究目标:利用在线综合考勤平台,及早发现特殊学生,对三类学生做到个性化关怀.因材施教. 预期成果:学生学习成绩提高10%,学生对大学惬意度提高20

lucene 评分机制研究

评分公式 1.coord(q,d),查询覆盖率 /** Implemented as <code>overlap / maxOverlap</code>. */ @Override public float coord(int overlap, int maxOverlap) { return overlap / (float)maxOverlap; } 例如: 查询:query=title:search and content:lucenen 确定最大覆盖maxOverlap =

lucene自定义评分

要实现自定义评分,想把我们认为应该排在前面成为top,lucenen给我们留了一个扩展类就是CustomScoreQuery 首先.我们创建评分的时候要先定义一个我们自己要改变的评分域 FieldScoreQuery fieldScoreQuery=new FieldScoreQuery("score", Type.INT);//设置评分域为socre 然后indexSearch.search中要传入一个CustomScoreQuery,要覆盖getCustomScoreProvide

lucene 自定义评分 影响排序

前记 这段时间需要修改一个别人写的一个搜索有关的项目,恰好底层使用的是lucene搜索框架. 为什么要去修改呢,当然是搜索结果不太令人满意啦,于是去研读了项目中关于搜索的代码...... 正文 经过了几天代码的研读,最终总结出来了几条问题: 创建索引的过程,相当简单,感觉仅仅是把lucene当成关键词匹配的工具去了(需要修改索引策略) 搜索的过程也是比较简单,没有结合项目的需求,定制化搜索(搜索策略需要修改) 额,感觉上面两条好像是在说废话,感觉lucene的和核心就是索引和搜索 为了能尽快解决

Lucene中使用Score进行自然排序

/* *这里是使用Lucene进行相关度排序 *使用Score进行自然排序 *相关度排序是一种最简单的排序方式. *所谓相关度,其实是文档的得分 *文档的得分在每次查找的时候都不同, *需要根据查找的关键字来确定. * *tf词条频率 *idf反转文档频率 *boost Field的激励因子 *LengthNum长度因子  */ 结果图: package score; import java.io.IOException; import org.apache.lucene.analysis.st

Spring Transaction + MyBatis SqlSession事务管理机制研究学习

线上的系统中,使用的是Spring+Mybatis+Mysql搭建的框架,由于客户需要,最近一直在对性能提升部分进行考虑,主要是涉及Mysql的一些重要参数的配置学习,以及Spring事务管理机制的学习,因为通过观察服务器日志,发现在这两部分的时候耗时比较严重,特别是进行mysql事务提交的时候,项目源码中使用了Spring的声明式事务,即通过@Transactional注解来控制事务的开启与提交,这两天看了一些关于Spring Transaction事务的一些文章,也debug了源码,总算有点

内容中心网络下的缓存替换机制研究

关于缓存替换机制的问题主要有: 1.当替换发生时,替换哪些数据比较合适? 对于这个问题,可以通过一些算法,计算出缓存中每个数据块的价值,每次替换价值最小的数据.关于如何计算缓存数据的价值问题将在下面进行介绍. 由于不同的节点可能存储相同的的数据,因而在替换数据时,不仅考虑当前路由器的状态,同时也通过缓存协同,在了解了周围路由器的信息后再做替换决策.而要实现缓存的协同,路由器中需要保存与它交互的路由器中的信息,比如,我们在每个路由器中建立一个索引表,它记录了与之交互的路由其中保存的缓存信息. 假如

Android ----------- 事件分发 机制 研究

事件的分发机制: 1 Android 中 对应屏幕 点击事件, 会进行 分发: 屏幕及 是由硬件完成物理坐标计算的.生成一个对应于 手机屏幕 左上角的 偏移量! 包含: rawX  rawY 2 setContentView(R.layout.activity_main); 当屏幕 发生 点击事件的时候: Android 会将 原始的点击坐标, 发送给 windows 类.  Windows(系统级别的类) 手机的窗口, 负责 事件传递的老祖宗. 有Android 系统提供的 并且只有 一个Wi