Lucene打分公式的推导

·  阅读 1547
Lucene打分公式的推导

lucene 介绍

lucene是一个高效的,基于Java的全文检索库,利用lucene可以很方便加入到现有应用中来提供全文检索的功能。比如分布式搜索引擎Elastic Search底层的搜索就是使用lucene。(全文检索是从大量的现有的文档中,搜索出与查询语句最相关的文档集的过程)

为了更高效的完成全文检索的过程,lucene使用倒排索引来完成查询词到文档查找。lucene全文检索的过程主要由创建索引和搜索索引,本文主要介绍文档相关性排序部分。

lucene中相关概念:

  • document:lucene存储的基本单位,类似于mysql数据库中一条记录
  • field:document中一个字段,类似于mysql数据库表中一个字段

note:本文分析的lucene版本为5.1.0

倒排索引模型

左边一列是按照一定顺序排列的经过分词处理后的Term,称为字典;每个词都指向的包含该词的文档组成链表称为倒排表。

lucene处理流程

创建索引的过程:

  • 准备待索引的原文档,数据来源可能是文件、数据库或网络
  • 对文档的内容进行分词组件处理,形成一系列的Term
  • 索引组件对文档和Term处理,形成字典和倒排表

搜索索引的过程:

  • 对查询语句进行分词处理,形成一系列Term
  • 根据倒排索引表查找出包含Term的文档,并进行合并形成符合结果的文档集
  • 比对查询语句与各个文档相关性得分,并按照得分高低返回

评分公式推导

空间向量模型

lucene的评分计算模型是基于VSM,即空间向量模型,但实现上稍有不同。
空间向量模型的逻辑就是将每个文档看做是由N个Term组成的向量,文档间相关性就是两个向量的在N维空间内的夹角大小,由于两个向量的之间的夹角越小,相关性就越大,所以就可以将夹角的余弦值作为相关性的得分,夹角越小,余弦值越大,相关性越大。

VSM(空间向量模型) 计算公式:

比如索引中的文档
Document Vector = {Weight1, Weight2, ..., Weight N},查询语句也可以看做是一个文档,查询文档:Query Vector = {Weight1, Weight2, ..., Weight N},Weight是某个Term的权重,N是所有文档包括查询文档在内的所有Term总数,如果文档不包含某个Term,则文档向量中Term的Weight为0。

Term的权重计算

影响一个Term在文档中权重大小的因素主要有两个:

  • Term Frequency(tf):即该Term在该文档中出现的次数,tf越大说明越重要;
  • Document Frequency(df):即有多少个文档包含了该Term,df越小说明越重要;

计算公式:

以上是Term weight计算的典型实现,Lucene实现于此稍有不同,使用的是tf(t in d ) * idf(t)。

lucene公式推导

我们首先计算余弦公式的分子部分,也即两个向量的点积:

在这里有三点需要指出:

  • 由于是点积,则此处的 t1, t2, ..., tn 只有查询语句和文档的并集有非零值,只在查询语句出现的或只在文档中出现的 Term 的项的值为零。
  • 在查询的时候,很少有人会在查询语句中输入同样的词,因而可以假设 tf(t, q)都=1
  • idf 是指 Term 在多少篇文档中出现过,其中也包括查询语句这篇小文档,因而 idf(t, q)和 idf(t, d)其实是一样的,是索引中的文档总数加一,当索引中的文档总数足够大的时候,查询语句这篇小文档可以忽略,因而可以假设 idf(t, q) = idf(t, d) = idf(t)

下面推导查询语句的长度
由上面的讨论,查询语句中 tf 都为 1,idf 都忽略查询语句这篇小文档,得到如下公式:

接下来推导文档的长度
为什么在打分过程中,需要除以文档的长度呢? 因为在索引中,不同的文档长度不一样,很显然,对于任意一个 term,在长的文档中的 tf 要大的多,因而分数也越高,这样对小的文档不公平,举一个极端的例子,在一篇 1000 万 个词的鸿篇巨著中,“lucene” 这个词出现了 11 次,而在一篇 12 个词的短小文档中,“lucene” 这个词出现了 10 次,如果不考虑长度在内,当然鸿篇巨著应该分数更高,然而显然这篇小文档才是真正关注“lucene” 的。然而如果按照标准的余弦计算公式,完全消除文档长度的影响,则又对长文档不公平(毕竟它是包含更多的信息),偏向于首先返回短小的文档的,这样在实际应用中使得搜索结果 很难看。

在默认状况下,Lucene 采用 DefaultSimilarity,认为在计算文档的向量长度的时候,每个 Term 的权重就不再考虑在内了,而是全部为一。

再加上各种 boost 和 coord,则可得出 Lucene 的计算公式

lucene计算公式

coord(q,d):得分因子,score factor,一个文档中包含越多的查询Term词,则该文档的得分越高,对应lucene类TFIDFSimilarity.coord(int overlap, int maxOverlap)

queryNorm(q):归一化因子,normalizing factor,使得不同查询间的得分具有可比性,但并不会影响文档的排序,对应lucene类TFIDFSimilarity.queryNorm(float sumOfSquaredWeights)

tf(t in d):Term在文档d中出现的次数,对应lucene类TFIDFSimilarity.tf(float freq)

idf(t):Term的逆文档频率,即一个Term在所有文档中出现的次数越多,重要性越小,对应lucene类TFIDFSimilarity.idf(long docFreq, long numDocs)

norm(t,d):封装了文档字段field的权重和field内容长度因子,在index时计算,对应lucene类 TFIDFSimilarity.computeNorm(FieldInvertState state)

lengthNorm:field内容长度因子,field字段内容长度越短,则值越大,对应lucene类TFIDFSimilarity.lengthNorm(FieldInvertState state);

q.getBoost():查询的权重(默认值为1.0)
t.getBoost():子查询的权重(默认值为1.0)
f.getBoost():filed字段的权重(认值为1.0)

设置Query权重

BooleanQuery parentQuery=new BooleanQuery();
parentQuery.setBoost(1.0f);

TermQuery termQuery=new TermQuery(new Term("search"));
termQuery.setBoost(2.0f);
parentQuery.add(termQuery,BooleanClause.Occur.SHOULD);

termQuery=new TermQuery(new Term("掘金"));
termQuery.setBoost(2.0f);
parentQuery.add(termQuery,BooleanClause.Occur.SHOULD);

reader=DirectoryReader.open(index_directory);
IndexSearcher searcher=new IndexSearcher(reader);

TopScoreDocCollector collector=TopScoreDocCollector.create(5);
searcher.search(parentQuery,collector);
ScoreDoc[]hits=collector.topDocs().scoreDocs;复制代码

设置Field权重

Document document = new Document();
TextField questionField = new TextField(FieldConstants.QUESTION, question, Field.Store.YES);
questionField.setBoost(2.0f);
TextField answerField = new TextField(FieldConstants.ANSWER, question, Field.Store.YES);
answerField.setBoost(2.0f);

document.add(questionField);
document.add(answerField);
documents.add(document);复制代码

note:从lucene4.0之后就不在支持设置document的boost,应该使用filed

分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改