上篇文章《电影知识图谱问答(三)|Apache Jena知识存储及SPARQL知识检索》中讲到如何将处理后的RDF数据存储至Apache Jena数据库之中、如何利用SPARQL语句从Apache Jena之中进行知识检索和答案推理。本篇文章将主要介绍如何理解问句所表达的深层语义含义、如何将自然语言问句转换成SPARQL查询语句、如何进行答案推理。
上篇文章讲到利用SPARQL语句能够从Apache Jena数据库之中检索得到问题答案,那么如果想要构建电影知识图谱问答系统,亟需解决的问题就是如何将自然语言问句转换成SPARQL查询语句。比如问句“流浪地球的主演有哪些?”,转换成如下SPARQL查询语句需要经过哪些步骤呢?
PREFIX : <http://www.douban_kgqa.com#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX owl: <http://www.w3.org/2002/07/owl#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT ?x WHERE { ?s :movie_info_name '流浪地球'. ?s :has_actor ?a. ?a :movie_person_name ?x. } LIMIT 25
针对用户提问的自然语言问句,首先需要理解其中的深层次语义信息,即获取问句实体和目标属性信息。以问句“流浪地球的导演是谁?”为例,其问句实体是流浪地球、目标属性是导演,所采用的方法分别是实体识别和属性链接。
从问句中提取出实体可以采用以下两种方法:1)构建诸如BiLSTM-CRF(https://arxiv.org/pdf/1508.01991.pdf)等深度学习模型,然后利用训练好的深度学习模型预测出问句实体。2)构建实体词表,从问句中提取词表中所包含的实体。
第一种深度学习方法,能够预测得到训练数据中未出现过的电影名称,预测准确率保持在90%以上。缺点是需要构建训练数据,从头开始训练深度学习模型,耗费时间长;第二种词表方法,构建快捷方便,缺点是只能够发现词表中包含的电影实体名称,无法发现新的电影实体。
比较推荐的方法是词表+BiLSTM-CRF深度学习模型,但此处为了构建方便,只采用词表方法。词表构建方法是从爬取的数据之中,选出其中的电影和书籍名称、人物名称加入到词表之中。另外,有兴趣的朋友,可加上深度学习预测模型。BiLSTM-CRF模型在GitHub上有很多,可自主寻找。
属性链接可以采用以下两种方法:1)构建诸如CNN等多分类深度学习模型,然后利用训练好的深度学习模型预测问句的目标属性。2)构建关键词集合,把问句中所包含的关键词当作问句的目标属性。
同样,此处为了方便,直接采用关键词方法。有兴趣的朋友,可自主加上CNN等多分类预测模型。CNN等多分类模型GitHub上有很多,此处不再介绍。
需要注意的是,同一目标属性可以表达成多种含义。比如流浪地球的评分是多少?、也可以表达成流浪地球在豆瓣有多少分数?,那么此时我们就需要同时考虑评分和分数关键词。
rating = (W('评分') | W('分数')) # 评分
获取问句的实体和目标属性之后,便可根据规则模版将传统自然语言问句转换得到SPARQL查询语句,进而从Apache Jena数据库之中推理得到问题答案。构建规则模型可利用Python Refo库进行构建,比如构建某某电影的导演是谁?模糊匹配规则,方法如下所示。
movie_info_rules = [ Rule(condition_num=1,condition=(book_or_movie_entity + Star(Any(), greedy=False) + director + Star(Any(), greedy=False)) | (writer + Star(Any(), greedy=False) + book_or_movie_entity) + Star(Any(), greedy=False), action=QuestionSet.has_director) ]
模糊匹配规则中has_director为SPARQL检索语句转换函数,函数定义方法如下所示。
SPARQL_PREFIX = u"""PREFIX : <http://www.douban_kgqa.com#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX owl: <http://www.w3.org/2002/07/owl#> #某电影有哪些导演 """ SPARQL_SELECT_TEM = u"{prefix}\n" + \ u"SELECT DISTINCT {select} WHERE {{\n" + \ u"{expression}\n" + \ u"}}\n" def has_director(word_objects): """ 某电影有哪些导演 :param word_objects: :return: """ select = u"?x" sparql = None for w in word_objects: if w.pos == pos_book_or_movie: e = u"?m :movie_info_name '{movie}'.\n" \ u"?m :has_director ?a.\n" \ u"?a :movie_person_name ?x".format(movie=w.token) sparql = SPARQL_SELECT_TEM.format(prefix=SPARQL_PREFIX, select=select, expression=e) break return sparql
通过上述规则模版,能够处理以下类型的简单问句。
# 某电影的图片/上映地区/语言/上映时间/时长/其他名称/介绍/评分/ 评价人数 # 某电影的类型 # 某电影有哪些演员 # 某电影有哪些编剧 # 某电影有哪些导演 # 某电影的详细信息 # 某人的图片/性别/星座/生日/出生地/职业/其他名称/介绍/ # 某人演了哪些电影 # 某人写了哪些电影 # 某人指导了哪些电影 # 某人的详细信息
当然,也可以处理以下类型的复杂问句,但规则模版构建比较复杂。
# 某电影的评分是否大于8 # 哪些喜剧电影的评分小于4 # ... # 某人出演了多少部电影 # 某演员参演的评分大于X的电影有哪些 # 某演员出演过哪些类型的电影 # 演员A和演员B合作出演了哪些电影 # ...
将问句转换成SPARQL查询语句之后,便可从Apache Jena之中检索得到问句答案,查询代码如下所示。另外,为提高推理的准确率,还可以对《电影知识图谱问答(三)|Apache Jena知识存储及SPARQL知识检索》中所介绍的自定义推理规则进行补充。
# -*- coding:utf-8 -*- """ jena fuseki查询 """ from collections import OrderedDict from SPARQLWrapper import SPARQLWrapper, JSON class SparqlQuery: """ SPARQL 查询 """ def __init__(self, endpoint_url='http://localhost:3030/douban_kgqa/query'): """ 初始化链接 :param endpoint_url: """ self.sparql_conn = SPARQLWrapper(endpoint_url) def get_sparql_result(self, query): """ 根据查询条件,得到查询结果 :param query: :return: """ self.sparql_conn.setQuery(query) self.sparql_conn.setReturnFormat(JSON) return self.sparql_conn.query().convert() @staticmethod def parse_result(query_result): """ 解析返回的结果 :param query_result: :return: """ try: query_head = query_result['head']['vars'] query_results = [] for r in query_result['results']['bindings']: temp_dict = OrderedDict() for h in query_head: temp_dict[h] = r[h]['value'] query_results.append(temp_dict) return query_head, query_results except Exception as err: print('解析结果错误' + str(err)) def get_sparql_result_value(self, query_result): """ 列表存储结果值 :param query_result: :return: """ query_head, query_result = self.parse_result(query_result) if query_head is None: return query_result else: values = [] for qr in query_result: for _, value in qr.items(): values.append(value) return values
通过问句理解模块,能够得到问句的实体和目标属性信息。然后结合基于模版的答案推理方法,能够将问句转换成SPARQL查询语句,进而在Apache Jena数据库之中推理得到问题答案。但基于规则的答案推理仅能够处理已定义的规则,不能覆盖问句的所有情况。而我们又不能定义所有规则,这应该怎么处理呢?
这时,可以采用基于表示学习的答案推理方法,比如知识图谱嵌入中经典的Trans系列方法。这里我们以TransE(https://www.utc.fr/~bordesan/dokuwiki/_media/en/transe_nips13.pdf)为例进行解释,知识图谱中三元组向量化后可以表示为<h, r, t>,其中头实体为h、关系为r、尾实体为t。TransE假设实体和关系之间存在h+r ≈ t,即头实体h加上关系r的向量信息近似等于尾实体,那么我们便能够通过头实体和关系预测得到尾实体。也就是说,能够通过问句中实体和目标属性信息,预测得到问句答案。
此处对TransE原理内容不进行过多介绍,有兴趣的朋友可以去看论文。TransE训练代码可以从thunlp/OpenKE(https://github.com/thunlp/OpenKE)获取,训练数据可以从已爬取的豆瓣数据中抽取,训练完成后便可结合问句理解模块进行答案预测。
本篇文章介绍了如何理解问句深层次语义信息、如何将自然语言问句转换成SPARQL查询语句、如何利用TransE表示学习进行答案预测。至此,通过【一、二、三、四(本文)】几篇文章的介绍,我们已经了解如何从豆瓣官网中爬取数据;如何将爬取的数据转换得到可用的三元组数据,并存储至Apache Jena之中;如何利用SPARQL查询语言进行知识检索和答案推理;如何理解问句所表达的深层语义信息,即获取问句实体和目标属性信息;如何利用问句的深层语义信息,结合规则和表示学习方法,推理得到问题答案。结合上面几篇文章,已经能够从零开始构建一个电影知识图谱问答系统,有兴趣的朋友可以尝试构建。
下篇文章,将介绍如何将电影知识图谱问答系统部署至微信公众平台,并利用微信公众号进行知识问答,构建一个完整的知识图谱问答系统Demo。
本文分享自微信公众号 - 谓之小一(weizhixiaoyi),作者:谓之小一
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2019-08-11
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句