简介
gAnswer系统的主要思想,是将自然语言问题转化为语义查询图,再和RDF图做子图匹配。在转换成查询图的第一步就是确定查询图的节点,即节点提取(Node Extraction, NE)。
查询图中的节点由实体(entity)、类型(type)和通配符(wild-cards)构成,因此节点提取的主要工作就是提取问题中的实体和类型的提及(mention),以及给出在知识库中对应的候选。
具体而言,节点提取模块分为离线和在线两部分。离线部分主要是建立实体提及词典(Entity Mention Dictionary),从而将实体提及映射到一定置信度的候选实体上;在线部分首先利用文本处理工具获取问题中的单词和它们的词性标注,再枚举所有词组,通过在字典中查询判断该词组是否为实体或类型。在gAnswer中采用CrossWikis dictionary作为实体提及词典,通过第三方工具standfordNLP来获取单词,以及用开源程序库Lucene实现在字典中查询。
gAnswer中调用NE模块流程
gAnswer中调用NE模块部分如下图所示,在创建的Query类对象中,首先对自然语言问题进行预处理,将部分单词改成等价的其他单词,并去除标点符号,从而减少后续步骤中语法依存分析的错误率;再调用getMergedQuestionList方法返回修改后的问题,并在mWordList对象中保存提取的节点信息。
// step 0: Node (entity & type & literal) Recognition
long t0 = System.currentTimeMillis(), t, NRtime;
Query query = new Query(input);
qlog = new QueryLogger(query);
ArrayList<Sparql> rankedSparqls = new ArrayList<Sparql>();
NRtime = (int)(System.currentTimeMillis()-t0);
System.out.println("step0 [Node Recognition] : "+ NRtime +"ms");
GAnswer.java中调用NE部分
public Query(String _question)
{NLQuestion = _question;NLQuestion = removeQueryId(NLQuestion);TransferedQuestion = getTransferedQuestion(NLQuestion); // step1. NODE RecognitionMergedQuestionList = getMergedQuestionList(TransferedQuestion);// build SentencesList = new ArrayList<Sentence>();for(String mergedQuestion: MergedQuestionList){Sentence sentence = new Sentence(this, mergedQuestion);sList.add(sentence);}
}
Query类的构造函数
getMergedQuestionList方法会新建一个EntityRecognition类的对象,通过process方法实现节点提取。首先通过standfordNLP获取问题中的单词和它们的词性标注,再三重循环对所有词组进行枚举:首先判断是否为category(category是针对DBpedia2016数据集手动筛选出的专有名词),如果不是会继续判断该词组是否为entity或type。
在判断entity时,会分别对该词组的基本形式(baseForm)以及在文中出现的原始形式(originalForm)用lucene和DBpedia Lookup在预处理的词典中进行查找,并返回候选实体及其分数,存放在emList中。
Lucene是由Apache软件基金会支持和提供的一套用于全文检索的开源程序库,提供了简单却强大的接口,能够在较理想的时间内完成对全文的索引和搜寻。在离线部分用lucene对DBpedia2016建立索引后,在线部分可直接通过调用新建lucene.search.IndexSearcher类完成对一个词组的搜索。
DBpedia Lookup是一个基于DBpedia的用于检索RDF数据的实体检索服务,通过配置RDF数据的索引,可将待查询的关键字解析为实体标识符。gAnswer首先通过离线生成的实体提及词典查找每个词组对应的提及,再利用远程的DBpedia Lookup服务进行实体链接。
// Search entity
ArrayList<EntityMapping> emList = new ArrayList<EntityMapping>();
if(!entOmit && !stopEntList.contains(baseWord))
{System.out.println("Ent Check: "+originalWord);checkEntCnt++;// Notice, the second parameter is whether use DBpedia Lookup.emList = getEntityIDsAndNamesByStr(originalWord, (UpperWordCnt>=len-1 || len==1),len);if(emList == null || emList.size() == 0){emList = getEntityIDsAndNamesByStr(baseWord, (UpperWordCnt>=len-1 || len==1), len);}if(emList!=null && emList.size()>10){ArrayList<EntityMapping> tmpList = new ArrayList<EntityMapping>();for(int i=0;i<10;i++){tmpList.add(emList.get(i));}emList = tmpList;}
}
判断entity
在判断type时,会分别将该词组的originalForm和baseForm利用lucene在DBpedia2016中查找,并根据得分排序候选答案;若没找到,再在YAGOtype中搜索。上述结果存放在tmList中。
// Search type
int hitMethod = 0; // 1=dbo(baseWord), 2=dbo(originalWord), 3=yago|extend()
ArrayList<TypeMapping> tmList = new ArrayList<TypeMapping>();
if(!typeOmit)
{System.out.println("Type Check: "+originalWord);//checkTypeCnt++;//search standard type tmList = tr.getTypeIDsAndNamesByStr(baseWord);if(tmList == null || tmList.size() == 0){tmList = tr.getTypeIDsAndNamesByStr(originalWord);if(tmList != null && tmList.size()>0)hitMethod = 2;}elsehitMethod = 1;//Search extend type (YAGO type)if(tmList == null || tmList.size() == 0){tmList = tr.getExtendTypeByStr(allUpperWord);if(tmList != null && tmList.size() > 0){preLog += "++++ Extend Type detect: "+baseWord+": "+" prefferd relaiton:"+tmList.get(0).prefferdRelation+"\n";hitMethod = 3;}}
}
判断type
之后gAnswer会将字符串类型的词组名称、是否为category, entity, type, literal这四个布尔变量,以及emList, tmList全部封装在mWordList中,完成第一步筛选。之后会剔除重复的词组并对候选词组按分数排序,得到处理后的mWordLIst,用于后续问题分析、建立查询图等步骤。