從零開始構(gòu)建一個電影知識圖譜實現(xiàn)KBQA智能問答[上篇]:本體建模

方法無法考慮周全,且隨著世界變化,對應的概念還在增長。
領域知識圖譜多采用自頂向下的方法來構(gòu)建本體。一方面,相對于開放域知識圖譜,領域知識圖譜涉及的概念和范圍都是固定或者可控的;另一方面,對于領域知識圖譜,我們要求其滿足較高的精度?,F(xiàn)在大家接觸到的一些語音助手背后對接的知識圖譜大多都是領域知識圖譜,比如音樂知識圖譜、體育知識圖譜、烹飪知識圖譜等等。正因為是這些領域知識圖譜來滿足用戶的大多數(shù)需求,更需要保證其精度。
本實例是一個電影領域的知識圖譜,我們采用自頂向下的方法來構(gòu)建本體結(jié)構(gòu)。首先介紹下我們使用的工具?protégé(點擊進入官網(wǎng)下載):
Protégé,又常常簡單地拼寫為 “Protege”,是一個斯坦福大學開發(fā)的本體編輯和知識獲取軟件。開發(fā)語言采用 Java,屬于開放源碼軟件。由于其優(yōu)秀的設計和眾多的插件,Protégé 已成為目前使用最廣泛的本體論編輯器之一(來自維基百科)。
打開 protege,看到和下圖類似的界面。在 Ontology IRI 中填寫我們新建本體資源的 IRI。讀者可以填寫自己的符合標準的 IRI。

點擊 “Entities”tab 標簽,選擇“Classes” 標簽。在這個界面,我們創(chuàng)建電影知識圖譜的類 / 概念。注意,所有的類都是 “Thing” 的子類。最左邊紅色小方框中的按鈕用于創(chuàng)建當前選中類的子類,中間的按鈕用于創(chuàng)建兄弟類(平行類),最右邊的按鈕刪除當前選中的類。我們創(chuàng)建了三個類,“人物”、“電影”、“類別”。右下方的界面是用于描述該類的一些特性,例如:"disjoint of" 是用于表示該類與哪些類是互斥的。本例中,三個類都是互斥的。也就是說,一個實例只能是三個類中的一個。我們沒有在 protege 中顯式地定義互斥關系,讀者可以自己定義。

接下來我們切換到 "Object Properties" 頁面,我們在此界面創(chuàng)建類之間的關系,即,對象屬性。這里我們創(chuàng)建了三個對象屬性,"hasActedIn" 表示某人參演了某電影,因此我們在右下方的 3 號矩形框中定義該屬性的 "domain" 是人,4 號框定義 "range" 是電影。這個很好理解,"domain" 表示該屬性是屬于哪個類的,"range" 表示該屬性的取值范圍。2 號框表示該屬性的逆屬性是 "hasActor",即,有了推理機,盡管我們的 RDF 數(shù)據(jù)只保存了 A 出演了 B,我們在查詢的時候也能得到 B 的演員有 A。1 號方框中是一些描述該屬性的詞匯,我們在上一篇文章中已經(jīng)介紹過,這里不再贅述。同理,我們定義另外兩個屬性,這里不再展示。

最后,我們切換到 "Data properties",我們在該界面創(chuàng)建類的屬性,即,數(shù)據(jù)屬性。其定義方法和對象屬性類似,除了沒有這么豐富的描述屬性特性的詞匯。其實不難理解,這些描述特性的詞匯是傳遞、對稱、反對稱、自反等,表明其必定有指向其他資源或自身的邊,而我們之前提到過,數(shù)據(jù)屬性相當于樹的葉子節(jié)點,只有入度,而沒有出度。

其實區(qū)分數(shù)據(jù)屬性和對象屬性還有一個很直觀的方法,我們觀察其 "range",取值范圍即可。對象屬性的取值范圍是類,而數(shù)據(jù)屬性的取值范圍則是字面量,如下圖。

protege 也支持以可視化的方式來展示本體結(jié)構(gòu)。我們點擊 "Window" 選項,在 "Tabs" 中選擇 "OntoGraf",然后 "Entities" 旁邊就多了一個標簽頁。在右側(cè)窗口中移動元素,可以很直觀地觀察本體之間的關系。

在這個小節(jié),我們簡單地介紹了如何用 protege 自頂向下地構(gòu)建知識圖譜的本體結(jié)構(gòu)。對于 Protege 更詳細的操作和介紹,請參考這篇文檔。
3.關系數(shù)據(jù)庫到 RDF

本文首先介紹 W3C 的 RDB2RDF 工作小組制定的兩個標準,用于將關系型數(shù)據(jù)庫的數(shù)據(jù)轉(zhuǎn)換為 RDF 格式的數(shù)據(jù)。然后介紹如何利用 d2rq 這個工具把我們 Mysql 中的數(shù)據(jù)轉(zhuǎn)為 RDF。
3.1 兩個標準
第一個標準是 direct mapping,即直接映射。何為直接映射?。
之所以說RDFS/OWL是RDF的“衣服”,因為它們都是用來描述RDF數(shù)據(jù)的。為了不顯得這么抽象,我們可以用關系數(shù)據(jù)庫中的概念進行類比。用過Mysql的讀者應該知道,其database也被稱作schema。這個schema和我們這里提到的schema language十分類似。我們可以認為數(shù)據(jù)庫中的每一張表都是一個類(Class),表中的每一行都是該類的一個實例或者對象(學過java等面向?qū)ο蟮木幊陶Z言的讀者很容易理解)。表中的每一列就是這個類所包含的屬性。如果我們是在數(shù)據(jù)庫中來表示人和地點這兩個類別,那么為他們分別建一張表就行了;再用另外一張表來表示人和地點之間的關系。RDFS/OWL本質(zhì)上是一些預定義詞匯(vocabulary)構(gòu)成的集合,用于對RDF進行類似的類定義及其屬性的定義。
Notice: RDFS/OWL序列化方式和RDF沒什么不同,其實在表現(xiàn)形式上,它們就是RDF。其常用的方式主要是RDF/XML,Turtle。另外,通常我們用小寫開頭的單詞或詞組來表示屬性,大寫開頭的表示類。數(shù)據(jù)屬性(data property,實體和literal字面量的關系)通常由名詞組成,而對象數(shù)據(jù)(object property,實體和實體之間的關系)通常由動詞(has,is之類的)加名詞組成。剩下的部分符合駝峰命名法。為了將它們表示得更清楚,避免讀者混淆,之后我們都會默認這種命名方式。讀者實踐過程中命名方式?jīng)]有強制要求,但最好保持一致。
規(guī)則十分簡單:
數(shù)據(jù)庫的表作為本體中的類(Class)。比如我們在 mysql 中保存的數(shù)據(jù),一共有 5 張表。那么通過映射后,我們的本體就有 5 個類了,而不是我們自己定義的三個類。
表的列作為屬性(Property)。
表的行作為實例 / 資源。
表的單元格值為字面量
如果單元格所在的列是外鍵,那么其值為 IRI,或者說實體 / 資源。
在實際應用中我們很少用到這種方法,盡管它是最便捷的方式。詳細的解釋和示例,請參考 W3C 的官方文檔 (A Direct Mapping of Relational Data to RDF)。
Direct mapping 的缺點很明顯,不能把數(shù)據(jù)庫的數(shù)據(jù)映射到我們自己定義的本體上。RDB2RDF 工作小組指定了另外一個標準——R2RML,可以讓用戶更靈活的編輯和設置映射規(guī)則。
我不打算在這里詳細地講解 R2RML 的具體語法和規(guī)則,讀者可以自己參考 W3C 的文檔 (R2RML: RDB to RDF Mapping Language)。其實可以把它當做一個工具,用的時候再查文檔即可,不用把所有的特性和語法都記下來,只需要知道它是什么,能干什么即可。為了讓讀者有個直觀地認識,我們以 mysql 中的數(shù)據(jù)為例,介紹怎么把 person 這個表映射到我們在 protege 中定義的 Person 類上,person_name 映射到 personName 上。
#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 語句來對查詢結(jié)果進行映射。比如,我們有一列表示某人的性別,我們可以用 SQL 語句選取男性的行,把這些行映射成我們定義的男性類。女性同理。這種特性大大增強了其靈活性。
下面我們介紹如何用 d2rq 這個工具把 mysql 的數(shù)據(jù)轉(zhuǎn)為 RDF。
3.2 D2RQ
D2RQ?的官方介紹是:
Accessing Relational Databases as Virtual RDF Graphs
沒錯,以虛擬 RDF 圖的方式訪問關系數(shù)據(jù)庫是其最主要的一個特性。它的機理就是通過 mapping 文件,把對 RDF 的查詢等操作翻譯成 SQL 語句,最終在 RDB 上實現(xiàn)對應操作。在做知識圖譜項目的時候,我們可以靈活地選擇數(shù)據(jù)訪問方式。當對外提供服務,查詢操作比較頻繁的情況下,最好是將 RDB 的數(shù)據(jù)直接轉(zhuǎn)為 RDF,會節(jié)省很多 SPARQL 到 SQL 的轉(zhuǎn)換時間。
D2RQ 提供了自己的 mapping language,其形式和 R2RML 類似。D2RQ 發(fā)布了 r2rml-kit 以支持 W3C 制定的兩個映射標準。D2RQ 有一個比較方便的地方,可以根據(jù)你的數(shù)據(jù)庫自動生成預定義的 mapping 文件,用戶可以在這個文件上修改,把數(shù)據(jù)映射到自己的本體上。就我們這個例子而言,數(shù)據(jù)關系比較簡單,自己編輯 R2RML 文件或者在 D2RQ 生成的 mapping 文件上修改效率差不多。在數(shù)據(jù)關系很復雜的時候,我建議直接在 D2RQ 生成的 mapping 文件上修改,會節(jié)省很多時間。D2RQ 的 mapping language 也很簡潔,同樣支持對 SQL 結(jié)果進行映射,其 SQL 是用 condition 關鍵詞隱式地表達,不像 R2RML 是顯式的 SQL 語句。更多的細節(jié)請參考官方文檔。
下載 D2RQ,進入其目錄,運行下面的命令生成默認的 mapping 文件:
generate-mapping -u root -o kg_demo_movie_mapping.ttl jdbc:mysql:///kg_demo_movie
root 是 mysql 的用戶名,沒有密碼則不輸入,-o 指定輸出文件路徑及名稱,jdbc:mysql:///kgdemomovie 指定我們要映射的數(shù)據(jù)庫。該命令的其他參數(shù)及使用方式請參考文檔。
根據(jù)我們的 mysql 數(shù)據(jù)庫生成的默認 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";
? ?.
下面是根據(jù)我們定義的本體修改的 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 genremap: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";
? ?.
語法規(guī)則比較簡單,具體的操作不再贅述,讀者可以參考相關文檔 (The D2RQ Mapping Language)。
D2RQ 支持的數(shù)據(jù)庫有 Oracle、MySQL、PostgreSQL、SQL Server、HSQLDB、Interbase/Firebird。也支持其他某些數(shù)據(jù)庫,但可能會有限制。請參考數(shù)據(jù)庫兼容性說明 (Accessing Relational Databases as Virtual RDF Graphs)。
使用下面的命令將我們的數(shù)據(jù)轉(zhuǎn)為 RDF:
.\dump-rdf.bat -o kg_demo_movie.nt .\kg_demo_movie_mapping.ttl
kgdemomovie_mapping.ttl 是我們修改后的 mapping 文件。其支持導出的 RDF 格式有 “TURTLE”, “RDF/XML”, “RDF/XML-ABBREV”, “N3”, 和“N-TRIPLE”?!癗-TRIPLE” 是默認的輸出格式。
kgdemomovie.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> .
kgdemomovie.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 腳本進行交互。跳過之前實踐篇練習的讀者,需要做的準備有:導入數(shù)據(jù)到 Mysql,下載 mapping 文件
4.1 SPARQL endpoint
前一篇介紹 SPARQL 的文章中提到,SPARQL endpoint 是 SPARQL 協(xié)議的一部分,用于處理客戶端的請求,可以類比 web server 提供用戶瀏覽網(wǎng)頁的服務。通過 endpoint,我們可以把數(shù)據(jù)發(fā)布在網(wǎng)上,供用戶查詢。
D2RQ,是以虛擬 RDF 的方式來訪問關系數(shù)據(jù)庫中的數(shù)據(jù),即我們不需要顯式地把數(shù)據(jù)轉(zhuǎn)為 RDF 形式。通過默認,或者自己定義的 mapping 文件,我們可以用查詢 RDF 數(shù)據(jù)的方式來查詢關系數(shù)據(jù)庫中的數(shù)據(jù)。換個說法,D2RQ 把 SPARQL 查詢,按照 mapping 文件,翻譯成 SQL 語句完成最終的查詢,然后把結(jié)果返回給用戶。下面是 D2R Server 的架構(gòu)圖:

進入 d2rq 目錄,使用下面的命令啟動 D2R Server:
d2r-server.bat kg_demo_movie_mapping.ttl
“kgdemomovie_mapping.ttl” 是我們定義的 mapping 文件。其他參數(shù)和配置請參考官方文檔。默認端口是 2020,在瀏覽器輸入 “http://localhost:2020/”,可以看到如下界面:

紅色方框 1 是我們定義的類別,點擊某個類別,我們可以看到其對應的所有實例(默認顯示 50 個,可以在 mapping 文件中修改服務器配置)。選中某個實例,可以看到其包含的所有屬性,如下圖:

點擊紅色方框 2 中的鏈接,進入 endpoint,如下圖:

4.2 瀏覽器中查詢
輸入框默認的 SPARQL 查詢是獲取所有的 RDF 三元組,“LIMIT” 關鍵詞指定返回結(jié)果數(shù)量的上限。點擊下圖紅框中的 “Go!”,執(zhí)行查詢:

讀者可以自行嘗試上篇文章中的例子:
“周星馳出演了哪些電影?”

“英雄這部電影有哪些演員參演?”

“鞏俐參演的評分大于 7 的電影有哪些?”

讀者也可以通過命令行的方式進行查詢,具體方法請參考官方的文檔。
4.3 編寫 Python 腳本進行交互
構(gòu)建基于知識圖譜的應用,我們希望將 SPARQL 查詢集成在代碼當中,對其進行包裝便于后續(xù)開發(fā)。這里介紹一個 Python 第三方庫:SPARQLWrapper。如其名,這是一個 Python 下的包裝器,可以讓我們十分方便地和 endpoint 進行交互。下面是通過 SPARQLWrapper,向 D2RQ endpoint 發(fā)送查詢 “鞏俐參演的評分大于 7 的電影有哪些”,得到結(jié)果的代碼。
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"])
運行結(jié)果:
2046Memoirs of a Geisha
荊軻刺秦王
大紅燈籠高高掛
霸王別姬
活著
唐伯虎點秋香
秋菊打官司
菊豆
Hong gao liang
畫魂
風月
Piao Liang Ma Ma
The Hand
初始化 Wrapper 需要的參數(shù)是 endpoint 對外提供服務的鏈接,D2RQ 默認的鏈接是 “http://localhost:2020/sparql”。
小結(jié)總結(jié)
這篇文章簡單地介紹了如何利用 D2RQ 開啟 SPARQL endpoint 服務和兩種進行交互的方式。D2RQ 是以虛擬 RDF 圖的方式來訪問關系數(shù)據(jù)庫,在訪問頻率不高,數(shù)據(jù)變動頻繁的場景下,這種方式比較合適。對于訪問頻率比較高的場景(比如 KBQA),將數(shù)據(jù)轉(zhuǎn)為 RDF 再提供服務更為合適。接下來的實踐篇我們將介紹如何利用 Apache Jena,創(chuàng)建基于顯式 RDF 數(shù)據(jù)的 SPARQL endpoint;并展示,在加入推理機后,對數(shù)據(jù)進行本體推理我們可以得到額外的信息。
項目碼源見文末跳轉(zhuǎn)
跳轉(zhuǎn)鏈接
更多優(yōu)質(zhì)內(nèi)容請關注公號&知乎:汀丶人工智能;會提供一些相關的資源和優(yōu)質(zhì)文章,免費獲取閱讀。
