搜索引擎核心技术与算法 —— 倒排索引初体验
今天开启一个新篇章——智能搜索与NLP。本篇章将由羸弱菜鸡小Q和大家一同学习与智能搜索相关的知识和技术,希望能和大家一同学习与进步,冲鸭!!
这里首先区分两个概念:搜索和检索
检索:数据库时代的概念,及将数据存入数据库,有需要的时候进行查取。对结果的要求绝对精确;比如我要在图书馆里找到所有出现“白马”字样的图书,这里用到的就是检索。
搜索:互联网时代的概念,人们将信息资源放在网上,第三方将互联网的信息搜罗起来,建立索引,所以搜索更多是指基于问题相关性的信息收集方式。当我想知道“如何骑白马最帅?”的时候,这里就不能只罗列出现过“白马”字样的图书了,毕竟骑白马的不一定是王子,还有可能是唐僧 (╯-_-)╯~╩╩。所以这时候就需要真正的搜索了。
闻道有先后,术业有专攻。那我们就先来讲一讲关于检索的那些事儿。
一、倒排索引の初体验
让我来先进入一个例子:我想要在《莎士比亚全集》中找到所有出现过“Brutus”和“Caesar”的章节,一个办法就是从头到尾阅读这本书,留意每一个出现“Brutus”和“Caesar”的地方。这种线性扫描就是一种最简单的计算机文档检索方式。那我现在想找出所有出现过“Calpurnia”章节,难道还是需要对整本书重新阅读一遍嘛?这样时间开销也太大了吧!!!为了解决这个问题,我们引入第一个核心概念——倒排索引。
一个典型的倒排索引分为两部分:词项词典和倒排记录表(如下图所示)
每一个词项对应一个倒排记录,一条倒排记录表示该词项所出现过的所有文档列表(如“Brutus”在第1、2、4、11...篇文档中出现过),文档列表的可以是无序的,但是升序更有利于我们之后的高效检索,下文会提到。
易得,建立一个倒排索引的时间复杂度是O(N),其中N是所有文档中单词的数量。
那么有了倒排索引后,我们如何完成上诉检索任务呢?
先以一个简单的查询为例:找到同时出现“Brutus”和“Calpurnia”两个单词的文档。
具体步骤如下:
(1)在词典中定位Brutus;
(2)返回其倒排记录表;
(3)在词典中定位Calpurnia;
(4)返回其倒排记录表;
(5)对两个倒排记录表求交集,如下图
图二 求交集示意图
在这里,交集(intersection)操作非常关键,这是因为我们必须快速将倒排记录表求交集以尽快找到哪些文档同时包括两个词项。该操作有时也称为合并(merge)。经常刷题的同学一眼就看出来,这不就是有序数组求交集嘛?leetcode原题实锤了嗷!(leetcode 1213 原题,easy难度)⊙﹏⊙|||
伪代码如下:
图三 merge伪代码
思路就是用两个指针p1、p2分别指向两个倒排记录(倒排记录是按升序排列的)的头部,如果两个指针指向的文档ID相同,则将该文档放入结果集中,并同时后移两个指针;如果不相同,则将指向文档ID较小的指针向后移动。
a = [1, 2, 3, 6, 9, 11, 45, 67]
b = [4, 6, 13, 45, 69, 98]
i = j = 0
result = []
while i < len(a) and j < len(b):
if a[i] == b[j]:
result.append(a[i])
i = i + 1
j = j + 1
elif a[i] < b[j]:
i = i + 1
else:
j = j + 1
print(result) #[6, 45]
假设两个倒排记录表的大小分别是x和y,那么上述求交集的过程需要 O(x+y)次操作。更正式的说法是,查询的时间复杂度为O(N)。和之前“再读一遍书”的扫描方法相比,同样是O(N)的复杂度,但是该方法的中的N是文档数,而前者的N是单词数,假设平均一篇文档有1000个单词,那么倒排索引的效率至少也提高了1000倍。
二、构建倒排索引
为获得检索速度的提升,就必须要事先建立索引。建立索引的主要步骤如下。
(1)收集需要建立索引的文档,如:
Doc1: Friends Romans countrymen.
Doc2: So let it be with Caesar …
(2)将每篇文档转换成一个个词条的列表,这个过程通常称为词条化(tokenization)或者分词,如:
Friends, Romans, countrymen, So, …
(3) 进行语言学预处理,产生归一化的词条来作为词项,如:
Friends, roman, countrymen ,So …
(4) 对所有文档按照其中出现的词项来建立倒排索引,索引中包括一部词典和一个全体倒排记录表。
建立索引最核心的步骤是将这个列表按照词项的字母顺序进行排序,其中一个词项在同一文档中的多次出现会合并在一起。词典中同样可以记录一些统计信息,比如出现某词项的文档的数目,即文档频率,这里就是指每个倒排记录表的长度。
在最终得到的倒排索引中,词项词典和倒排记录表都有存储开销。前者往往放在内存中,而后者由于规模大得多,通常放在磁盘上。因此,两部分的大小都非常重要。