搜索引擎:常用信息检索方式介绍与倒排索引实现(Python)

一.信息检索方式

(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
 

猜你喜欢

转载自blog.csdn.net/yt266666/article/details/127405088