欢迎您光临本小站。希望您在这里可以找到自己想要的信息。。。

lucene评分详解

架构&设计模式 water 365℃ 0评论

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

Lucene得分计算公式如下所示:

score(q,d)= coord(q,d)·queryNorm(q)·∑( tf(t in d)·idf(t)^2·t.getBoost()·norm(t,d) )

coord(q,d)因子

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

coord(q,d) = overlap / maxOverlap 

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

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

doc.add(new Field("title", "search engine",Field.Store.YES, Field.Index.ANALYZED));
doc.add(new Field("content", "good lucene luke lucene searchserver", Field.Store.YES, Field.Index.ANALYZED));

我们看到,检索title:search和content:lucene这两个Term,在title这个Field中("searchengine")匹配上title:search,

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

queryNorm(q)因子

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

queryNorm(q) = queryNorm(sumOfSquaredWeights)=1/(sumOfSquaredWeights^(1/2)) 

sumOfSquaredWeights

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

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值的,下面我们通过一个相对比较复杂的例子来说明一下:

BooleanQuerybq1 = new BooleanQuery(); // 第一个BooleanQuery查询子句
TermQuery tq1 = new TermQuery(new Term("title", "search"));
tq1.setBoost(2.0f);
bq1.add(tq1, Occur.MUST); 
TermQuery tq2 = new TermQuery(new Term("content","lucene"));
tq2.setBoost(5.0f);
bq1.add(tq2, Occur.MUST);
bq1.setBoost(0.1f); // 给第一个查询子句乘上0.1,实际是减弱了其贡献得分的重要性
BooleanQuery bq2 = new BooleanQuery(); // 第二个BooleanQuery查询子句
TermQuery tq3 = new TermQuery(new Term("title", "book"));
tq3.setBoost(8.0f);
bq2.add(tq3, Occur.MUST);
TermQuery tq4 = new TermQuery(new Term("content","lucene"));
tq4.setBoost(5.0f);
bq2.add(tq4, Occur.MUST);
bq2.setBoost(10.0f); // 给第二个查询子句乘上10.0,该子句更重要
BooleanQuery bq = new BooleanQuery(); // 对上述两个BooleanQuery查询子句再进行OR运算
bq.add(bq1, Occur.SHOULD);
bq.add(bq2, Occur.SHOULD);

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

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

idf(t)

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

idf(t)= 1.0 + log(numDocs/(docFreq+1))

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

t.getBoost()

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

TermQuery tq3 = new TermQuery(new Term("title","book"));
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)的含义,计算公式如下所示:

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中计算公式如下:

lengthNorm = 1.0 / Math.sqrt(numTerms) 

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

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

f.getBoost()

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

Document doc = new Document();
doc.add(new Field("title", "search engine",Field.Store.YES, Field.Index.ANALYZED));
Field fcontent1 = new Field("content", "nutch solr lucene lucenesearch server", Field.Store.YES, Field.Index.ANALYZED);
fcontent1.setBoost(2.0f);
doc.add(fcontent1);
Field fcontent2 = new Field("content", "good lucene luke luceneindex server", Field.Store.YES, Field.Index.ANALYZED);
fcontent2.setBoost(5.0f);
doc.add(fcontent2);
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。

转载请注明:学时网 » lucene评分详解

喜欢 (0)or分享 (0)

您必须 登录 才能发表评论!