通过前面几篇文章的介绍,读者应该对知识图谱,其相关概念,以及语义网技术栈中的RDF,RDFS/OWL有了一定的了解。
者准备开一个实践篇,结合理论篇,让读者能够从无到有构建一个领域知识图谱,并在其上搭建一个基于知识图谱的问答小程序。demo比较简单,问答实现是基于模板匹配和正则表达式,整个流程是为了让读者对知识图谱及其相关应用有个直观的认识。
首先介绍我们使用的数据、数据来源和数据获取方法;其次,基于数据内部关系,介绍如何以自顶向下的方式构建本体结构。
一、数据准备
实践篇使用的数据是与电影相关的。基本统计数据如下:
-
演员数量:505人
-
电影数量:4518部
-
电影类型:19类
-
人物与电影的关系:14451
-
电影与类型的关系:7898
演员的基本信息包括:姓名、英文名、出生日期、死亡日期、出生地、个人简介。
电影的基本信息包括:电影名称、电影简介、电影评分、电影发行日期、电影类型。
数据是从“The Movie Database (TMDb”网站获取的,官方提供注册用户API KEY用于查询和下载数据。
本实例数据获取方法:以周星驰为初始入口,获取其出演的所有电影;再获取这些电影的所有参演演员;最后获取所有参演演员所出演的全部电影。经过去重处理,我们得到了505个演员的基本信息和4518部电影的基本信息。数据保存在mysql中,其ER图如下:
二、本体建模
本体的构建大体有两种方式:自顶向下和自底向上。
-
开放域知识图谱的本体构建通常用自底向上的方法,自动地从知识图谱中抽取概念、概念层次和概念之间的关系。这也很好理解,开放的世界太过复杂,用自顶向下的方法无法考虑周全,且随着世界变化,对应的概念还在增长。
-
领域知识图谱多采用自顶向下的方法来构建本体。一方面,相对于开放域知识图谱,领域知识图谱涉及的概念和范围都是固定或者可控的;另一方面,对于领域知识图谱,我们要求其满足较高的精度。现在大家接触到的一些语音助手背后对接的知识图谱大多都是领域知识图谱,比如音乐知识图谱、体育知识图谱、烹饪知识图谱等等。正因为是这些领域知识图谱来满足用户的大多数需求,更需要保证其精度。
本实例是一个电影领域的知识图谱,我们采用自顶向下的方法来构建本体结构。首先介绍下我们使用的工具protégé:
Protégé,又常常简单地拼写为“Protege”,是一个斯坦福大学开发的本体编辑和知识获取软件。开发语言采用Java,属于开放源码软件。由于其优秀的设计和众多的插件,Protégé已成为目前使用最广泛的本体论编辑器之一(来自维基百科)。
这样我们就定义好了本体
关系数据库到RDF
上一篇文章介绍了我们所使用的数据。
其实,知识图谱数据的来源主要有三个:结构化数据、半结构化数据和非结构化的数据。我们所使用的电影数据就是结构化的数据。半结构化的数据指的是数据有一定的组织形式,但较结构化数据而言更松散(属性名和属性值具有多样性,比如“生日”就有“出生日期”、“诞辰”等多种表达方式),例如百度百科、维基百科、互动百科等;非结构化数据通常是指我们看到的一切纯文本数据。
本文首先介绍W3C的RDB2RDF工作小组制定的两个标准,用于将关系型数据库的数据转换为RDF格式的数据。然后介绍如何利用d2rq这个工具把我们Mysql中的数据转为RDF。
一、两个标准
第一个标准是direct mapping,即直接映射。
规则十分简单:
-
数据库的表作为本体中的类(Class)。比如我们在mysql中保存的数据,一共有5张表。那么通过映射后,我们的本体就有5个类了,而不是我们自己定义的三个类。
-
表的列作为属性(Property)。
-
表的行作为实例/资源。
-
表的单元格值为字面量
-
如果单元格所在的列是外键,那么其值为IRI,或者说实体/资源。
RDB2RDF工作小组指定了另外一个标准——R2RML,可以让用户更灵活的编辑和设置映射规则。
R2RML也支持SQL语句来对查询结果进行映射。比如,我们有一列表示某人的性别,我们可以用SQL语句选取男性的行,把这些行映射成我们定义的男性类。女性同理。这种特性大大增强了其灵活性。
二、D2RQ
主要功能是将MySQL数据转换为RDF
以虚拟RDF图的方式访问关系数据库是其最主要的一个特性。它的机理就是通过mapping文件,把对RDF的查询等操作翻译成SQL语句,最终在RDB上实现对应操作。在做知识图谱项目的时候,我们可以灵活地选择数据访问方式。当对外提供服务,查询操作比较频繁的情况下,最好是将RDB的数据直接转为RDF,会节省很多SPARQL到SQL的转换时间。
D2RQ发布了r2rml-kit以支持W3C制定的两个映射标准。D2RQ有一个比较方便的地方,可以根据你的数据库自动生成预定义的mapping文件,用户可以在这个文件上修改,把数据映射到自己的本体上。就我们这个例子而言,数据关系比较简单,自己编辑R2RML文件或者在D2RQ生成的mapping文件上修改效率差不多。在数据关系很复杂的时候,我建议直接在D2RQ生成的mapping文件上修改,会节省很多时间。
使用步骤
- 下载D2RQ,进入其目录,运行下面的命令生成默认的mapping文件:
./generate-mapping -u root -p sdzh521 -o kg_demo_movie_mapping.ttl jdbc:mysql:///kg_demo_movie
root是mysql的用户名,没有密码则不输入,-o指定输出文件路径及名称,jdbc:mysql:///kg_demo_movie 指定我们要映射的数据库。该命令的其他参数及使用方式请参考文档。
- 修改mapping文件中对应的本体
@prefix : http://www.kgdemo.com# .
d2rq:class :Genre;
将其中的表名换为本体中的数据
- 使用下面的命令将我们的数据转为RDF:
./dump-rdf -f TURTLE -o dump.nt kg_demo_movie_mapping.ttl
RDF查询语言SPARQL
前面我们已经介绍过了语义网技术栈中的RDF,RDFS/OWL。这次我们介绍最后一个核心技术标准——SPARQL(RDF,OWL和SPARQL称为语义网的三大核心技术)。RDF本质上是一种数据模型,那么我们如何在RDF上进行查询呢?类似使用SQL查询关系数据库,我们使用SPARQL查询RDF格式的数据。本文先简单介绍一下SPARQL的历史,然后结合我们实践篇的数据举几个具体的例子。
一、SPARQL
SPARQL即SPARQL Protocol and RDF Query Language的递归缩写,专门用于访问和操作RDF数据,是语义网的核心技术之一。W3C的RDF数据存取小组(RDF Data Access Working Group, RDAWG)对其进行了标准化。相对第一个版本,其支持RDF图的更新,提供更强大的查询,比如:子查询、聚合操作(像我们常用的count)等等。
从SPARQL的全称我们可以知道,其由两个部分组成:协议和查询语言
- 查询语言很好理解,就像SQL用于查询关系数据库中的数据,XQuery用于查询XML数据,SPARQL用于查询RDF数据。
- 协议是指我们可以通过HTTP协议在客户端和SPARQL服务器(SPARQL endpoint)之间传输查询和结果,这也是和其他查询语言最大的区别。
一个SPARQL查询本质上是一个带有变量的RDF图,以我们之前提到的罗纳尔多RDF数据为例:
http://www.kg.com/person/1 http://www.kg.com/ontology/chineseName “罗纳尔多·路易斯·纳萨里奥·德·利马”^^string.
我们把属性值用变量代替(SPARQL中,用问号加变量名的方式来表示一个变量。),即:
http://www.kg.com/person/1 http://www.kg.com/ontology/chineseName ?x.
SPARQL查询是基于图匹配的思想。我们把上述的查询与RDF图进行匹配,找到符合该匹配模式的所有子图,最后得到变量的值。就上面这个例子而言,在RDF图中找到匹配的子图后,将"罗纳尔多·路易斯·纳萨里奥·德·利马"和“?x”绑定,我们就得到最后的结果。简而言之,SPARQL查询分为三个步骤:
-
构建查询图模式,表现形式就是带有变量的RDF。
-
匹配,匹配到符合指定图模式的子图。
-
绑定,将结果绑定到查询图模式对应的变量上。
二、例子
以实践篇的RDF电影数据为例,我们介绍如何利用SPARQL查询:
- 所有的RDF三元组。
- 周星驰出演了哪些电影?
- 英雄这部电影有哪些演员参演?
- 巩俐参演的评分大于7的电影有哪些
如何查询所有数据?参照我们在第一个部分介绍的查询过程,查询所有数据即我们没有任何已知值,SPO三元组每个都是未知变量。对应的SPARQL查询语言为:
PREFIX : <http://www.kgdemo.com#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <XML Schema>
PREFIX vocab: <http://localhost:2020/resource/vocab/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX map: <http://localhost:2020/resource/#>
PREFIX db: <http://localhost:2020/resource/>
SELECT * WHERE {
?s ?p ?o
}
SPARQL的部分关键词:
SELECT, 指定我们要查询的变量。在这里我们查询所有的变量,用*代替。
WHERE,指定我们要查询的图模式。含义上和SQL的WHERE没有区别。
FROM,指定查询的RDF数据集。我们这里只有一个图,因此省去了FROM关键词。
PREFIX,用于IRI的缩写。
“周星驰出演了哪些电影”:
PREFIX : <http://www.kgdemo.com#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <XML Schema>
PREFIX vocab: <http://localhost:2020/resource/vocab/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX map: <http://localhost:2020/resource/#>
PREFIX db: <http://localhost:2020/resource/>
SELECT ?n WHERE {
?s rdf:type :Person.
?s :personName '周星驰'.
?s :hasActedIn ?o.
?o :movieTitle ?n
}