文章目录
- 一.信息检索方式
- (1)线性扫描
- (2)词项—文档关联矩阵
- (3)倒排索引
- 二.倒排索引实现及常用语料处理方式
- (1) 实现目标
- (2) 完整代码
- (3) 运行结果
一.信息检索方式
(1)线性扫描
计算机对于文档内容检索有多种可能的方式,如直接从头遍历至尾端,根据我们输入的关键词提取内容。
这类检索方式与我们人类阅读的习惯相同,因此实现简单且很容易被接受。
若问你《三国演义》中是否存在’舌战群儒’这一词语,我们常常会选择浏览全文从中找出匹配的词语。
而从《三国演义》中提取出关键词 , 通过现代计算机不会花费太长时间;
但假如目标是世界文学合集呢?企业一年的财务报告呢?又或者是现代信息世界产生的规模更大的文档集。
尽管计算机算力强大,线性扫描的信息检索方式也仅仅只能够用于处理小文本。
(2)词项—文档关联矩阵
于是产生了词项—文档关联矩阵,我们利用空闲的算力,在计算机内部提前构建矩阵。
三国演义 | 红楼梦 | 水浒传 | 西游记 | 悟空传 | |
孙悟空 | 0 | 0 | 0 | 1 | 1 |
贾宝玉 | 0 | 1 | 0 | 0 | 0 |
当我们输入关键词孙悟空时,将《西游记》《悟空传》两个文档作为结果返回。
这样的方式大大提高了检索速度
词项是索引的单位 而文档则是返回的结果。
从行来看,能够得到对应词项的文档向量;从列来看,则获取文档的词项向量。
当然这类方式也存在较大的不足 如上述我所举例的表格 能够看见矩阵中0值占比极高
在造成负担的同时 也极大地减缓了检索速度。
(3)倒排索引
对数据结构有一定涉猎的同学应该很快就能想到优化方式——稀疏数组。
我们建立词项词典与倒排记录表 从而完成对文档的高效检索。
词项词典 | 倒排记录表 |
孙悟空 | 4—>5 |
贾宝玉 | 2 |
倒排索引的两个部分。词典部分往往放在内存中,而指针指向的每个倒排记录表则往往存放在磁盘上。
二.倒排索引实现及常用语料处理方式
(1) 实现目标
读取文本文件 对不同的单行文档 进行词条化及归一化,
关注标点符号的去重 停用词的筛选以及大小写的转化。
建立倒排索引/词项字典。
(2) 完整代码
import re
import string
from stop_words import get_stop_words
from nltk.stem.porter import PorterStemmer
# 列表去重
def unique(word_list):
return list(dict.fromkeys(word_list))
# 移除停用词
def stop_word(token):
en_stop = get_stop_words('en')
stopped_tokens = [i for i in token if i not in en_stop]
return stopped_tokens
# 词干提取
def stem_extracting(stopped_tokens):
p_stemmer = PorterStemmer()
texts = [p_stemmer.stem(i) for i in stopped_tokens]
return texts
# Porter stemmer 并不是要把单词变为规范的那种原来的样子,
# 它只是把很多基于这个单词的变种变为某一种形式!换句话说,
# 它不能保证还原到单词的原本,也就是"created"不一定能还原到"create",
# 但却可以使"create" 和 "created" ,都得到"creat" !
def incidence_matrix(text, docID):
# 目的:
# 传入一段文本及其文档号
# 获取其词项列表
# 1.清除英文标点符号
punctuation_string = string.punctuation
lines = re.sub('[{}]'.format(punctuation_string), " ", text)
# 2.将英文文本内容切片
lsplit = lines.split()
# 3.大小写转换
for num in range(len(lsplit)):
lsplit[num] = lsplit[num].lower()
# 4.移除停用词 词干提取
# lsplit = stem_extracting(stop_word(lsplit))
# 5.去重并转字典
lsplit_dic = dict.fromkeys(lsplit)
# 6.记录文档号
for word in lsplit_dic.keys():
lsplit_dic[word] = [docID]
return lsplit_dic
def read_file(filename):
result = {}
count = 0
with open(filename, 'r') as file_to_read:
# 以只读形式打开该文件
while True:
# 以行为单位进行读取
lines = file_to_read.readline()
# 当某行内容为空时 停止读取
if len(lines) == 0:
break
count = count + 1
lsplot = incidence_matrix(lines, count)
result = dic_zip(result, lsplot)
# 关闭文件读取
file_to_read.close()
return result
def dic_sort(a_dic):
b_dic = dict.fromkeys(sorted(a_dic))
for word in b_dic.keys():
b_dic[word] = a_dic[word]
return b_dic
# 不同文档字典 同一词项合并
def dic_zip(a_dic, b_dic):
# 将b_dic并入a_dic中
for word in b_dic.keys():
if a_dic.get(word, None):
a_dic[word].extend(b_dic[word])
else:
a_dic[word] = b_dic[word]
return a_dic
def show_dic(a_dic):
# 文档频率可用于做查询优化
tplt = "{0:^10}\t{1:{3}^10}\t{2:^40}"
print(tplt.format("词项", "文档频率", "倒排记录", chr(12288)))
for word in a_dic.keys():
print(tplt.format(word, len(a_dic[word]), str(a_dic[word]), chr(12288)))
def main():
# 读取filename下的英文文本文件 将每一行作为单独的文本
# 建立倒排索引。
filename = './Reverse_Index_Word2.txt'
matrix = dic_sort(read_file(filename=filename))
show_dic(matrix)
if __name__ == '__main__':
main()
(3) 运行结果
可以读取更大规模的文档,此处选取小文本是为了便于展示。
归一化的处理被我注释掉了 需要时可以删除#符号。
#读入的文档
new home sales top forecasts
home sales rise in july
increase in home sales in july
july new home sales rise
# 运行结果
"D:\Program Files\Python\python.exe"
词项 文档频率 倒排记录
forecasts 1 [1]
home 4 [1, 2, 3, 4]
in 2 [2, 3]
increase 1 [3]
july 3 [2, 3, 4]
new 2 [1, 4]
rise 2 [2, 4]
sales 4 [1, 2, 3, 4]
top 1 [1]
Process finished with exit code 0