一. 简介 基于浙江大学在openKG上提供的基于REfO的KBQA实现及示例 。代码部分浙大方面已经完成绝大部分,这里主要将其应用到自己的知识图谱上。在运行KBQA代码前,应按照前面的教程将电影类知识图谱导入到Jena的TDB数据库中,并运行fuseki服务器,这样我们才能进行访问查询。
本教程的项目代码放在github上,下载地址为《从零开始学习知识图谱》项目源代码 。
二. 环境准备 1. 操作系统 支持操作系统:windows、macOS、Linux。为了方便大家搭建开发环境,笔者尽可能在windows下构建,系列篇未特意说明时操作系统都是windows 。Linux安装可以参考VirtualBox虚拟机安装Ubuntu 或VirtualBox虚拟机安装CentOS8 进行安装。
2. jdk 安装参见windows系统安装JDK
3. Python3 安装参见从零开始学习知识图谱 之 一
三. 项目构建 1. 代码结构 代码结构为 .: data/ get_dict.sh query.py utils/ ./data: actorName.txt get_dict.txt movieName.txt ./utils: init.py init.pyc rules.py rules.pyc word_tagging.py word_tagging.pyc
其中data 目录存放由数据库倒出生成的字典文件,用于扩展jieba分词,由 get_dict.sh 生成。 utils/ 内存放查询预处理的模块。word_tagging.py 用于将词的文本和词性打包,视为词对象,对应:class:Word(token, pos)。rules.py 内定义各种规则并将自然语言转换为SPARQL查询语言,最终以JSON返回结果。 query.py 为程序入口,运行它来进行简单的KBQA。
2. 具体实现 基于REfO的简单知识问答的原理很简单,就是通过REfo提供的匹配能力,在输入的自然语言问题中进行匹配查找。如果找到我们预先设定的词或词性组合,那么就认为该问题与这个词或词性组合匹配。而一个词或词性的组合又对应着一个SPARQL查询模板,这样我们就借助REfO完成了自然语言到查询模板的转换。得到查询模板后,我们就利用Jena fuseki 服务器提供的端口进行查询得到返回的结果。
1). 模块一 word_tagging部分 该部分利用jieba分词对中文句子进行分词和词性标注。将词的文本和词性进行打包,视为词对象,对应 :class:Word(token, pos)。
1 2 3 4 5 6 7 8 9 10 11 class Tagger : def __init__ (self, dict_paths ): for p in dict_paths: jieba.load_userdict(p) def get_word_objects (self, sentence ): """ Get :class:WOrd(token, pos) """ return [Word(bytes .decode(word.encode('utf-8' )), tag) for word, tag in pseg.cut(sentence)]
2). 模块二 rules 部分 该部分为程序核心,负责将自然语言转换为SPARQL模板。
下面为rules的程序入口,customize_rules 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def customize_rules(): # some rules for matching # TODO: customize your own rules here person = (W(pos ="nr" ) | W(pos ="x" ) | W(pos ="nrt" ) | W(pos ="nz" )) movie = (W(pos ="nz" )) place = (W("出生地" ) | W("出生" )) intro = (W("简介" ) | W(pos ="介绍" )) rules = [ Rule(condition =W(pos="r") + W("是" ) + person | \ person + W("是" ) + W(pos ="r" ), action =who_is_question), Rule(condition =person + Star(Any(), greedy =False ) + place + Star(Any(), greedy =False ), action =where_is_from_question), Rule(condition =movie + Star(Any(), greedy =False ) + intro + Star(Any(), greedy =False ) , action =movie_intro_question) ] return rules
该函数中我们设置了一些简单的匹配规则,例如我们设置 ‘’’movie = (W(pos=”nz”))’’‘,即movie 的词性应该是nz。其中的W()是我们在继承REfO的Predicate方法的基础上扩展更新了match方法。您可以简单的把它理解为re中compile后的match,只不过多个W()间出现的顺序可以变化。这样通过多个定制的W()和Star(Any(), greedy=False)(相当于.*?)这种通配符的组合,我们就定义了一组匹配规则,当遇到符合该规则的句子时,就选取该规则后action对应的查询模板。
例如当输入为“周星驰是谁”这样的问题时,会匹配到rules 中的 第一条规则。而后执行该规则后对应的action, who_is_question。而who_is_question对应的查询模板为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def who_is_question (x ): select = u"?x0" sparql = None for w in x: if w.pos == "nr" or w.pos == "x" : e = u" ?a :actor_chName '{person}'. \n \ ?a :actor_bio ?x0" .format (person=w.token) sparql = SPARQL_TEM.format (preamble=SPARQL_PREAMBLE, select=select, expression=INDENT + e) break return sparql
有了查询模板后,我们通过SPARQLWrapper 模块的SPARQLWrapper 执行该查询,并对返回的结果进行转换得到回答。对应的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from SPARQLWrapper import SPARQLWrapper, JSON from utils.word_tagging import Tagger from utils.rules import customize_rules if __name__ == "__main__" : print ("init..........." ) sparql_base = SPARQLWrapper ("http://localhost:3030/kg_movie/query" ) tagger = Tagger (['data/actorName.txt' , 'data/movieName.txt' ] ) rules = customize_rules () print ("done \n" ) while True: print ("Please input your question: " ) default_question = input () seg_list = tagger.get_word_objects (default_question) for rule in rules: query = rule.apply (seg_list) if query: sparql_base.setQuery (query) sparql_base.setReturnFormat (JSON) results = sparql_base.query ().convert () if not results["results" ] ["bindings" ] : print ("No answer found :(" ) continue for result in results["results" ] ["bindings" ] : print ("Result: " , result["x0" ] ["value" ] )
3. 项目运行 运行,提问示例结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 C:\my\Python\python.exe C:/d/mmm/pycharm/patternREfO/query.py Building prefix dict from the default dictionary ... init........... Loading model from cache C:\Users\mmm\AppData\Local\Temp\jieba.cache Loading model cost 0.816 seconds. Prefix dict has been built succesfully. done Please input your question: 刘德华是哪个 Result: 刘德华(Andy Lau),1961 年9 月27 日出生于中国香港,中国香港男演员、歌手、作词人、制片人。1981 年出演电影处女作《彩云曲》 [1] 。1983 年主演的武侠剧《神雕侠侣》在香港获得62 点的收视纪录 [2-3] 。1991 年创办天幕电影公司 [4] 。1992 年,凭借传记片《五亿探长雷洛传》获得第11 届香港电影金像奖最佳男主角提名 [5] 。1994 年担任剧情片《天与地》的制片人 [6] 。2000 年凭借警匪片《暗战》获得第19 届香港电影金像奖最佳男主角奖 [7] 。2004 年凭借警匪片《无间道3 :终极无间》获得第41 届台湾金马奖最佳男主角奖 [8] 。2005 年获得香港UA院线颁发的全港最高累积票房香港男演员”奖 [9] 。2006 年获得釜山国际电影节亚洲最有贡献电影人奖 [10] 。2011 年主演剧情片《桃姐》,并凭借该片先后获得台湾金马奖最佳男主角奖、香港电影金像奖最佳男主角奖 [11] ;同年担任第49 届台湾电影金马奖评审团主席 [12] 。2017 年主演警匪动作片《拆弹专家》 [13] 。1985 年发行首张个人专辑《只知道此刻爱你》 [14] 。1990 年凭借专辑《可不可以》在歌坛获得关注 [15] 。1994 年获得十大劲歌金曲最受欢迎男歌星奖 [16] 。1995 年在央视春晚上演唱歌曲《忘情水》 [17] 。2000 年被《吉尼斯世界纪录大全》评为“获奖最多的香港男歌手” [18] 。2004 年第六次获得十大劲歌金曲最受欢迎男歌星奖。2016 年参与填词的歌曲《原谅我》正式发行 [19] 。1994 年创立刘德华慈善基金会 [20] 。2000 年被评为世界十大杰出青年 [21] 。2005 年发起亚洲新星导计划 [22] 。2008 年被委任为香港非官守太平绅士 [23] 。2016 年连任中国残疾人福利基金会副理事长。 [21] Please input your question: 刘德华出生地 Result: 香港新界大埔镇泰亨村 Please input your question: 七小福简介 Result: 《七小福》(Painted Faces)是由罗启锐导演,洪金宝、郑佩佩、林正英、岑建勋、午马等领衔主演的喜剧电影。影片讲述京剧大师于占元当年创办的“中国戏剧研究学院”,名字虽然取得唬人,但要论规模在香港众多戏校中也只算得中等,而且校舍破败,条件简陋,所幸师傅并未误人子弟,教得认真。“七小福“戏班最终名扬天下的故事。