关于自然语言处理:从零开始构建一个电影知识图谱实现KBQA智能问答上篇本体建模

5次阅读

共计 10832 个字符,预计需要花费 28 分钟才能阅读完成。

从零开始构建一个电影常识图谱,实现 KBQA 智能问答 [上篇]:本体建模、RDF、D2RQ、SPARQL endpoint 与两种交互方式具体教学

成果展现:

首先介绍咱们应用的数据、数据起源和数据获取办法;其次,基于数据外部关系,介绍如何以自顶向下的形式构建本体构造。

1. 数据筹备

实际篇应用的数据是与电影相干的。根本统计数据如下:

  1. 演员数量:505 人
  2. 电影数量:4518 部
  3. 电影类型:19 类
  4. 人物与电影的关系:14451
  5. 电影与类型的关系:7898

演员的根本信息包含:姓名、英文名、出生日期、死亡日期、出生地、个人简介。

电影的根本信息包含:电影名称、电影简介、电影评分、电影发行日期、电影类型。

数据是从“The Movie Database (TMDb”网站获取的,官网提供注册用户 API KEY 用于查问和下载数据。我原本打算从豆瓣获取电影数据,但当初豆瓣 API 曾经敞开了个人用户申请入口。

本实例数据获取办法:以周星驰为初始入口,获取其出演的所有电影;再获取这些电影的所有参演演员;最初获取所有参演演员所出演的全副电影。通过去重解决,咱们失去了 505 个演员的根本信息和 4518 部电影的根本信息。数据保留在 mysql 中,其 ER 图如下:

读者能够间接下载咱们获取到的数据,或者用咱们提供的脚本本人从网站获取额定的数据,再或者依据本人的须要从新编写脚本。

2. 本体建模

本体的构建大体有两种形式:自顶向下和自底向上。

  1. 凋谢域常识图谱的本体构建通常用自底向上的办法,主动地从常识图谱中抽取概念、概念档次和概念之间的关系。这也很好了解,凋谢的世界太过简单,用自顶向下的办法无奈思考周全,且随着世界变动,对应的概念还在增长。
  2. 畛域常识图谱多采纳自顶向下的办法来构建本体。一方面,绝对于凋谢域常识图谱,畛域常识图谱波及的概念和范畴都是固定或者可控的;另一方面,对于畛域常识图谱,咱们要求其满足较高的精度。当初大家接触到的一些语音助手背地对接的常识图谱大多都是畛域常识图谱,比方音乐常识图谱、体育常识图谱、烹饪常识图谱等等。正因为是这些畛域常识图谱来满足用户的大多数需要,更须要保障其精度。

本实例是一个电影畛域的常识图谱,咱们采纳自顶向下的办法来构建本体构造。首先介绍下咱们应用的工具 protégé(点击进入官网下载):

Protégé,又经常简略地拼写为“Protege”,是一个斯坦福大学开发的本体编辑和常识获取软件。开发语言采纳 Java,属于开放源码软件。因为其优良的设计和泛滥的插件,Protégé 已成为目前应用最宽泛的本体论编辑器之一(来自维基百科)。

关上 protege,看到和下图相似的界面。在 Ontology IRI 中填写咱们新建本体资源的 IRI。读者能够填写本人的符合标准的 IRI。

点击“Entities”tab 标签,抉择“Classes”标签。在这个界面,咱们创立电影常识图谱的类 / 概念。留神,所有的类都是“Thing”的子类。最右边红色小方框中的按钮用于创立以后选中类的子类,两头的按钮用于创立兄弟类(平行类),最左边的按钮删除以后选中的类。咱们创立了三个类,“人物”、“电影”、“类别”。右下方的界面是用于形容该类的一些个性,例如:”disjoint of” 是用于示意该类与哪些类是互斥的。本例中,三个类都是互斥的。也就是说,一个实例只能是三个类中的一个。咱们没有在 protege 中显式地定义互斥关系,读者能够本人定义。

接下来咱们切换到 “Object Properties” 页面,咱们在此界面创立类之间的关系,即,对象属性。这里咱们创立了三个对象属性,”hasActedIn” 示意某人参演了某电影,因而咱们在右下方的 3 号矩形框中定义该属性的 “domain” 是人,4 号框定义 “range” 是电影。这个很好了解,”domain” 示意该属性是属于哪个类的,”range” 示意该属性的取值范畴。2 号框示意该属性的逆属性是 “hasActor”,即,有了推理机,只管咱们的 RDF 数据只保留了 A 出演了 B,咱们在查问的时候也能失去 B 的演员有 A。1 号方框中是一些形容该属性的词汇,咱们在上一篇文章中曾经介绍过,这里不再赘述。同理,咱们定义另外两个属性,这里不再展现。

最初,咱们切换到 “Data properties”,咱们在该界面创立类的属性,即,数据属性。其定义方法和对象属性相似,除了没有这么丰盛的形容属性个性的词汇。其实不难理解,这些形容个性的词汇是传递、对称、拥护称、自反等,表明其必然有指向其余资源或本身的边,而咱们之前提到过,数据属性相当于树的叶子节点,只有入度,而没有出度。

其实辨别数据属性和对象属性还有一个很直观的办法,咱们察看其 “range”,取值范畴即可。对象属性的取值范畴是类,而数据属性的取值范畴则是字面量,如下图。

protege 也反对以可视化的形式来展现本体构造。咱们点击 “Window” 选项,在 “Tabs” 中抉择 “OntoGraf”,而后 “Entities” 旁边就多了一个标签页。在右侧窗口中挪动元素,能够很直观地察看本体之间的关系。

在这个大节,咱们简略地介绍了如何用 protege 自顶向下地构建常识图谱的本体构造。对于 Protege 更具体的操作和介绍,请参考这篇文档。

3. 关系数据库到 RDF

本文首先介绍 W3C 的 RDB2RDF 工作小组制订的两个规范,用于将关系型数据库的数据转换为 RDF 格局的数据。而后介绍如何利用 d2rq 这个工具把咱们 Mysql 中的数据转为 RDF。

3.1 两个规范

第一个规范是 direct mapping,即间接映射。何为间接映射?。

之所以说 RDFS/OWL 是 RDF 的“衣服”,因为它们都是用来形容 RDF 数据的。为了不显得这么形象,咱们能够用关系数据库中的概念进行类比。用过 Mysql 的读者应该晓得,其 database 也被称作 schema。这个 schema 和咱们这里提到的 schema language 非常相似。咱们能够认为数据库中的每一张表都是一个类(Class),表中的每一行都是该类的一个实例或者对象(学过 java 等面向对象的编程语言的读者很容易了解)。表中的每一列就是这个类所蕴含的属性。如果咱们是在数据库中来示意人和地点这两个类别,那么为他们别离建一张表就行了;再用另外一张表来示意人和地点之间的关系。RDFS/OWL 实质上是一些预约义词汇(vocabulary)形成的汇合,用于对 RDF 进行相似的类定义及其属性的定义。

Notice: RDFS/OWL 序列化形式和 RDF 没什么不同,其实在表现形式上,它们就是 RDF。其罕用的形式次要是 RDF/XML,Turtle。另外,通常咱们用小写结尾的单词或词组来示意属性,大写结尾的示意类。数据属性(data property,实体和 literal 字面量的关系)通常由名词组成,而对象数据(object property,实体和实体之间的关系)通常由动词(has,is 之类的)加名词组成。剩下的局部合乎驼峰命名法。为了将它们示意得更分明,防止读者混同,之后咱们都会默认这种命名形式。读者实际过程中命名形式没有强制要求,但最好保持一致。

规定非常简略:

  1. 数据库的表作为本体中的类(Class)。比方咱们在 mysql 中保留的数据,一共有 5 张表。那么通过映射后,咱们的本体就有 5 个类了,而不是咱们本人定义的三个类。
  2. 表的列作为属性(Property)。
  3. 表的行作为实例 / 资源。
  4. 表的单元格值为字面量
  5. 如果单元格所在的列是外键,那么其值为 IRI,或者说实体 / 资源。

在理论利用中咱们很少用到这种办法,只管它是最便捷的形式。具体的解释和示例,请参考 W3C 的官网文档 (A Direct Mapping of Relational Data to RDF)。

Direct mapping 的毛病很显著,不能把数据库的数据映射到咱们本人定义的本体上。RDB2RDF 工作小组指定了另外一个规范——R2RML,能够让用户更灵便的编辑和设置映射规定。

我不打算在这里具体地解说 R2RML 的具体语法和规定,读者能够本人参考 W3C 的文档 (R2RML: RDB to RDF Mapping Language)。其实能够把它当做一个工具,用的时候再查文档即可,不必把所有的个性和语法都记下来,只须要晓得它是什么,能干什么即可。为了让读者有个直观地意识,咱们以 mysql 中的数据为例,介绍怎么把 person 这个表映射到咱们在 protege 中定义的 Person 类上,person_name 映射到 personName 上。

@prefix rr: <R2RML: RDB to RDF Mapping Language Schema>.
@prefix : <http://www.kgdemo.com#>.

<#TriplesMap1>
    rr:logicalTable [rr:tableName "person"];
    rr:subjectMap [rr:template "http://www.kgdemo.com/person/{person_id}";
        rr:class :Person;
    ];
    rr:predicateObjectMap [
        rr:predicate :personName;
        rr:objectMap [rr:column "person_name"];
    ].

rr:template 指定实体 / 资源的 IRI 生成模板,括号中的字符串是对应表中的某个列名。在本例中指每个人物的 IRI 由咱们预约义的前缀加人物 ID 组成。rr:Class 申明这些实体 / 资源的类是咱们在 Ontology 中定义的 Person。rr:predicate 指定谓语,即属性。rr:objectMap 指定该属性的值是来源于哪一列。其余属性的定义相似,读者能够本人查文档尝试。对于外键的定义,读者也能够参考文档相干示例。

R2RML 也反对 SQL 语句来对查问后果进行映射。比方,咱们有一列示意某人的性别,咱们能够用 SQL 语句选取男性的行,把这些行映射成咱们定义的男性类。女性同理。这种个性大大加强了其灵活性。

上面咱们介绍如何用 d2rq 这个工具把 mysql 的数据转为 RDF。

3.2 D2RQ

D2RQ 的官网介绍是:

Accessing Relational Databases as Virtual RDF Graphs

没错,以虚构 RDF 图的形式拜访关系数据库是其最次要的一个个性。它的机理就是通过 mapping 文件,把对 RDF 的查问等操作翻译成 SQL 语句,最终在 RDB 上实现对应操作。在做常识图谱我的项目的时候,咱们能够灵便地抉择数据拜访形式。当对外提供服务,查问操作比拟频繁的状况下,最好是将 RDB 的数据间接转为 RDF,会节俭很多 SPARQL 到 SQL 的转换工夫。

D2RQ 提供了本人的 mapping language,其模式和 R2RML 相似。D2RQ 公布了 r2rml-kit 以反对 W3C 制订的两个映射规范。D2RQ 有一个比拟不便的中央,能够依据你的数据库主动生成预约义的 mapping 文件,用户能够在这个文件上批改,把数据映射到本人的本体上。就咱们这个例子而言,数据关系比较简单,本人编辑 R2RML 文件或者在 D2RQ 生成的 mapping 文件上批改效率差不多。在数据关系很简单的时候,我倡议间接在 D2RQ 生成的 mapping 文件上批改,会节俭很多工夫。D2RQ 的 mapping language 也很简洁,同样反对对 SQL 后果进行映射,其 SQL 是用 condition 关键词隐式地表白,不像 R2RML 是显式的 SQL 语句。更多的细节请参考官网文档。

下载 D2RQ,进入其目录,运行上面的命令生成默认的 mapping 文件:

generate-mapping -u root -o kg_demo_movie_mapping.ttl jdbc:mysql:///kg_demo_movie

root 是 mysql 的用户名,没有明码则不输出,-o 指定输入文件门路及名称,jdbc:mysql:///kg_demo_movie 指定咱们要映射的数据库。该命令的其余参数及应用形式请参考文档。

依据咱们的 mysql 数据库生成的默认 mapping 文件:

# 局部展现
@prefix map: <#> .
@prefix db: <> .
@prefix vocab: <vocab/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <XML Schema> .
@prefix d2rq: <http://www.wiwiss.fu-berlin.de/suhl/bizer/D2RQ/0.1#> .
@prefix jdbc: <http://d2rq.org/terms/jdbc/> .

map:database a d2rq:Database;
    d2rq:jdbcDriver "com.mysql.jdbc.Driver";
    d2rq:jdbcDSN "jdbc:mysql:///kg_demo_movie";
    d2rq:username "root";
    jdbc:autoReconnect "true";
    jdbc:zeroDateTimeBehavior "convertToNull";
    .

上面是依据咱们定义的本体批改的 mapping 文件。首先,为了表白简练,咱们给本体的 IRI 设置一个前缀。这样

http://www.kgdemo.com#Person

就能够表白为

:Person

其余的词汇同理。

接下来,把默认的映射词汇改为咱们本体中的词汇即可。在解决外键的时候要留神以后编辑的属性的 domain 和 range,belongsToClassMap 是 domain,refersToClassMap 是 range。

# 局部展现
@prefix map: <#> .
@prefix db: <> .
@prefix vocab: <vocab/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <XML Schema> .
@prefix d2rq: <http://www.wiwiss.fu-berlin.de/suhl/bizer/D2RQ/0.1#> .
@prefix jdbc: <http://d2rq.org/terms/jdbc/> .
@prefix : <http://www.kgdemo.com#> .

map:database a d2rq:Database;
    d2rq:jdbcDriver "com.mysql.jdbc.Driver";
    d2rq:jdbcDSN "jdbc:mysql:///kg_demo_movie";
    d2rq:username "root";
    jdbc:autoReconnect "true";
    jdbc:zeroDateTimeBehavior "convertToNull";
    .

#Table genre
map:genre a d2rq:ClassMap;
    d2rq:dataStorage map:database;
    d2rq:uriPattern "genre/@@genre.genre_id@@";
    d2rq:class :Genre;
    d2rq:classDefinitionLabel "genre";
    .
map:genre_genre_name a d2rq:PropertyBridge;
    d2rq:belongsToClassMap map:genre;
    d2rq:property :genreName;
    d2rq:propertyDefinitionLabel "genre genre_name";
    d2rq:column "genre.genre_name";
    .

语法规定比较简单,具体的操作不再赘述,读者能够参考相干文档 (The D2RQ Mapping Language)。

D2RQ 反对的数据库有 Oracle、MySQL、PostgreSQL、SQL Server、HSQLDB、Interbase/Firebird。也反对其余某些数据库,但可能会有限度。请参考数据库兼容性阐明 (Accessing Relational Databases as Virtual RDF Graphs)。

应用上面的命令将咱们的数据转为 RDF:

.\dump-rdf.bat -o kg_demo_movie.nt .\kg_demo_movie_mapping.ttl

kg_demo_movie_mapping.ttl 是咱们批改后的 mapping 文件。其反对导出的 RDF 格局有“TURTLE”,“RDF/XML”,“RDF/XML-ABBREV”,“N3”, 和“N-TRIPLE”。“N-TRIPLE”是默认的输入格局。

kg_demo_movie.nt 中对于演员的片段:

<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/163441> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/13> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/240171> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/24> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1336> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/79> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1337> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/79> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1338> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/79> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1339> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/79> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1340> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/79> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1341> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/79> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/643> <http://www.kgdemo.com#hasActedIn> <file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/82> .

kg_demo_movie.nt 中对于电影的片段:

<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/54321> <http://www.kgdemo.com#movieRating> "7.0E0"^^<XML Schema> .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/54321> <http://www.kgdemo.com#movieIntroduction> "" .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/54321> <http://www.kgdemo.com#movieTitle> "Mang quan gui shou" .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/54321> <http://www.kgdemo.com#movieReleaseDate> "1979-07-13" .
<file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/54321> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.kgdemo.com#Movie> .

4.D2RQ SPARQL endpoint 与两种交互方式

这次咱们介绍利用 D2RQ 开启 SPARQL endpoint 服务和两种交互方式:在浏览器中进行查问或者编写 python 脚本进行交互。跳过之前实际篇练习的读者,须要做的筹备有:导入数据到 Mysql,下载 mapping 文件

4.1 SPARQL endpoint

前一篇介绍 SPARQL 的文章中提到,SPARQL endpoint 是 SPARQL 协定的一部分,用于解决客户端的申请,能够类比 web server 提供用户浏览网页的服务。通过 endpoint,咱们能够把数据公布在网上,供用户查问。

D2RQ,是以虚构 RDF 的形式来拜访关系数据库中的数据,即咱们不须要显式地把数据转为 RDF 模式。通过默认,或者本人定义的 mapping 文件,咱们能够用查问 RDF 数据的形式来查问关系数据库中的数据。换个说法,D2RQ 把 SPARQL 查问,依照 mapping 文件,翻译成 SQL 语句实现最终的查问,而后把后果返回给用户。上面是 D2R Server 的架构图:

进入 d2rq 目录,应用上面的命令启动 D2R Server:

d2r-server.bat kg_demo_movie_mapping.ttl

“kg_demo_movie_mapping.ttl”是咱们定义的 mapping 文件。其余参数和配置请参考官网文档。默认端口是 2020,在浏览器输出“http://localhost:2020/”,能够看到如下界面:

红色方框 1 是咱们定义的类别,点击某个类别,咱们能够看到其对应的所有实例(默认显示 50 个,能够在 mapping 文件中批改服务器配置)。选中某个实例,能够看到其蕴含的所有属性,如下图:

点击红色方框 2 中的链接,进入 endpoint,如下图:

4.2 浏览器中查问

输入框默认的 SPARQL 查问是获取所有的 RDF 三元组,“LIMIT”关键词指定返回后果数量的下限。点击下图红框中的“Go!”,执行查问:

读者能够自行尝试上篇文章中的例子:

“周星驰出演了哪些电影?”

“英雄这部电影有哪些演员参演?”

“巩俐参演的评分大于 7 的电影有哪些?”

读者也能够通过命令行的形式进行查问,具体方法请参考官网的文档。

4.3 编写 Python 脚本进行交互

构建基于常识图谱的利用,咱们心愿将 SPARQL 查问集成在代码当中,对其进行包装便于后续开发。这里介绍一个 Python 第三方库:SPARQLWrapper。如其名,这是一个 Python 下的包装器,能够让咱们非常不便地和 endpoint 进行交互。上面是通过 SPARQLWrapper,向 D2RQ endpoint 发送查问“巩俐参演的评分大于 7 的电影有哪些”,失去后果的代码。

from SPARQLWrapper import SPARQLWrapper, JSON

sparql = SPARQLWrapper("http://localhost:2020/sparql")
sparql.setQuery("""
    PREFIX : <http://www.kgdemo.com#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

    SELECT ?n WHERE {
      ?s rdf:type :Person.
      ?s :personName '巩俐'.
      ?s :hasActedIn ?o.
      ?o :movieTitle ?n.
      ?o :movieRating ?r.
    FILTER (?r >= 7)
    }
""")
sparql.setReturnFormat(JSON)
results = sparql.query().convert()

for result in results["results"]["bindings"]:
    print(result["n"]["value"])

运行后果:

2046
Memoirs of a Geisha
荆轲刺秦王
大红灯笼高高挂
霸王别姬
活着
唐伯虎点秋香
秋菊打官司
菊豆
Hong gao liang
画魂
风月
Piao Liang Ma Ma
The Hand

初始化 Wrapper 须要的参数是 endpoint 对外提供服务的链接,D2RQ 默认的链接是“http://localhost:2020/sparql”。

小结总结

这篇文章简略地介绍了如何利用 D2RQ 开启 SPARQL endpoint 服务和两种进行交互的形式。D2RQ 是以虚构 RDF 图的形式来拜访关系数据库,在拜访频率不高,数据变动频繁的场景下,这种形式比拟适合。对于拜访频率比拟高的场景(比方 KBQA),将数据转为 RDF 再提供服务更为适合。接下来的实际篇咱们将介绍如何利用 Apache Jena,创立基于显式 RDF 数据的 SPARQL endpoint;并展现,在退出推理机后,对数据进行本体推理咱们能够失去额定的信息。

我的项目码源见文末跳转

跳转链接

更多优质内容请关注公号 & 知乎:汀丶人工智能;会提供一些相干的资源和优质文章,收费获取浏览。

本文参加了 SegmentFault 思否写作挑战「摸索编码世界之旅 – 记我的第一份编程工作」,欢送正在浏览的你也退出。

正文完
 0