【信息检索导论】第二章 词项词典与倒排记录表

总览

本章介绍的较为琐碎,因为书中提及了很多实际应用中的具体困难,而这些困难书中也并未提及解决方案,一般是凭经验去权衡。其内容主要还是围绕倒排表进行讲述的,讲述重点有两个。一是如何从文本中抽取词条,涉及到分词,以及词语的归一化问题(2.2), 二是如何高效的进行检索,即倒排表如何快速合并,考虑连续的二元组该如何查询等(2.3, 2.4)

2.1 文档分析与编码转换

编码转换:主要介绍文档的解码问题,用UTF-8还是其他编码,以及不同平台间的文档的解码问题。

索引粒度(index granularity): 或者称为文档单位, 定义为根据什么粒度去划分被检索的文档。 显然粒度太大的话,如将一本书看成一个文档,那么检索出来的信息中包含有很多无关信息,属于高recall, 低precision的情况。反之,如果粒度较小的话,比如一篇文章按照句子划分,那么与query相关的信息可能就被分散到多个单元中,会丢失一些相关信息,这种属于高precision,低recall的情况。

2.2 词项的确定

在这里插入图片描述
如上图所示,如果需要建立倒排索引表,则首先我们需要确定左边的词项词典,然后其右边的链表存储包含该词项的文档编号。这一小节介绍词项确定中的具体步骤及问题。

a. 分词

对于给定的语料库,我们首先对语料库中的每一句话进行词语拆分,在英文中可以根据空格拆分。而在对于中文,就需要有专业的分词算法,因为中文不会在词与词之间插空格。在这里我先枚举一下算法种类,详细的分词算法的介绍请见: link.
中文分词方法种类

b. 去除停用词

停用词:停用词是指在整个文档中出现频率非常高,但是对语义基本没有影响的词。在英文中如“the”,“a”,中文如“的”,“非常”等等。给定一个询问,这些词对找相关文档并没有太大帮助。我们将这些词收集起来建立一个停用词表,在表里面的词汇在建立词项表索引时会被忽略。一般来说,不把停用词建立索引不会被检索的内容有影响,因为停用词基本与内容无关,但是当情况变复杂之后,如索引词项中包含短语,“The apple” 和“apple”的意义就完全不一样了。

停用词表的大小: 因为停用词的衡量本身是一个主观的概念,大停用词表可能有200-300词,小停用词表大约7-12词。在web检索中,一般不会使用停用词表,因为相对于web文档与词项的数量而言,增加停用词表并不会造成很大的开销。

c. 词项归一化(token normalization)

等价类: 即不同的词项可能会包含相同或者相近的意思,可以粗略的理解成“同义词”,如用户需要检索“凤梨”,自然包含“菠萝”的文档也需要包含在检索答案中,在这里我们就需要把“凤梨”和“菠萝”看成是等价类。 当然,除了狭义上的同义词之外,等价类还包括其他类型的情况,如英语中的“pineapple”意思也是菠萝, 不同语言间表达同一个意思的词汇也应该被看做是等价类,等等。等价类中的所有词,都可以看成是同一个意思。

英文词汇的归一化:由于在英文中,同一个词涉及有时态,单复数以及词性,词形的变化,因为对英文还需要有额外归一化的操作,包括但不限于统一大小写词干还原词型归并等操作,在此不做详细介绍

2.3 结合跳表的倒排记录表合并

前言:在上一节中,我们合并两个词项对应的倒排表使用的算法是O(n)的,通过线性扫描两个有序链表的位置,可以做到在m+n次操作时完成两个链表的合并(m,n分别是两个链表的长度)。在实际应用中,我们还可以使用一些方式来优化这个过程。

跳表(skip list): 跳表是指在一个词项的记录表上,设置跳跃的链接,使之跳过不需要查询的文档编号。如下图所示:
在这里插入图片描述
上图为合并词项“brutus”以及"caesar"的倒排表,可以看到两个表中各自设有三条跳跃链接。结合按照上一节中的线性扫描法来介绍一下跳跃链接的作用。根据线性扫描法,两指针a(“brutus”),b(“caesar”)分别从两个链表的第一个位置开始扫描,然后比较当前两个指针所指的文档编号的大小,编号较小者的指针往后移动。假设现在a,b两指针都指向文档编号为8的位置,接下来b指针指向41,然后a指针指向16,可以看到16 < 41, 那么接下来a指针需要继续往后移动,而a指针文档编号为16的地方有一个跳跃链接,通过此链接后a指针会指向28,若此时28依旧比b指针指的文档编号(41)要小,那么a指针就可以利用这个跳跃链接直接跳到28号的位置(因为倒排表是有序的,28号之前的位置文档编号肯定小于28)。注意跳跃链接只有在使用该链接之后依旧小于另一指针的文档编号才可以使用。以上就是跳表的使用过程,具体细节如图2-10所示。

什么位置上需要放跳跃链接?:其实是随便放的, 不过显然跳跃链接越多,虽然指针移动过程中跳跃的机会会增加,但是也意味着跳跃的距离越短,并且还需要空间来存储跳跃指针。反之跳跃链接越少,则跳跃的机会会减少,但是一旦跳跃就可以跳跃更长的距离。 这里凭借经验主义,若链表的长度为p,则跳表的长度设置为 p \sqrt{p} p

跳表的局限性: 可以看到跳表是合并两个链表时候的一种优化,仅仅对于初始的倒排表,并且仅针对布尔检索的“AND”操作有效。此外,由于倒排表经常变动,每次变动都需要重新生成跳表,也是一笔不可忽略的开销。
在这里插入图片描述

2.4含位置信息的倒排记录表及短语查询

前言: 由于用户在查询的时候,绝大部分并非单个词语的查询,而是合并的短语查询,对于短语查询,因为短语中的词汇具有顺序关系,对于单个词项的倒排表,采用布尔检索的“AND”操作就不能完全符合用户的要求。

举个栗子:单词项所蕴含的问题是丢失了短语中包含的词序。如短语"苹果公司的总裁",分词后为[“苹果”,“公司”,“的”,“总裁”],可能某个新闻第一句是“公司的苹果卖不出去”,新闻的最后一句是“公司的老板和总裁急坏了”。如果不考虑顺序关系的话,那么这个文档就会被匹配。

本小节主要介绍两种方案来缓解短语查询的问题。两种解决方式分别为“二元索引”以及“位置信息索引”。

a. 二元词索引

其思路十分简单,如“苹果公司的总裁”,可以将其分成【“苹果公司”】,【“公司的”】,【“的总裁”】三个二元组,然后用这三个二元组建立的倒排表通过布尔检索的“AND”操作来获得包含以上三个二元组的文档集合。 这种方式相较于单词项的主要优势在于,其考虑了这句话中相邻两个词项的位置关系。 但是这种方式的处理中存在非名词词性的词语处理问题,比如上例中的“的”。上例可能分为【“苹果公司”】,【“公司总裁”】可能会减少更多噪声。
短语索引(phrase index):有二元词索引自然而然的可以引申出可以使用长度大于2的短语作为词项来进行索引。随着短语长度的增加,检索到的内容出错的可能性就越小,不过问题在于长度越长的短语,短语词汇就更多样,穷举长度大于2的短语几乎是一件不可能的事情。
总而言之,在计算机性能允许的情况下,我们可以尝试使用一部分二元甚至多元的短语索引(将高频出现的短语加入索引)。

b. 位置信息索引

上文所提到的二元词检索并不能完全建模词语间的顺序关系,并且随着短语索引的长度增加短语的数量呈指数增长。还有一种解决方式是尝试引入位置索引。如下图所示,在建立倒排表时,在某一词项对应的表中,不仅仅记录包含该词项的文档编号,还记录该词项在该文档中出现的位置。具体解释见图注。
在这里插入图片描述
拥有位置信息的倒排表,就可以进行一种叫做K词临近搜索的操作了,在合并两个词项的倒排表时,我们不仅仅寻找某一文档包含这两个词,并且还要额外限制这两个词在该文档中的位置不能超过K。即这两个词在该文档中处于空间临近的关系,这样就可以较为完整的解决用户的短语查询。

位置索引的局限性: 显而易见的,如果要存放位置的话,要额外增加许多空间,一个不完全的统计是 在大文件下会造成索引存储空间增加两个数量级 不过总的来说比二元词索引稍微好一些。

c. 混合索引机制

“只有小孩子才做选择,我全都要” ---- 鲁迅

前面介绍的两种策略显然各有各的优势,而且也是可以合并的。 显然对于那些用户查询次数非常高的短语,如“中国平安”,“三一重工”我们可以将其加到短语索引中, 并且合理限制短语索引的数量(这样就利用了短语索引的优势,也解决了短语索引的问题(短语数量过多))。而对于其他的单词项,我们就使用存储位置索引。 然而具体的结合方式得结合问题具体分析。

总结

本章主要介绍了:
a. 如何在语料库的基础上建立索引字典,包括分词,去停用词和归一化(等价类等)
b. 加入跳表,优化两个单词项倒排表的合并
c. 如何处理短语查询(短语索引, 位置索引)

本章思维导图

在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生成词项词典倒排记录表的具体实现步骤如下: 1. 首先,需要读入一个文本文件,并对文本进行分词处理。可以使用Python中的nltk库或jieba库进行分词。 2. 对于每个分词结果,需要对其进行词项归一化操作,比如将其转化为小写字母形式等。 3. 对于每个词项,需要记录其在文本中的出现次数,以及出现该词项的文档ID。 4. 根据文档ID,将每个词项加入到对应的倒排记录表中。 5. 最后,将所有词项及其对应的出现次数和倒排记录表保存到磁盘上,以便后续的信息检索操作使用。 下面是一个简单的Python代码示例,用于生成词项词典倒排记录表: ```python import jieba # 读取文本文件 with open('test.txt', 'r', encoding='utf-8') as f: text = f.read() # 分词 words = jieba.lcut(text) # 词项归一化 words = [word.lower() for word in words] # 生成词项词典倒排记录表 word_dict = {} for i, word in enumerate(words): if word not in word_dict: word_dict[word] = {'tf': 1, 'doc_ids': [i]} else: word_dict[word]['tf'] += 1 word_dict[word]['doc_ids'].append(i) # 保存词项词典倒排记录表到磁盘上 with open('word_dict.txt', 'w', encoding='utf-8') as f: for word, info in word_dict.items(): f.write('{}\t{}\t{}\n'.format(word, info['tf'], ','.join(map(str, info['doc_ids'])))) ``` 在上述代码中,我们使用了jieba库进行分词,并对每个词项进行了归一化操作。然后,我们遍历所有词项,根据其出现次数和文档ID生成了词项词典倒排记录表。最后,我们将这些信息保存到磁盘上。需要注意的是,我们在保存倒排记录表时使用了逗号分隔的文档ID列表,以便后续进行查询操作时能够方便地进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值