Lucene源码(三):全文检索的底层原理

news/2024/7/19 19:34:21 标签: 搜索引擎, 全文检索, lucene, java

文章目录

  • IndexSearcher
    • searchAfter
      • CollectorManager
    • search
    • createNormalizedWeight
    • createWeight
  • TermQuery
    • createWeight
    • TermWeight
  • TFIDFSimilarity
  • BooleanScorer

Lucene源码(一):分词器的底层原理
Lucene源码(二):文本相似度TF-IDF原理

核心代码是下面这几句。Query query中存储搜索文本的分词结果。具体在Lucene源码(一):分词器的底层原理仔细了解分词的底层原理。

java">IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(index)));
IndexSearcher searcher = new IndexSearcher(reader);

Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser(field, analyzer);
Query query = parser.parse(line);

TopDocs results = searcher.search(query, 5 * hitsPerPage);

index是创建的索引目录,filed是索引的字段,5 * hitsPerPage其实就是搜索返回的最大数量。
搜索结果存储在result中,如下:筛选出了分数较高的两个文档ScoreDoc,doc为文档id,score为匹配分数。详细计算方法Lucene源码(二):文本相似度TF-IDF原理
在这里插入图片描述

IndexSearcher

searchAfter

TopDocs results = searcher.search(query, 5 * hitsPerPage),在search方法中,又会进到下面这里来:

第一部分的代码很简单,就是计算出需要遍历的最大数量
在这里插入图片描述

CollectorManager

第二部分的代码是创建一个CollectorManager实例manage,这个实例的作用是管理collectors(可以理解为数据查询,因为数据结构是树,所以就是收集叶子节点),并且更好的进行并行处理。
在这里插入图片描述
这个类需要重写两个方法:

  1. 新建collector的方法
  2. 对带有结果的所有独立的collector进行聚合

search

在这里插入图片描述
Lower-level search API,其实就是新建collector,然后进入另一个search方法,这里需要先执行createNormalizedWeight方法
在这里插入图片描述

createNormalizedWeight

在这里插入图片描述
请记住,这里的query是BooleanQuery,在createWeight里面会进入BooleanWeight的构造方法,在里面又会重新进入IndexSearchercreateWeight,但这个时候的query是TermQuery

createWeight

在这里插入图片描述

TermQuery

createWeight

在这里插入图片描述
这里的变量termSate是TermContext对象,里面存储着含有单词的文档数量docFreq单词的词频totalTermFreq,那么前面部分的代码作用就是做这个统计的。

然后进入TermWeight的构造方法,但是在TermQuery类里面实现的

TermWeight

进来之后,前面都是一些赋值和初始化。
在这里插入图片描述
接着,看关键代码。searcher.collectionStatistics方法统计文档中的所有单词的词频,即所有文档总共有多少个单词,统计结果存储在变量collectionStats中;

第二句的话,就是对象转换了:TermContext–>TermStatistics
在这里插入图片描述
最后,就开始计算TF_IDF了。
在这里插入图片描述

TFIDFSimilarity

Lucene在这个类里面实现TF-IDF计算方法,跟以往的TF-IDF计算是不一样的。所以,如果我们想自定义TF-IDF的计算方式,可以参考这个类重新实现一个TF-IDF的实现类。

进来computeWeight方法之后就跳转到idfExplain方法
在这里插入图片描述
idfExplain方法的功能其实就是计算IDF,返回一个Explanation
在这里插入图片描述
计算完idf之后,Lucene还会进行标准化,返回TFIDFSimilarity对象
在这里插入图片描述
最后,一层层return,还记得最终在哪里返回吗…

再梳理一遍,调用链是这么回事

IndexSearch.createNormalizedWeight–>IndexSearcher.createWeight(BooleanQuery)–>BooleanWeight构造器–>IndexSearcher.createWeight(TermQuery)–>TermQuery.createWeight–>TermQuery.TermWeight–>TFIDFSimilarity.computeWeight–>TFIDFSimilarity.IDFStats

所以,最终是返回到了IndexSearch.createNormalizedWeight方法里,然后又进行一些IDF标准化处理和加入了一些评分因子,具体看上面IndexSearch.createNormalizedWeight截图。

下面就开始计算我们输入的搜索内容跟每个文档的相似度了。

其实,在前面计算好每个Term即单词的TF-IDF值之后,基本上就可以直接得到相似度了,只是把文档中出现的单词的TF-IDF累加起来,即是文档的相似度了,当然这是常规的做法。

但是Lucene的计算方式不一样,它还引入了文档长度的加权因子,作用就是提高短文档的分数,降低长文档的分数。

对应到代码中,做法当然是:遍历每个文档,然后进行计算。前面的代码就是获取文档对应的叶子节点了。
在这里插入图片描述
然后,新建一个对象scorer来计算和存储最终的相似度。这里的BulkScorer是一个接口类,对应的实现类是BooleanScorer,所以,最终是BooleanScorer.score方法中完成计算的。
在这里插入图片描述

BooleanScorer

在这里插入图片描述
从代码不难看出,这里实际就是在对每个单词的分数进行累加,核心的算法还是在TFIDFSimilarity中计算的,即scorer.score()获取分数的方法是TFIDFSimilarity实现方法。如下:
在这里插入图片描述
这个其实就是TF * IDF * 文档长度加权因子(上面提到的)

欢迎关注同名公众号:“我就算饿死也不做程序员”。
交个朋友,一起交流,一起学习,一起进步。在这里插入图片描述


http://www.niftyadmin.cn/n/1693499.html

相关文章

维特比(Viterbi)算法

算法思想 维特比(Viterbi)算法属于一种动态规划算法,目标在于寻找最优路径。我个人用得最多就是与BiLSTMCRF模型的结合,比如命名实体识别、分词等,计算了每个token的归一化概率矩阵和转移概率矩阵之后,最后…

Spring Boot项目技术入门级使用教程

前言 由于本人并没有经常使用Spring Boot项目,没有去阅读源码,此篇博客仅仅是入门级别的使用教程,为了在想简单使用Spring Boot项目时可以用上。 创建springboot项目 在IDEA安装插件:Spring Assistant。然后按平时的操作新建项…

条件随机场(CRF)的原理与实现

一、概率无向图模型 模型定义 又称马尔科夫随机场。设有联合概率分布P(Y),由无向图G(V,E)表示,结点V表示随机变量,边E表示随机变量之间的依赖关系。如果P(Y)满足成对、局部或全局马尔科夫性,就此联合概率分布为概率无向图模型。…

隐马尔科夫模型(HMM)模型训练:Baum-Welch算法

在上一篇博客中隐马尔科夫模型(HMM)原理详解,对隐马尔科夫模型的原理做了详细的介绍。今天,我们要对其中的模型训练算法Baum-Welch做一个实现,Baum-Welch算法可以在不知道状态序列的情况下,对模型的参数进行训练拟合。 这其实是非…

BERT等复杂深度学习模型加速推理方法——模型蒸馏

参考《Distilling the Knowledge in a Neural Network》Hinton等 蒸馏的作用 首先,什么是蒸馏,可以做什么? 正常来说,越复杂的深度学习网络,例如大名鼎鼎的BERT,其拟合效果越好,但伴随着推理…

布隆过滤器 (Bloom Filter):用于超大数据量时检索一个元素是否存在

相信大家在开发过程中,经常会遇到判断一个字符串(或其他类型的变量值)是否已经出现过的需求,这个时候一般使用HashMap可以解决,先将出现过的字符串存于HashMap对象的keySet中,下次只要判断HashMap对象的key…

局部敏感哈希(LSH):高维数据下的最近邻查找

哈希算法 首先,将局部敏感哈希之前,我们先说下普通的哈希算法,把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。 最理想的是所有不同的输入都可以映射到散列值,但是存在这种可能性的。当不同的…

tensorflow是如何实现RNN的递归计算

我们都知道RNN是一种循环神经网络,其实可以认为是一种递归计算,每一个时刻的输出都是根据上一个时刻的输出和本时刻的输入得到: Ht1f(Ht,xt1)H_{t1} f(H_t, x_{t1})Ht1​f(Ht​,xt1​) 那么在tensorflow是如何实现这种递归计算的呢&#xff…