前言
Lucene官方文档
http://lucene.apache.org/core/6_0_0/core/org/apache/lucene/search/similarities/TFIDFSimilarity.html
上篇文章中介绍了Lucene中下一代的打分公式以及Lucene经典的TF-IDF打分公式。那么Lucene中真正的打分公式是怎样的呢?接下来我们分析一下Lucene的源码(Lucene6.0,从6.0开始Lucene默认打分公式切换到了BM25)了解一下Lucene中具体的TF-IDF打分公式。
相似性计算
Lucene中的相似度计算是信息检索的中布尔模型(Boolean model)和向量空间模型( Vector Space Model,VSM)的结合。在Lucene的VSM中,文档和查询被表示为多维空间中的加权向量,其中每个不同的索引词是维度,权重是tf-idf值。
VSM并不要求必须使用tf-idf的值来计算,但是tf-idf计算的值被认为能够产生高质量的搜索结果,因此Lucene中使用tf-idf的值来计算相似性。下面让我们看一下对于给定的文档d,在确定的查询q下的余弦相似度:
其中
是加权向量的点积,
注意,向量空间模型的基础是余弦定理,即使用两个向量的夹角来表示相似度
Lucene概念评分公式
Lucene从搜索质量和可用性两方面修改了SVM计算:
- 由于上述公式中消除了文档d的长度信息的影响,对某些文档消除文档长度信息是合理的,例如,一个通过复制某一段10次而成的文件。但是对于不包含重复段落的文件,这可能是错误的。为了避免这个问题,使用不同的文档长度归一化因子doc_len_norm(d).
- 在进行索引时,用户可以指定某些文档比其他文档更重要,即对文档提权。每个文档的分数需要乘以其指提权值doc_boost(d).
- Lucene是基于域(Field)的,因此除了文档提权之外还有域提权,例如,文档中的title域其中的内容可能要比content域中的内容更重要,因此在索引过程中可以单独对文档的某个域进行提权。在索引过程中,可以将相同的域添加到文档中(即可以多次添加相同的域),因此域的提权是文档内该域添加提权的乘积。
- 搜索时用户可以对每个查询、子查询、查询词进行提权,因此查询词对文档得分的贡献需要乘以该查询词的提权query_boost(q).
-
一个文档可以匹配查询的多个词,因此可以根据匹配查询的词的个数对文档提权,使用调节因子进行调节coord_factor(q,d).
综上所述,我们可以得到Lucene的概念公式:
Lucene的实用得分公式
概念公式是在某种意义上的简化,(1)词和文档是在域上的,(2)提权通常是基于查询词而不是查询的。
现在来描述Lucene如何实现这个概念评分公式,并从中推导出Lucene的实用评分函数,
为了有效的计算分数,一些评分项会被预先计算和聚合:
- 在搜索开始时,查询(实际上对于每个查询词)的Query_boost(查询提权)是已知的。
-
可以在搜索开始时计算,因为它与被评分的文档无关。从搜索优化的角度看,为什么要那么麻烦要对查询进行归一化处理?每个计算的文档都会被乘以相同数值,文档的最后得分不会因其而改变。但是由于以下原因需要保持这个计算:
- 余弦相似性可以用于比较文档的相似性,也就是说三分是文档级别的,因此查询可以看做是一个文档,只是这个文档比较小,只包含查询词。该值使得两个不同查询的结果文档的得分是可以比较的。对于同一个Index的同一个Query该值是唯一的。作为查询归一化,那么它作用就是缩小同一个Query在不同Index上产生的影响。这使得分布式查询(同一个Query用在不同的Index上并计算文档的分数)的评分变得有意义和有可比性。
- 其可以防止由于浮点精度限制而导致分数数据丢失。
- 文档长度归一化doc_len_norm(d) 和文档提权doc_boost(d)在索引期间是已知的,它们是事先计算出来的,它们的乘积在索引中保存为一个单一的值norm(t in d) 其代表着 norm(field(t) in doc d) ,field(t)表示与t相关的域。
以上,可得出Lucene的实用得分公式:
其中tf(t in d)与词频相关,是词t在文档d中出现的次数,词频越高文档得分越高,对于tf(t in q)默认认为查询的词频为1,Lucene中的默认计算公式为:
idf(t)是逆文档频率,这个值与docFreq(词t在文档中出现的次数)的倒数有关,即文档频率越低得分越高,Lucene中的默认计算公式为:
coord(q,d)是基于在指定文档中找到多少查询词的分数因子。通常,查询词多的文档比查询词少的文档得分更高。这个值是搜索时动态计算的,Lucene中默认的计算公式如下:
maxOverlap:是Query中有效的term数;overlap:表示文档中出现term数
queryNorm(q)是用于使查询之间的分数相当的标准化因子。这个因素不影响文档排名(因为所有排名的文档都乘以相同的因子)而只是试图使得来自不同查询(甚至不同索引)的分数具有可比性。这是一个搜索时因子,由搜索时有效的相似度计算得出。Lucene中的默认计算公式为:
查询项的平方权重的总和由查询权重对象计算。例如,一个BooleanQuery计算这个值为:
的计算公式变为
t.getBoost()是查询过程中对查询q的文本中的指定词t的提权(默认为1,请参阅查询语法),该值也可以通过使用BoostQuery进行设置。
请注意,在多项查询中没有直接的API来获取一个词的权重,但是多词查询可以被表示为多个TermQuery对象,因此可以通过调用子查询的getBoost()来获取。
norm(t,d)在索引过程中封装了一些域权重和长度因子:
- Field boost - 通过在将字段添加到文档之前调用field.setBoost()来设置。衡量Field的重要程度。
- lengthNorm - 当向索引中添加文档时,分别计算不同Field在文档中的长度(term的个数),默认长度短的Field会获得更高的得分。该值在索引建立过程中计算。
computeNorm(org.apache.lucene.index.FieldInvertState)方法负责将所有这些因素组合成一个浮点数。
当一个文件被添加到索引时,所有上述因子都会相乘。如果文档具有多个名称相同的Field,每个Field的权重相乘作为最终权重。
norm(t,d)在索引时计算完毕后将编码为一个Byte存储起来,在搜索时,再从文件中读取出该值并解码成float。在这个过程中,可能会造成精度的缺失,并不能保证decode(encode(morm(t,d))) = morm(t,d)。
以上是Lucene6.0中的javadoc文档翻译。
Lucene评分公式的理解
Lucene中使用空间向量模型,其基于余弦定理,对于搜索中的每一个词term看做向量中的一维,每一维度的值由Lucene中的tf-idf打分公式给出,最后使用余弦定理获得搜索结果的最终得分。
向量余弦定理
则
boost的作用就是在索引时或搜索时,改变filed或者term的重要,从而影响文档的最终得分。
- 两个时间点:搜索时和索引时,都能设置
- 四个级别:Query条件、文档、字段和词条
Lucene概念得分公式到实际得分公式之间的转变
coord_factor(q,d)不变,使用符号coord(q,d)代替,表示文档中与查询词匹配的term数与有效查询词的个数的比值;
作为实际公式中的queryNorm(q),请看之前具体公式;
doc_len_norm(d) * doc_boost(d)合并到
Lucene不同版本中算分公式中的变化
Lucene4.0之前并没有org.apache.lucene.search.similarities包,其默认打分公式是放在org.apache.lucene.search.DefaultSimilarity类中的tf-idf计算公式,在Lucene4.0之前,对Document可以使用方法setBoost(float boost)设置文档权重,4.0之后就删掉了该方法;
在4.0之后增加了专门的包org.apache.lucene.search.similarities用来计算搜索得分,默认使用本文介绍的计算方法,在搜索过程中计算结果得分,4.0到6.0之间一直使用tf-idf作为Lucene默认的算分公式;
Lucene6.0之后,更换了默认的算分公式,默认使用BM25算法计算搜索过程中的文档得分。
参考文献:
http://blog.csdn.net/zteny/article/details/57366074
http://blog.csdn.net/woaizhoulichao1/article/details/6663275
注:能力一般,水平有限,如有不当之处,请批评指正,定当虚心接受!