Lucene Scoring 评分机制 ( by quqi99 )
Lucene Scoring 评分机制 http://blog.chenlb.com/2009/08/lucene-scoring-architecture.html
首先来看下 lucene 的评分公式(在 Similarity 类里的说明)
|
其中:
- tf(t in d) 关联到项频率,项频率是指 项 t 在 文档 d 中出现的次数 frequency。默认的实现是:
tf(t in d) = frequency½
- idf(t) 关联到反转文档频率,文档频率指出现 项 t 的文档数docFreq。docFreq 越少 idf 就越高(物以稀为贵),但在同一个查询下些值是相同的。默认实现:
idf(t) = 1 + log( numDocs ––––––––– docFreq+1 )
- coord(q,d) 评分因子,是基于文档中出现查询项的个数。越多的查询项在一个文档中,说明些文档的匹配程序越高。默认是出现查询项的百分比。
- queryNorm(q) 查询的标准查询,使不同查询之间可以比较。此因子不影响文档的排序,因为所有有文档都会使用此因子。默认值:
queryNorm(q) = queryNorm(sumOfSquaredWeights) = 1 –––––––––––––– sumOfSquaredWeights ½ 每个查询项权重的平分方和(sumOfSquaredWeights)由 Weight 类完成。例如 BooleanQuery 地计算:
sumOfSquaredWeights = q.getBoost() 2 · ∑ ( idf(t) ·t.getBoost() )2 t in q
- t.getBoost() 查询时期的 项 t 加权(如:java^1.2),或者由程序使用 setBoost()。
- norm(t,d) 压缩几个索引期间的加权和长度因子:
- Document boost - 文档加权,在索引之前使用 doc.setBoost()
- Field boost - 字段加权,也在索引之前调用 field.setBoost()
- lengthNorm(field) - 由字段内的 Token 的个数来计算此值,字段越短,评分越高,在做索引的时候由 Similarity.lengthNorm 计算。
以上所有因子相乘得出 norm 值,如果文档中有相同的字段,它们的加权也会相乘:norm(t,d) = doc.getBoost() ·lengthNorm(field) · ∏ f.getBoost() fieldf ind named ast 索引的时候,把 norm 值压缩(encode)成一个 byte 保存在索引中。搜索的时候再把索引中 norm 值解压(decode)成一个 float 值,这个 encode/decode 由 Similarity 提供。官方说:这个过程由于精度问题,以至不是可逆的,如:decode(encode(0.89)) = 0.75。
/**
* 利用IndexReader获取下列信息,注意,建索引时需加Field.TermVector.YES:
(1) 统计term在整个collection中的文档频度(document frequency, DF);
(2) 统计term在整个collection中出现的词次(term frequency in whole collection);
(3) 统计term在某个文档中出现的频度(term frequency, TF);
(4) 列出term在某文档中出现的位置(position);
(5) 整个collection中文档的个数;
*/
public static void printIndex(IndexReader reader) throws Exception
{
// 显示有多少个document
System.out.println(reader + "\tNumDocs = " + reader.numDocs());
for (int i = 0; i < reader.numDocs(); i++)
{
System.out.println(reader.document(i));
}
// 枚举term,获得<document, term freq, position* >信息
TermEnum termEnum = reader.terms();
while (termEnum.next())
{
System.out.println(termEnum.term());
System.out.println("\tDocFreq=" + termEnum.docFreq());
TermPositions termPositions = reader.termPositions(termEnum.term());
int i = 0;
int j = 0;
while (termPositions.next())
{
System.out.println((i++) + "->" + " DocNo:" + termPositions.doc() + ", Freq:" + termPositions.freq());
for (j = 0; j < termPositions.freq(); j++)
System.out .println("[" + termPositions.nextPosition() + "]");
System.out.println();
}
// 直接获取 <term freq, document> 的信息
TermDocs termDocs = reader.termDocs(termEnum.term());
while (termDocs.next())
{
System.out.println((i++) + "->" + " DocNo:" + termDocs.doc() + ", Freq:" + termDocs.freq());
}
}
//在reader是SegmentReader类型的情况下有效
// FieldInfos fieldInfos = reader.fieldInfos;
// FieldInfo pathFieldInfo = fieldInfos.FieldInfo("path");
// 显示 term frequency vector
for (int i = 0; i < reader.numDocs(); i++)
{
// 对contents的token之后的term存于了TermFreqVector
TermFreqVector termFreqVector = reader.getTermFreqVector(i,"contents");
if (termFreqVector == null)
{
System.out.println("termFreqVector is null.");
continue;
}
String fieldName = termFreqVector.getField();
String[] terms = termFreqVector.getTerms();
int[] frequences = termFreqVector.getTermFrequencies();
System.out.println("FieldName:" + fieldName);
for (int j = 0; j < terms.length; j++)
{
System.out.println("[" + terms[j] + ":" + frequences[j] + "]");
}
System.out.println();
}
System.out.println();
}