Lucene的评分机制
Lucene系统的排序是根据信息检索的向量空间模型来计算的。文档(d)和查询条件(q)之间越接近,那么文档(d)的得分就越高。计算公司如下:
score(q,d) = coord(q,d) · queryNorm(q) · ∑ ( tf(t in d) · idf(t)2 · t.getBoost() · norm(t,d) ) t in q
其中:
- tf(t in d)
表示的是查询条件中,每个(t:term)在本文档(d)中的出现频率。查询关键词出现的频率越高,文档的得分就越高。这个部分的默认计算公式是:
2. idf(t)
表示的是反转文档频率( Inverse Document Frequency).这个函数表示的是(t:term)在所有文档中一共在多少个文档中出现过。因为文档出现的次数越少就越容易定位,所以文档数越少,得分就越高。这个函数的默认计算公式如下:
3.coord(q,d)
这个函数表示的是在这个文档(d)中Term(t)出现的百分比,也就是文档中出现的不同Term数量和查询条件(q)中的不同Term(t)的数量之比。所以,文档中出现的Term种类越多,分值就高。
4.queryNorm(q) 这个函数是一个调节因子,不影响具体的排序情况。主要是用来让排序结果在不同的查询条件(或者不同的索引)之间可以比较。这个条件是在搜索的时候计算的。它的计算公式如下: The sum of squared weights (查询条件的terms)是由查询的权重对象计算的。不同的查询方式,有不同的计算方法。例如:Boolean query的计算公式如下:
queryNorm(q) = queryNorm(sumOfSquaredWeights) = 1 –––––––––––– sumOfSquaredWeights½
sumOfSquaredWeights = q.getBoost() 2 · ∑ ( idf(t) · t.getBoost() ) 2 t in q 5.norm(t,d)
这个函数得到的是一些建索引的时候得到的一些参数计算值:encapsulates a few (indexing time) boost and length factors:
- – 文档的boost,是建索引的时候设置的文档得分。
- Field boost – 在将一个字段加入到文档中去的时候加入的字段得分。(不同的字段得分不同有利于排序,例如标题的得分应该比内容的得分要高等)
- lengthNorm(field) – 文档在建立索引的时候加入的一个参数,根据文档的某个字段含有的Term数量来计算的。Term数量比较少的字段将得到更多的得分。这个函数是由Similarity类在建立索引的时候计算的。 (1/numTerms*numTerms)
norm(t,d)=doc.getBoost()·lengthNorm(field) · ∏ f.getBoost() field f in d named as t
最后,norm的结果是经过加密,形成一个单字节的形式保存的。在搜索的时候,这个字节从索引目录中读取,然后解密成float型的norm值。这种压缩/解压缩的过程可以减少索引的大小,但是会有一定精度上的损失。也就是说decode(encode(x))并不一定等于x,比如说decode(encode(0.89))=0.75。
Lucene的排序修改
修改Similarity(相似度计算)
DefaultSimilarity基本上可以满足一般的搜索要求。但是在有些应用中,你可以定制你自己的Similarity来服务你自己的应用需求。例如:有些人认为没有必要让文档短的文章得分更高一点 (参考 a "fair" similarity).
修改Similarity需要同时对索引和搜索都进行修改,必须在搜索或者排序之间修改Similarity。
要定制你自己的Similarity,也就是你不想直接使用DefaultSimilarity,你只要在建立索引的之前调用IndexWriter.setSimilarity,或者在搜索之前调用Searcher.setSimilarity.
你如果想知道,别人都是怎么修改similarity的,你可以参考一下Lucene的邮件列表Overriding Similarity. 总的来说有下面这些修改:
SweetSpotSimilarity
- --
- gives small increases as the frequency increases a small amount and then greater increases when you hit the "sweet spot", i.e. where you think the frequency of terms is more significant.
- Overriding tf -- In some applications, it doesn't matter what the score of a document is as long as a matching term occurs. In these cases people have overridden Similarity to return 1 from the tf() method.
- Changing Length Normalization -- By overriding lengthNorm, it is possible to discount how the length of a field contributes to a score. In DefaultSimilarity, lengthNorm = 1 / (numTerms in field)^0.5, but if one changes this to be 1 / (numTerms in field), all fields will be treated "fairly".
定制你自己的评分系统(专家级)
修改评分系统是专家级的工作,所以你要谨慎工作,随时和别人交流。在Lucene中,修改评分系统将比修改similarity更加能够影响结果。Lucene的评分系统是一个非常复杂的机制,主要由下面三个类来实现:
Query
- -- The abstract object representation of the user's information need.
Weight
- -- The internal interface representation of the user's Query, so that Query objects may be reused.
- Scorer -- An abstract class containing common functionality for scoring. Provides both scoring and explanation capabilities.
The Query Class
从某种意义上来说,Query是评分开始的地方。没有查询就没有什么可以评分的。更重要的是它是其他的评分系统的催化剂,由它来生成其他的评分系统,然后将他们整合起来。Query有一些重要的方法需要被继承:
- createWeight(Searcher searcher) -- A
- is the internal representation of the Query, so each Query implementation must provide an implementation of Weight. See the subsection on
- below for details on implementing the Weight interface.
- rewrite(IndexReader reader) -- Rewrites queries into primitive queries. Primitive queries are: TermQuery, BooleanQuery, OTHERS????
Weight 接口
权重接口主要用来定义Query的一个代表实现接口,所以可以被重用。任何可以用来被搜索的类都应该内置一个Weight,而不是在Query类。这个接口定义了6个要被执行的方法:
Weight#getQuery()
- -- Pointer to the Query that this Weight represents.
Weight#getValue()
- -- The weight for this Query. For example, the TermQuery.TermWeight value is equal to the idf^2 * boost * queryNorm
Weight#sumOfSquaredWeights()
- -- The sum of squared weights. Tor TermQuery, this is (idf * boost)^2
Weight#normalize(float)
- -- Determine the query normalization factor. The query normalization may allow for comparing scores between queries.
Weight#scorer(IndexReader)
- -- Construct a new
- for this Weight. See
- below for help defining a Scorer. As the name implies, the Scorer is responsible for doing the actual scoring of documents given the Query.
- Weight#explain(IndexReader, int) -- Provide a means for explaining why a given document was scored the way it was.
评分类:
Scorer是评分的抽象类,提供一些基本的计分功能供所有的评分类实现,是Lucene评分机制的核心类。Scorer定义了一下的方法,必须被实现。:
Scorer#next()
- -- Advances to the next document that matches this Query, returning true if and only if there is another document that matches.
Scorer#doc()
- -- Returns the id of the
- that contains the match. Is not valid until next() has been called at least once.
Scorer#score()
- -- Return the score of the current document. This value can be determined in any appropriate way for an application. For instance, the
- returns the tf * Weight.getValue() * fieldNorm.
Scorer#skipTo(int)
- -- Skip ahead in the document matches to the document whose id is greater than or equal to the passed in value. In many instances, skipTo can be implemented more efficiently than simply looping through all the matching documents until the target document is identified.
- Scorer#explain(int) -- Provides details on why the score came about.
t.getBoost() Term的权重。这个是在搜索的时候设置的,用户可以在查询条件中设置,也可以由应用程序设置。