倒排索引
一、倒排索引
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每
一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定
属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。
带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
建立索引是聊天机器人的语料库搜索核心技术之一,目的是加快响应用户的输
入。使用了搜索引擎技术中最常用的倒排索引技术,它是“单词”到“文档”的
一个映射。由于问答系统中的查询都是输入一段自然语言文本进行搜索,经过中
文分词都转化为一系列关键词。利用倒排索引,可以通过关键词找到包含它们的
文档集合,然后将其中的每一个文档与查询进行相似度匹配,从而返回与用户查
询最相关的答案。
现在我们先了解下倒排索引建立的大概流程图:
词语 ID、文档 ID 等
分配 ID、分词
加入
包括
生成
排序
文档源
词语
词典
临时文件
词语有序列表
按各文档
ID 排序
倒序索引列表
得 到 各 子
排序
有序文档
合并各个文档
的词语集
具有相同语义的词语集表
词语集合表
词语
语义
分类
倒序索引流程示意图
由流程图可知道建立索引大概分为5大步:
1)文档的分析。首先为每一篇文档分配唯一的ID,然后对文档进行分词(主要
是去除停用词和抽取词干),接着把得到的每个新词存放到词典当中,如果词典
中已经存在该词语,则更新相对应的数据信息。当一篇文档被分析完之后会产生
一个临时文件,该临时文件时用来存放词语ID、文档ID和相对出现的位置等信
息。
2)排序。将得到的临时文件按词语ID进行排序,得到一个初步有序列表。
3)词语合并。一篇文档是有很多词语所组成的,而每一个汉字或词语都有许多
的近义词,就是词语意思一样,但表达的方式不一样。所以,这一步就是要把一
篇文档中的近义词或同义词进行合并,得到一个词类的有序列表。
4)文档合并归类。由于整个知识库和语料库中不止一篇文档,而是由很多文档
组成,所以要把这些文档中的相同词语的删除和同义词的进一步归类,从而形成
一个总的有序类表的一个临时文件。
5)倒排文件。读取整个文档集的临时文件中各个词语的倒序类表,再根据需要
采取必要的压缩算法,写入到倒排文件中从而得到了一个倒排序表。
二、代码结合解说
该代码是用 java 实现的,所以首先要调用的就是 java 的一个类方法 java.util.*,为代码
实现了 List、get() 的接口和 iterator()的返回调用集合的迭代器等功能。并且应用了哈希表进
行信息的存储(在实现前运用到 tree map 进一步将获得的信息进行排序,但由于选用了 hash
table ,所以就应用了 hash map 但顺序却不一定能保证!)
程序哈希表的总结构图:
总表
分表 ID
分表 1
分表 2
..............
分表 1 属性
词语
词语 1
词语 2
...............
分表 2 属性
..........
ID
位置
次数.......
该程序的结构图可知道,该程序分为两种哈希表,现有分表统计个文档词语的信息,然后在
到总表中汇报,整理成一个倒排列表(也就是说表中表结构)
1.元素组成:
public Hashtable> h_invDoc;
建立一个总的哈希表 h_invDoc,用来存放词语和问题之间的权重关系,已达到以文档依赖
词语的关系。
public Hashtable termList;
该为总表中的一个分表,用来存放类文档中的相近意义词语的有续集的一个序表
termIdea:词语 url:问题 times:出现的次数 ID::权重 tf:文档篇数 occur:文档中
出现该词的次数
2.创建倒排索引并更新的函数方法详解
1.)InvDoc.java----------主要负责是分表的词语合并排序与更新
public boolean m_iInsertTerm(String termId, String url){
if(h_invDoc.containsKey(termId)){
if( ! isNewTerm(termId, url)){
................................
.................................
}else {
..............................................;
}
}else {
........................................
if(h_invDoc.get(termId).containsKey(url))
.............................
该方法主要是各分表的一个汇报以及合并以实现总表的倒排序表的创建与更新该过程分为
2 步:
1.)h_invDoc.containsKey(termId。把分割好的词语,并在哈希表中查找词语是否已经存在。
如果已经存在的就提取其信息。反则,就在该总的哈希表中创建新一项,并把该词的信息更
新记录在表中。再把该词与问题建立权重关系。
2.)isNewTerm。该函数主要是判断该词在问题中是否存在权重关系,也就是说该词在问题
中的成分比重,如果是有一定的代表该问题的性质就返回一个 False,然后 if 中就成 true,并
获得该词在哈希表中的信息(权重),并相对加 1,更新记录列表。否则在该表中创建新的
一项,赋予初值并赋予总表的更新信息。
2.)Dict.java-----------主要是负责总表的词语排序与更新
public int insertTerm(String termText, boolean isInNewDoc){
.............
if(h_dict.containsKey(termText)){
......................................
if(isInNewDoc){
......................
}else{
......................
.....................................
}else {
.....................................
}
...................................
}
private boolean isInNewDoc(String termText, String url) {
// TODO Auto-generated method stub
......................
}
}
该方法主要是记录每一篇文档中各个词语的权重、出现的位置、出现的次数、频率等信息。
该方法总体分为 2 步:(一下为每一步的具体功能以及实现)
1.)h_dict.containsKey(termText)。提取分表中的词语进行相对应的在总的哈希表中查找,看
是否已经存在该词语,如果存在,则提取该词相对应信息;否则,就创新一张新的哈希表记
录该词的信息,并把它加入到总的哈希表中进行更新。
2.)isInNewDoc。再通过分表传递的一个参数 isInNewDoc 的值来定义该总表中的词语是否
找到相应的词语,如果找到,则提取该词语的信息(提取的是总表中该词语的信息,不是分
表中的信息)并且对应更新数据;否则,在总表中创建新的一张表记录该词语的信息并合并
到总表中进行更新。
三、总的流程图
语 料 库 中 的
问题
分词
问词
提取下一个问词
设置权重为 1
是否已经在分
表中存在?
否
建立倒排表
表中创建新项
是
是否存在权重?
否
权重置 1
是
权重加 1
建立倒排表
四、源代码
1.)Dict .java
package org.aitools.programd.thulib.model;
import java.util.Hashtable;
/**
* @author lji
*
*/
class DictNode {
public String text;
public int termId;
public int df;
public int occur;
}
public class Dict {
public Hashtable h_dict;
private int Id;
public Dict(){
h_dict = new Hashtable();
Id = 0;
}
public int insertTerm(String termText, boolean isInNewDoc){
DictNode dictNode;
if(h_dict.containsKey(termText)){
dictNode = h_dict.get(termText);
if(isInNewDoc){
dictNode.df++;
dictNode.occur++;
}else{
dictNode.occur++;
}
h_dict.put(termText, dictNode);
}else {
dictNode = new DictNode();
dictNode.text = termText;
dictNode.termId = Id;
dictNode.occur = 1;
dictNode.df = 1;
h_dict.put(termText, dictNode);
Id++;
}
return dictNode.termId;
}
private boolean isInNewDoc(String termText, String url) {
// TODO Auto-generated method stub
return false;
}
}
2.) InvDoc.java
class DocsNode{
public int max;
public int length;
public Hashtable termList;
}
public class InvDoc {
public int maxLength;
public Hashtable> h_invDoc;
public InvDoc(){
h_invDoc = new Hashtable>();
maxLength = 0;/
}
public boolean m_iInsertTerm(String termId, String url){
Double times = 0.0;
if(h_invDoc.containsKey(termId)){
if(!isNewTerm(termId, url)){
times = h_invDoc.get(termId).get(url);
times++;
h_invDoc.get(termId).put(url, times)
}else {
times = 1.0;
h_invDoc.get(termId).put(url, times);
}
}else
{
Hashtable m_hDocList = new Hashtable();
m_hDocList.put(url, 1.0);
times = 1.0;
h_invDoc.put(termId, m_hDocList);
}
return times==1.0;
}
public boolean isNewTerm(String termId, String url){
if(h_invDoc.get(termId).containsKey(url))
{
return false;
}
return true;
}
}