2024-03-20 10:03:16 版本 : Lucene评分规则机制
作者: 系统管理员1 于 2024年03月20日 发布在分类 / 常见问题 / 其他 下,并于 2024年03月20日 编辑
 历史版本

备注 修改日期 修改人
创建版本 2024-03-20 10:03:16[当前版本] 系统管理员1


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接: https://blog.csdn.net/a822631129/article/details/78550439


最近部门把公司的搜索业务接过来了,搜索使用的solr,需要研究下solr的评分机制,solr评分规则引用的是Lucene,简单整理了下Lucene评分规则,如下。

1 简介

Lucene默认是按照评分机制对每个Document进行打分,然后在返回结果中按照得分进行降序排序。

Lucene的评分是叫做TF/IDF算法,基本意思就是词频算法。

根据分词词库,所有的文档在建立索引的时候进行分词划分。进行搜索的时候,也对搜索的短语进行分词划分。

TF代表分词项在文档中出现的次数(term frequency),IDF逆向文件频率,可以简单理解为由总文件数目除以包含该词语之文件的数目,包含该词语之文件的数目越小,结果越大,说明该词越重要。

lucene的算法简单来说就是将搜索的短语进行分词得出分词项,每个分词项和每个索引中的文档根据TF/IDF进行词频出现的评分计算。然后每个分词项的得分相加,就是这个搜索对应的文档得分。

2 评分公式


这个评分公式有6个部分组成

· coord(q,d)评分因子,基于文档中出现查询项的个数。越多的查询项在一个文档中,说明文档的匹配程度越高。

· queryNorm(q)查询的标准查询

· tf(t in d)指项t在文档d中出现的次数frequency。具体值为次数的开根号。

· idf(t)反转文档频率,出现项t的文档数docFreq

· t.getBoost查询时候查询项加权

· norm(t,d)长度相关的加权因子

3 公式详解

3.1 coord(q, d)

这个评分因子的计算公式是:

public float coord(int overlap, int maxOverlap) {
  return overlap / (float)maxOverlap;
}

· overlap:文档中命中检索的个数

· maxOverlap:检索条件的个数

比如检索"englishbook"现在有一个文档是"this is an chinesebook"
那么,这个搜索对应这个文档的overlap1(因为匹配了book),而maxOverlap2(因为检索条件有两个bookenglish)。
最后得到的这个搜索对应这个文档的coord值为0.5

3.2 queryNorm(q)

这个因素对所有文档都是一样的值,所以它不影响排序结果。比如如果我们希望所有文档的评分大一点,那么我们就需要设置这个值。

public float queryNorm(float sumOfSquaredWeights) {
  return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
}
public float sumOfSquaredWeights() {
 //除了用户指定的boost以外,其他都不计算在打分内
 queryWeight = getBoost();
 return queryWeight * queryWeight;
}

3.3 tf(t in d)

t在文档d中出现的次数

public float tf(float freq) {
  return (float)Math.sqrt(freq);
}

比如有个文档叫做"thisis book about chinese book"我的搜索项为"book",那么这个搜索项对应文档的freq就为2,那么tf值就为根号2,即1.4142135

3.4 idf

public float idf(long docFreq, long numDocs) {
  return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);
}

这里的两个值解释下

· docFreq指的是项出现的文档数,就是有多少个文档符合这个搜索

· numDocs指的是索引中有多少个文档。

比如我现在有三个文档,分别为:

· this book is about english

· this book is about chinese

· this book is about japan

我要搜索的词语是"chinese",那么对第二篇文档来说,docFreq值就是1,因为只有一个文档符合这个搜索,而numDocs就是3。最后算出idf的值是:

(float)(Math.log(numDocs/(double)(docFreq+1))+ 1.0) = ln(3/(1+1)) + 1 = ln(1.5) + 1 = 0.40546510810816 + 1 =1.40546510810816

3.5 t.getBoost

查询时期项t的加权,这个就是一个影响值,比如我希望匹配chinese的权重更高,就可以把它的boost设置为2

3.6 norm(t,d)

这个项是长度的加权因子,目的是为了将同样匹配的文档,比较短的放比较前面
比如两个文档:

· chinese

· chinese book

我搜索chinese的时候,第一个文档会放比较前面。因为它更符合"完全匹配"

norm(t,d) = doc.getBoost()· lengthNorm· ∏ f.getBoost()
public float lengthNorm(FieldInvertState state) {
  final int numTerms;
  if (discountOverlaps)
    numTerms = state.getLength() - state.getNumOverlap();
  else
    numTerms = state.getLength();
  return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));
}

这里的doc.getBoost表示文档的权重,f.getBoost表示字段的权重,如果这两个都设置为1,那么nor(t,d)就和lengthNorm一样的值。

比如我现在有一个文档:

· chinese book

搜索的词语为chinese那么numTerms2lengthNorm的值为 1/sqrt(2) = 0.71428571428571

但是非常遗憾,如果你使用explain去查看es的时候,发现lengthNorm显示的只有0.625
这个官方给出的原因是精度问题,norm在存储的时候会进行压缩,查询的时候进行解压,而这个解压是不可逆的,即decode(encode(0.714))= 0.625


注意事项:

· TF-IDF算法是以 term为基础的,term就是最小的分词单元,这说明分词算法对基于统计的ranking无比重要,如果你对中文用单字切分,那么就会损失所有的语义相关性,这个时候搜索只是当做一种高效的全文匹配方法。

· TF在生成索引的时候,就会计算出来并保存,而IDF是在query的时候获取,包含t的文档数= lengthtermposting list)

历史版本-目录  [回到顶端]
    wcp知识库系统-京ICP备15024440号-1 -V 5.2.0 -wcp