搜索引擎基础概念(2)—— 构建单词词典

  Lucene单词词典

    使用lucene进行查询不可避免都会使用到其提供的单词词典功能,即根据给定的term找到该term所对应的倒排文档id列表等信息。实际上lucene索引文件后缀名为tim和tip的文件实现的就是lucene的单词词典功能。

    怎么实现一个单词词典呢?我们马上想到排序数组,即term字典是一个已经按字母顺序排序好的数组,数组每一项存放着term和对应的倒排文档id列表。每次载入索引的时候只要将term数组载入内存,通过二分查找即可。这种方法查询时间复杂度为Log(N),N指的是term数目,占用的空间大小是O(N*str(term))。排序数组的缺点是消耗内存,即需要完整存储每一个term,当term数目多达上千万时,占用的内存将不可接受。

    单词词典是倒排索引中非常重要的组成部分,它用来维护文档集合中出现过的所有单词的相关信息,同时用来记载某个单词对应的倒排列表在倒排文件中的位置信息,如图1-2所示。在支持搜索时,根据用户的查询词,去单词词典里查询,就能够获得相应的倒排列表,并以此作为后续排序的基础。

                      

    对于一个规模很大的文档集合来说,可能包含几十万甚至上百万的不同单词,能否快速定位某个单词,这直接影响搜索时的响应速度,所以需要高效的数据结构来对单词词典进行构建 和查找,常用的数据结构包括哈希加链表结构和树形词典结构和FST有穷状态转换器。

    很多数据结构均能完成字典功能,总结如下。

数据结构 优缺点
排序列表Array/List 使用二分法查找,不平衡
HashMap/TreeMap 性能高,内存消耗大,几乎是原始数据的三倍
Skip List 跳跃表,可快速查找词语,在lucene、redis、Hbase等均有实现。相对于TreeMap等结构,特别适合高并发场景(Skip List介绍
Trie 适合英文词典,如果系统中存在大量字符串且这些字符串基本没有公共前缀,则相应的trie树将非常消耗内存(数据结构之trie树
Double Array Trie 适合做中文词典,内存占用小,很多分词工具均采用此种算法(深入双数组Trie
Ternary Search Tree 三叉树,每一个node有3个节点,兼具省空间和查询快的优点(Ternary Search Tree
Finite State Transducers (FST) 一种有限状态转移机,Lucene 4有开源实现,并大量使用

  一:哈希加链表

      图 1-3 是这种词典结构的示意图。这种词典结构主要由两个部分构成,主体部分是哈希表, 每个哈希表项保存一个指针,指针指向冲突链表,在冲突链表里,相同哈希值的单词形成链表结构。之所以会有冲突链表,是因为两个不同单词获得相同的哈希值,如果是这样,在哈希方法里被称做是一次冲突,可以将相同哈希值的单词存储在链表里,以供后续查找。

      注:

        -》主体部分为什么使用哈希表?

          数组、向量、集合等都可以存储对象,但是对象的位置是随机的,也就是说对象本身和位置并没有必然的联系,当要查找一个对象时,只能以某种顺序(如顺序查找或二分查找)与各个元素进行比较,当数组或向量或集合中的元素数量很多时,查找的效率会明显的降低。Hash表利用key、value模式,散列无序存储,效率高很多。

        -》为什么会有Hash冲突?

          哈希的原理就是抽样,取信息的特征。两个不同单词通过hash算法获得哈希值是有可能相同的(如果是这样,在哈希方法里被称做是一次冲突),而实际的哈希表,全部都要处理哈希值冲突的情况。而链表法就是解决hash冲突的一种手段(可以将相同哈希值的单词存储在链表里,以供后续查找),如:向哈希表中添加一个词汇2时,如果哈希表中已经存在一个具有相同哈希值的词汇1,则将词汇2添加到以词汇1为头节点的链表中。

                              

                                 图1-3 哈希加冲突链表词典结构

    在建立索引的过程中,词典结构也会相应地被构建出来。比如在解析一个新文档的时候,对于某个在文档中出现的词汇A:

      第1步:首先利用哈希函数获得其哈希值

      第2步:之后根据哈希值对应的哈希表项读取其中保存的指针,找到了对应的冲突链表。

      第3步:如果冲突链表里已经存在这个单词, 说明单词在之前解析的文档里已经出现过。

      第4步:如果在冲突链表里没有发现这个单词,说明该单词是首次碰到,则将其加入冲突链表里。

    通过这种方式,当文档集合内所有文档解析完毕时,相应的词典结构也就建立起来了。

    在响应用户查询请求时,其过程与建立词典类似,不同点在于即使词典里没出现过某个单词, 也不会添加到词典内。以图 1-3 为例,

      第1步:假设用户输入的查询请求为单词3,对这个单词进行哈希。

      第2步:定位到哈希表内的 2 号槽。

      第3步:从其保留的指针可以获得冲突链表。

      第4步:依次将单词 3 和冲突链表内的单词比较,发现单词 3 在冲突链表内,于是找到这个单词。之后可以读出这个单词对应的倒排列表来进行后续的工作。

      第5步:如果没有找到这个单词,说明文档集合内没有任何文档包含单词,则搜索结果为空。

  二:树形结构

    B 树(或者 B+树)是另外一种高效查找结构,图 1-4 是一个 B 树结构示意图。B树与哈希方式查找不同,需要字典项能够按照大小排序(数字或者字符序),而哈希方式则无须数据满足此项要求。

    B 树形成了层级查找结构,中间节点用于指出一定顺序范围的词典项目存储在哪个子树中, 起到根据词典项比较大小进行导航的作用,最底层的叶子节点存储单词的地址信息,根据这个地址就可以提取出单词字符串。

                  

图1-4 B树查找结构

  三:FST有穷状态转换器

     Finite State Transducers 简称 FST, 中文名:有穷状态转换器。在自然语言处理等领域有很大应用,其功能类似于字典的功能(STL 中的map,C# 中的Dictionary),但其查找是O(1)的,仅仅等于所查找的key长度。目前Lucene4.0在查找Term时就用到了该算法来确定此Term在字典中的位置。

    lucene从4开始大量使用的数据结构是FST(Finite State Transducer)。FST有两个优点:1)空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;2)查询速度快。O(len(str))的查询时间复杂度。

    

    下面简单描述下FST的构造过程。我们对“cat”、 “deep”、 “do”、 “dog” 、“dogs”这5个单词进行插入构建FST(注:必须已排序)。

    1)插入“cat”

     插入cat,每个字母形成一条边,其中t边指向终点。

    

    2)插入“deep”

    与前一个单词“cat”进行最大前缀匹配,发现没有匹配则直接插入,P边指向终点。

  

    3)插入“do”

    与前一个单词“deep”进行最大前缀匹配,发现是d,则在d边后增加新边o,o边指向终点。

 

    4)插入“dog”

    与前一个单词“do”进行最大前缀匹配,发现是do,则在o边后增加新边g,g边指向终点。

 

    5)插入“dogs”

     与前一个单词“dog”进行最大前缀匹配,发现是dog,则在g后增加新边s,s边指向终点。

 

    最终我们得到了如上一个有向无环图。利用该结构可以很方便的进行查询,如给定一个term “dog”,我们可以通过上述结构很方便的查询存不存在,甚至我们在构建过程中可以将单词与某一数字、单词进行关联,从而实现key-value的映射。

    

    FST压缩率一般在3倍~20倍之间,相对于TreeMap/HashMap的膨胀3倍,内存节省就有9倍到60倍!那FST在性能方面真的能满足要求吗?

  下面是我在苹果笔记本(i7处理器)进行的简单测试,性能虽不如TreeMap和HashMap,但也算良好,能够满足大部分应用的需求。

                    

    参考博文:

      sbp810050504  《lucene (42)源代码学习之FST(Finite State Transducer)在SynonymFilter中的实现思想

      zhanlijun       《lucene字典实现原理

原文地址:https://www.cnblogs.com/yaokaizhi/p/9744200.html

时间: 2024-11-07 01:55:16

搜索引擎基础概念(2)—— 构建单词词典的相关文章

搜索引擎基础概念(1)——倒排索引

" 吾有三剑,唯子所择:皆不能杀人,且先言其状.一曰含光,视之不可见,运之不知有.其所触也,泯然无际,经物而物不觉.二曰承影,将旦昧爽之交,日夕昏明之际,北面而察之,淡淡焉若有物存,莫识其状.其所触也,窃窃然有声,经物而物不疾也.三曰宵练,方昼则见影而不见光,方夜见光而不见形.其触物也,騞然而过,随过随合,觉疾而不血刃焉.此三宝者,传之十三世矣,而无施于事.匣而藏之,未尝启封." -- <列子·汤问> 倒排索引(Inverted Index):倒排索引是搜索引擎数据存储的结

搜索引擎基础概念(3)—— 倒排列表

倒排列表 倒排列表用来记录有哪些文档包含了某个单词.一般在文档集合里会有很多文档包含某个单词,每个文档 会记录文档编号(DocID),单词在这个文档中出现的次数(TF)及单词在文档中哪些位置出现过等信息,这样与一个文档相关的信息被称做倒排索引项(Posting),包含这个单词的一 系列倒排索引项形成了列表结构,这就是某个单词对应的倒排列表.图 1-1 是倒排列表的示意图,在文档集合中出现过的所有单词及其对应的倒排列表组成了倒排索引. 图1-1 倒排列表示意图 在实际的搜索引擎系统中,并不存储倒排

伯克利教授Stuart Russell:人工智能基础概念与34个误区

伯克利教授Stuart Russell:人工智能基础概念与34个误区 机器之心 9 个月前 机器之心 Russell 是加州大学伯克利分校人工智能系统中心创始人兼计算机科学专业教授,同时还是人工智能领域里「标准教科书」<人工智能:一种现代方法>作者(谷歌研究主管 Peter Norvig 也是该书作者).在这篇文章中,他以 Q&A 的方式讲解了人工智能的未来以及常见的误解. 1. 什么是人工智能? 是对让计算机展现出智慧的方法的研究.计算机在获得正确方向后可以高效工作,在这里,正确的方

linux基础概念和个人笔记总结(1)

防伪码:青,取之于蓝而青于蓝:冰,水为之寒而寒于水 各位亲爱的朋友们,本次分为6次更新,共12章节,请大家务必温故而知新,重在消化理解,熟练掌握linux基础概念与命令 一.linux系统管理与维护 1.分区:/boot(100-200m) swap:(交换分区,建议是物理分区的1.5-2倍) /:剩余空间 root是管理员用户,区分于administrator 2.对初学者建议:关闭iptables a.chkconfig iptables off b.打开/etc/sysconfig/sel

分布式块设备drbd基础概念及、原理及其主从模式配置

一.drbd基础 1.drbd基础概念 drbd(全称为Distributed Replicated Block Device,简称drbd)分布式块设备复制,说白了就是在不同节点上两个相同大小的设备块级别之间的数据同步镜像.drbd是由内核模块和相关脚本而构成,用以构建高可用性的集群.在高可用(HA)解决方案中使用drbd的功能,可以代替使用一个共享盘阵存储设备.因为数据同时存在于本地主机和远程主机上,在遇到需要切换的时候,远程主机只需要使用它上面的那份备份数据,就可以继续提供服务了. 2.d

RabbitMQ基础概念详细介绍

你是否遇到过两个(多个)系统间需要通过定时任务来同步某些数据?你是否在为异构系统的不同进程间相互调用.通讯的问题而苦恼.挣扎?如果是,那么恭喜你,消息服务让你可以很轻松地解决这些问题. 消息服务擅长于解决多系统.异构系统间的数据交换(消息通知/通讯)问题,你也可以把它用于系统间服务的相互调用(RPC).本文将要介绍的RabbitMQ就是当前最主流的消息中间件之一. RabbitMQ简介 AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议

Java 技术体系基础概念

Java 技术体系基础概念 =============================================================================== 概述: =============================================================================== 编程语言: [百度百科解释] 编程语言俗称"计算机语言",种类非常的多,总的来说可以分成机器语言.汇编语言.高级语言三大类.电脑每做的

varnish基础概念详解

varnish基础概念详解 比起squid更加轻量级,大致有以下几个特点: ·可以基于内存缓存,也可以在磁盘上缓存,但是就算存放在磁盘上,也不能实现持久缓存 只要进程崩溃,此前缓存统统失效,无论是在内存还是在磁盘,但是现在已经具备持久缓存功能,但是仍然在实验阶段,经常容易崩溃,而且最大大小不能超过1G 如果期望内存大小超过几十个G,比如图片服务器,纯粹使用内存,性能未必好,这时候可以使用磁盘进行缓存,或SSD X 2 做RAID 避免磁盘损坏,在实现随机访问上 ssd硬盘要比机械硬盘要好的多,如

【龙书笔记】语法分析涉及的基础概念简介

本篇笔记是我对龙书第2.3-2.5节内容的理解,主要介绍编译器前端关于语法分析的众多基础概念.下篇笔记将根据本篇笔记的主要内容,实现一个针对简单表达式的后缀式语法翻译器Demo(原书中是java实例,我给出的将是逻辑一致的Python版本的实现). 1. 语法分析(Syntax Analysis) 简单来说,语法分析的任务是分析输入的符号字符串(string of symbols, 通常是词法分析产生的tokens)是否遵循某种语言在其上下文无关文法(context-free grammar)中