Information Retrieval 倒排索引 学习笔记

一,问题描述

在Shakespeare文集(有很多文档Document)中,寻找哪个文档包含了单词“Brutus”和"Caesar",且不包含"Calpurnia"。这其实是一个查询操作(Boolean Queries)。

在Unix中有个工具grep,它能线性扫描一篇文档,然后找出某个单词是否在该文档中。因此,寻找哪篇文档包含了“Brutus”和“Caesar”可以用grep来实现。但是:不包含“Calpurnia”如何实现呢?

有时,还有一些更加复杂的情况:比如寻找“Romans”附近出现“countrymen”的文档有哪些?附近 表示寻找的范围,比如在某篇文档中“Romans”和“countrymen”出现在同一段落中,那么这篇文档就是要找的文档;再比如:“Romans”前后10个词内出现“countrymen”,则这篇文档就是要找的文档。这种情况又如何处理?

再比如,寻找 包含单词“Brutus”和"Caesar"的文档,返回的结果有很多篇文档,哪篇文档最相关呢?(Rank retrieval)。这些复杂的情况都无法用 grep 工具来实现,而是使用了一些特殊的数据结构(文档表示方式)。比如 Term-document incidence matrices 和 倒排索引(Inverted index)

二,Term-document incidence matrices

介绍一个新概念:Term

在处理文档时,经常以单词(word)作为分析处理单元(units),但有一些"专有名词",又不是传统意义上的单词,比如"Hong Kong"或者一些一连串的数字。因此,在IR中,用term来表示"index units"。看到这里,就明白tf-idf(term frequency–inverse document frequency) 中的 term 是什么意思了。

Terms are the indexed units,they are usually words, and for the moment you can think of them as words, 
but the information retrieval literature normally speaks of terms because some of them,
such as perhaps I-9 or Hong Kong are not usually thought of as words.

 

回到文章开头提出的那个问题:哪个文档包含了单词“Brutus”和"Caesar",且不包含"Calpurnia"?,更专业地:

哪个文档包含了 term  "Brutus" 和 term "Caesar",且不包含term "Calpurnia"?

首先将文档"拆分"成一个个的 term 来表示,若某个term在这篇文档中出现 就用1标识;未出现则用0标识

如上图所示:每一列代表一篇文档是否包含了某个term,比如 文档 Julius Caesar 就包含了"Antony"、"Brutus"、"Caesar"、"Calpurnia",但是不包含"Cleopatra"、"mercy"、"worser"。

每一行表示 某个term 出现在哪几篇文档中。比如 term "Antony"出现在第一篇文档、第二篇文档(Julius Caesar)和最后一篇文档中。

有了这个矩阵,就可以回答上面那个问题了。Brutus 在文档中出现情况是 110100;Caesar在文档中出现情况是 110111 ;Calpurnia 出现情况是 101111,将它们进行 与操作:

110100 AND 110111 AND 101111 = 100100

得出:包含了单词“Brutus”和"Caesar",且不包含"Calpurnia"的文档是第一篇文档Antony and Cleopatra 和 第四篇文档 Hamlet。而且对于计算机而言,进行与操作是很快的。

从上面可看出,通过Term-document incidence matrices这种文档的表示形式,将 grep 线性查找操作,变成了 位与 操作。

但是,这种表示方式也存在着问题:这个矩阵会很稀疏。比如中文汉字有几万个(term 很多),一篇新闻文档不会用到所有的中文汉字,因此矩阵中大部分元素为0。而要存储一个很大的稀疏矩阵,对内存就造成了浪费。而倒排索引就可以解决这个问题。

 

三,inverted index

倒排索引就是:如果某个term在文档中出现了,才记录它。若不出现,则不记录。

A much better representation is to record only the things that do occur, that is, the 1 positions.

We keep a dictionary of terms Then for each term, we have a list that records which documents the term occurs in.
Each item in the list – which records that a term appeared in a document– is conventionally called a posting

 

假设有很多文档,如何构建倒排索引呢?首先是从文档中选取出 term,也就是对文档进行分词,得到一个个的 term。比如有N篇文档如下:

文档一: Friends, Romans, countrymen.
文档二: So let it be with Caesar
....
....
文档N:

对文档文档分词(tokenize),得到一系列的tokens:Friends、Romans、countrymen、So……

有时还需要对分词的结果进行预处理(linguistic preprocessing),这种预处理操作一般是:删除一些 stop words,进行 Stemming 操作 和 lemmatization操作。stemming操作 是从词形上对单词归一化,比如说:复数cats 变成  cat。而 lemmatization 是寻找词根,比如:are, is, am 都归一化成 be

 

Stemming usually refers to a crude heuristic process that chops off the ends of words in the hope of 
achieving this goal correctly most of the time,
and often includes the removal of derivational affixes

 

Lemmatization usually refers to doing things properly with the use of a vocabulary and morphological analysis 
of words normally aiming to remove inflectional endings only and to return the base or dictionary form of a word,
which is known as the lemma。

 

预处理之后,得到一个个的 可索引的 term 了。倒排索引如下图所示:

"Brutus"就是一个term,它关联着一个链表(list),这个链表称之为posting,链表中的每个元素代表文档的标识,它表示: term  "Brutus"出现在 文档1,文档2,文档4,文档11,文档31……文档174中

若干个 term 组合起来就是一个 dictionary,所有的posting的集合就是 postings

从上可看出,使用倒排索引表示时:每个文档都有一个唯一的文档标识(docID),而且链表是有序的。并且上面的倒排索引只关注:某个term是文档中 是否 出现过,并不知道出现了多少次

现在如何根据倒排索引找出:哪个文档包含了单词“Brutus”和"Caesar",且不包含"Calpurnia"?这其实就是 "Brutus" 指向的链表 和 "Caesar"指向的链表 求并 操作(intersection)---两个有序的链表找公共元素。算法的伪代码如下:

INTERSECT(p1, p2)answer ← <>
while p1 != NIL and p2 != NILdo if docID(p1) = docID(p2)then ADD(answer, docID(p1))p1 ← next(p1)p2 ← next(p2)
else if docID(p1) < docID(p2)then p1 ← next(p1)else p2 ← next(p2)return answer

时间复杂度为:O(N),N就是 文档的总个数。对于两个有序链表 求并 操作,时间复杂度会不会小于O(N)呢?那也是有可能的,那就是在链表的某些元素上,存储一个"skip pointer"指针,如下图所示:

举个例子:假设目前已经找到了两个链表中的第一个公共元素8,现在要找下一个公共元素。链表1移动到下一个位置指向16,链表2移动到下一个位置指向41。由于元素16存储了一个skip pointer,该skip pointer指向28,由于链表是有序的而且28小于41,因此链表1可以直接跳过19、23这两个元素,直接移动到28这个元素上(从而不需要将 19和23 这两个元素与 链表2中的41比较)。算法伪代码如下:

INTERSECTWITHSKIPS(p1, p2)
1 answer ← <>
2 while p1 != NIL and p2 != NIL
3 do if docID(p1) = docID(p2)
4     then ADD(answer, docID(p1))
5     p1 ← next(p1)
6     p2 ← next(p2)
7 else if docID(p1) < docID(p2)
8  then if hasSkip(p1) and (docID(skip(p1)) ≤ docID(p2))
9     then while hasSkip(p1) and (docID(skip(p1)) ≤ docID(p2))
10         do p1 ← skip(p1)
11    else p1 ← next(p1)
12 else if hasSkip(p2) and (docID(skip(p2)) ≤ docID(p1))
13    then while hasSkip(p2) and (docID(skip(p2)) ≤ docID(p1))
14        do p2 ← skip(p2)
15     else p2 ← next(p2)
16 return answer

 

引入skip pointers到底是好还是坏呢?这个不一而足。说几个需要考虑的因素:

①引入skip pointer 需要额外的存储空间。②移动到某个元素上时,需要判断该元素是否存储了 skip pointers。③在哪些元素上存储 skip pointer比较好? skip pointers 跳过多少个元素比较好?……

 

额外的一点 补充,上面讲到:每个 term 的posting list 长度是未知的。要找某两个term的公共元素,其实就里线性遍历这两个term对应的posting list。因此,这个过程的时间复杂度是O(M+N) 。这里的M是第一个term对应的posting list的长度,N是第二个term对应的posting list的长度。那如果我要找多个term的posting list中的公共元素呢?

比如说:寻找 term : Brutus 、Calpurnia、Caesar这三个term,都在哪些文档中出现了?

这是一个与操作。如果知道 posting list的长度,先将长度比较短的term的posting list进行与操作,这样能提高查询的效率。比如说从上面图中可看出:Calpurnia 的posting list的长度为4,先执行 Calpurnia & Brutus 得出的结果的长度也不会超过4,然后再去Caesar对应的posting list中查询。效率要好。

即:执行顺序为Calpurnia & Brutus & Caesar 比 Caesar & Brutus & Calpurnia 要好。

 

四,参考资料:

《An Introduction to Information Retrieval》第一章和第二章

 

原文:http://www.cnblogs.com/hapjin/p/8214254.html

转载于:https://www.cnblogs.com/hapjin/p/8214254.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281433.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机地址栏搜索不了网,我的电脑地址栏不见了怎么办 地址栏不见了如何解决...

导语&#xff1a;小编对电脑是比较痴迷的&#xff0c;因此喜欢在自己的电脑上进行各种操作&#xff0c;也经常会碰到一些问题。今天要为大家介绍的是在我的电脑地址栏不见了之后怎么办&#xff0c;熟悉电脑的朋友都能够了解。在我的电脑主界面里面&#xff0c;有一个地址栏&…

实践App内存优化:如何有序地做内存分析与优化

由于项目里之前线上版本出现过一定比例的OOM,虽然比例并不大&#xff0c;但是还是暴露了一定的问题&#xff0c;所以打算对我们App分为几个步骤进行内存分析和优化&#xff0c;当然内存的优化是个长期的过程&#xff0c;不是一两个版本的事&#xff0c;每个版本都需要收集线上内…

WinForm(十四)窗体滚动日志

在桌面程序里&#xff0c;一般日志记录到文件里就可以了&#xff0c;但有的时间&#xff0c;也需要在窗体上动态滚动显示&#xff0c;这时&#xff0c;就需要引入日志框架了。这里引入的依旧是NLog&#xff08;在我的Mini API系统里&#xff0c;用的也是NLog&#xff09;。首先…

xp计算机找不到音量调节,WinXP电脑没声音且小喇叭不见了如何解决?

有用户在使用电脑听音乐的时候&#xff0c;突然发现电脑没有声音了&#xff0c;本来以为只是被禁了音&#xff0c;想着调节音量即可解决问题。但是当他想要点开音量小喇叭的时候&#xff0c;发现桌面任务栏通知区域的小喇叭不见了&#xff0c;这该怎么办呢&#xff1f;下面小编…

2018-2019-1 20165211 实验四 外设驱动程序设计

2018-2019-1 20165211 实验四 外设驱动程序设计 任务一 1.实验要求 学习资源中全课中的“hqyj.嵌入式Linux应用程序开发标准教程.pdf”中的第十一章 提交康奈尔笔记的照片&#xff08;可以多张&#xff09; 2. 任务完成 任务二 1. 实验要求 在Ubuntu完成资源中全课中的“hqyj.嵌…

《ASP.NET Core 6框架揭秘》实例演示[31]:路由高阶用法

ASP.NET的路由是通过EndpointRoutingMiddleware和EndpointMiddleware这两个中间件协作完成的&#xff0c;它们在ASP.NET平台上具有举足轻重的地位&#xff0c;MVC和gRPC框架&#xff0c;Dapr的Actor和发布订阅编程模式都建立在路由系统之上。Minimal API更是将提升到了前所未有…

java中文乱码解决之道(五)—–java是如何编码解码的

编码&解码 1&#xff1a;I/O操作 2&#xff1a;内存 3&#xff1a;数据库 4&#xff1a;javaWeb 下面主要介绍前面两种场景&#xff0c;数据库部分只要设置正确编码格式就不会有什么问题&#xff0c;javaWeb场景过多需要了解URL、get、POST的编码&#xff0c;servlet的解码…

win10系统按esc会弹出计算机,win10系统版本2004控制面板多出ESC是什么原因?

如果我们的电脑在升级了win102004控制面板多出ESC什么情况方法一&#xff1a;“干净启动”&#xff0c;排除第三方软体的影响1.停止非核心的程序运作(包括第三方杀毒、优化软体)2.情况允许的话&#xff0c;卸载设备中的第三方杀毒、管家、优化软件3.同时按【4.点击【服务】>…

CentOS6/7 配置守护进程

CentOS6.xCentOS6中转用Upstrat代替以前的init.d/rcX.d的线性启动方式。一、相关命令通过initctl help可以查看相关命令[rootlocalhost ~]# initctl help Job commands:start Start job.stop Stop job.restart …

Java并发(二十一):线程池实现原理

一、总览 线程池类ThreadPoolExecutor的相关类需要先了解&#xff1a; &#xff08;图片来自&#xff1a;https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8%A7%88&#xff09; Executor&#xff1a;位于最顶层&#xff0c;只有一个 execute(Runnable runnable) 方法&a…

进程池

转自&#xff1a;https://www.cnblogs.com/kaituorensheng/p/4465768.html 在利用Python进行系统管理的时候&#xff0c;特别是同时操作多个文件目录&#xff0c;或者远程控制多台主机&#xff0c;并行操作可以节约大量的时间。当被操作对象数目不大时&#xff0c;可以直接利用…

gulp版本号管理插件注意事项

2019独角兽企业重金招聘Python工程师标准>>> 打开node_modules\gulp-rev\index.js 第144行 manifest[originalFile] revisionedFile; 更新为: manifest[originalFile] originalFile ?v file.revHash; 打开node_modules\rev-path\index.js 第10行 return filena…

bigfile.to服务器位置,Cloudera Manager 迁移服务器

Cloudera Manager还是比较耗资源的&#xff0c;想把Cloudera Manager&#xff0c;移动到比较好的机器上。在这篇文章中&#xff0c;Cloudera Manager安装在bigserver1上面&#xff0c;bigserver1是奔腾双核的CPU。1&#xff0c;Cloudera Manager占资源比较多cloudera manager占…

公司新来了一位阿里P9,在全员大会上讲荤段子!还是上个世纪的老段子,太烂了!...

阿里P9在坊间的名声一向不好&#xff0c;这几年在业界出了不少令人无语的新闻&#xff0c;今天又来了一个&#xff1a;公司新来了一位阿里P9伪高管&#xff0c;全员大会上来先讲了一个荤段子&#xff0c;这个破段子还是上个世纪的&#xff0c;太烂了&#xff01;关于这个段子&a…

【转】博客美化(1)基本后台设置与样式设置

阅读目录 1.博客园后台设置2.自定义样式的设置博客园美化相关文章目录&#xff1a;博客园博客美化相关文章目录 一直都拜膜那些博客园的皮肤设计高手&#xff0c;由于本人对前端研究甚少&#xff0c;所以js,css这种东西只能看得懂最基本的&#xff0c;会简单改改。然后一直对自…

Airdoc创始人:工智能可以在医疗领域多个环节发挥作用 但有局限性

7月1日&#xff0c;在由武汉国家生物产业基地建设管理办公室主办、火石创造承办、光谷健康智慧园协办的医疗大数据与医学人工智能高峰论坛上&#xff0c;Airdoc创始人兼董事长张大磊做了题为《AI在医疗领域中应用的问题与局限》的演讲。 Airdoc是医疗领域人工智能领军企业&…

我的世界服务器抽奖系统怎么弄,我的世界自动识别货币抽奖机如何制作

我的世界是一款很经典的沙盒类游戏&#xff0c;在游戏中红石和命令方块是这部作品的核心&#xff0c;可以制作很多装备和道具&#xff0c;下面给大家分享下我的世界自动识别货币抽奖机如何制作&#xff0c;希望对大家有所帮助。自动识别货币抽奖机制作方法废话不多说,(貌似一句…

Java并发编程中volatile实现过程详细解析

2019独角兽企业重金招聘Python工程师标准>>> 首先并发编程有三大特性&#xff1a; 可见性&#xff0c;有序性&#xff0c;原子性。volatile关键字实现了前面两个特性。那么它是如何实现这两个特性的呢&#xff1f; 首先是可见性。可见性主要是让缓存&#xff0c;直接…

《ASP.NET Core 6框架揭秘》实例演示[32]:错误页面的N种呈现方式

由于ASP.NET是一个同时处理多个请求的Web应用框架&#xff0c;所以在处理某个请求过程中出现异常并不会导致整个应用的中止。出于安全方面的考量&#xff0c;为了避免敏感信息外泄&#xff0c;客户端在默认情况下并不会得到详细的出错信息&#xff0c;这无疑会在开发过程中增加…

Golang并发模型:合理退出并发协程

goroutine作为Golang并发的核心&#xff0c;我们不仅要关注它们的创建和管理&#xff0c;当然还要关注如何合理的退出这些协程&#xff0c;不&#xff08;合理&#xff09;退出不然可能会造成阻塞、panic、程序行为异常、数据结果不正确等问题。这篇文章介绍&#xff0c;如何合…