1. 程式人生 > >知識圖譜推理與實踐 (2) -- 基於jena實現規則推理

知識圖譜推理與實踐 (2) -- 基於jena實現規則推理

本章,介紹 基於jena的規則引擎實現推理,並通過兩個例子介紹如何coding實現。

規則引擎概述

jena包含了一個通用的規則推理機,可以在RDFS和OWL推理機使用,也可以單獨使用。

推理機支援在RDF圖上推理,提供前向鏈、後向鏈和二者混合執行模式。包含RETE engine 和 one tabled datalog engine。可以通過GenericRuleReasoner來進行配置引數,使用各種推理引擎。要使用 GenericRuleReasoner,需要一個規則集來定義其行為.

Rule的語法與結構

規則通過 Rule物件來進行定義,包含 body terms列表 (premises),head terms列表 (conclusions) 和可選的 name 和可選的direction。

An informal description of the simplified text rule syntax is:

_Rule_      :=   _bare-rule_ .
          or   [ _bare-rule_ ]
       or   [ ruleName : _bare-rule_ ]

_bare-rule_ :=   _term_, ... _term_ -> _hterm_, ... _hterm_    // forward rule
          or   _bhterm_ <- _term_, ... _term   _ // backward rule

_hterm     :=   term
_ or   [ _bare-rule_ ]

_term_      :=   (_node_, _node_, _node_)           // triple pattern
          or   (_node_, _node_, _functor_)        // extended triple pattern
          or   builtin(_node_, ... _node_)      // invoke procedural primitive

_bhterm_      :=   (_node_, _node_, _node_)           // triple pattern

_functor_   :=   functorName(_node_, ... _node_)  // structured literal

_node_      :=   _uri-ref_                   // e.g. http://foo.com/eg
          or   prefix:localname          // e.g. rdf:type
          or   <_uri-ref_>          // e.g. <myscheme:myuri>
          or   ?_varname_ // variable
          or   'a literal'                 // a plain string literal
          or   'lex'^^typeURI              // a typed literal, xsd:* type names supported
          or   number                      // e.g. 42 or 25.5

逗號 "," 分隔符是可選的.

前向和後向規則語法之間的區別僅與混合執行策略相關,請參見下文。

_functor_ 是一個擴充套件的三元組,用於建立和訪問文字值。functorName可以是任何簡單的識別符號。

為保障rules的可讀性URI引用支援qname語法。可以使用在 PrintUtil物件中註冊的字首。

下面是一些規則示例:

[allID: (?C rdf:type owl:Restriction), (?C owl:onProperty ?P),
     (?C owl:allValuesFrom ?D)  -> (?C owl:equivalentClass all(?P, ?D)) ]

[all2: (?C rdfs:subClassOf all(?P, ?D)) -> print('Rule for ', ?C)
    [all1b: (?Y rdf:type ?D) <- (?X ?P ?Y), (?X rdf:type ?C) ] ]

[max1: (?A rdf:type max(?P, 1)), (?A ?P ?B), (?A ?P ?C)
      -> (?B owl:sameAs ?C) ]
  • Rule allID說明了functor用於將OWL限制的元件收集到單個數據結構中,然後可以觸發進一步的規則
  • Rule all2 表示一個前向規則,它建立了一個新的後向規則,並且還呼叫了print.
  • Rule max1 說明了如何使用數字

可以使用以下方法載入和解析規則檔案:

List rules = Rule.rulesFromURL("file:myfile.rules");

或者

BufferedReader br = / _open reader_ / ;
List rules = Rule.parseRules( Rule.rulesParserFromReader(br) );

或者

String ruleSrc = / _list of rules in line_ /
List rules = Rule.parseRules( rulesSrc );

在前兩種情況下(從URL或BufferedReader讀取),規則檔案由一個簡單的處理器預處理,該處理器剝離註釋並支援一些額外的巨集命令:

# ...
: 註釋.

// ...
: 註釋

@prefix pre: <http://domain/url#>.
: 定義了一個字首pre ,可以用在規則檔案中.

@include <urlToRuleFile>.
: 包含指定規則,允許規則檔案包含RDFS和OWL的預定義規則

完整例項:

 @prefix pre: <http://jena.hpl.hp.com/prefix#>. 
 @include <RDFS>. 
 [rule1: (?f pre:father ?a) (?u pre:brother ?f) -> (?u pre:uncle ?a)]
 

規則推理demo1--喜劇演員

例如,在一個電影知識圖譜裡,如果一個演員參演的電影的型別是喜劇片,我們可以認為這個演員是喜劇電影

推理規則:

[ruleComedian: (?p :hasActedIn ?m) (?m :hasGenre ?g) (?g :genreName '喜劇') -> (?p rdf:type :Comedian)] 

我們用程式碼來實現:

        String prefix = "http://www.test.com/kg/#";
        Graph data = Factory.createGraphMem();

        // 定義節點
        Node movie = NodeFactory.createURI(prefix + "movie");
        Node hasActedIn = NodeFactory.createURI(prefix + "hasActedIn");
        Node hasGenre = NodeFactory.createURI(prefix + "hasGenre");
        Node genreName = NodeFactory.createURI(prefix + "genreName");
        Node genre = NodeFactory.createURI(prefix + "genre");
        Node person = NodeFactory.createURI(prefix + "person");
        Node Comedian = NodeFactory.createURI(prefix + "Comedian");

        // 新增三元組
        data.add(new Triple(genre, genreName, NodeFactory.createLiteral("喜劇")));
        data.add(new Triple(movie, hasGenre, genre));
        data.add(new Triple(person, hasActedIn, movie));
        
        // 建立推理機
        GenericRuleReasoner reasoner = (GenericRuleReasoner) GenericRuleReasonerFactory.theInstance().create(null);
        PrintUtil.registerPrefix("", prefix);
        // 設定規則
        reasoner.setRules(Rule.parseRules(
                "[ruleComedian: (?p :hasActedIn ?m) (?m :hasGenre ?g) (?g :genreName '喜劇') -> (?p rdf:type :Comedian)] \n"
                        + "-> tableAll()."));
        reasoner.setMode(GenericRuleReasoner.HYBRID); // HYBRID混合推理

        InfGraph infgraph = reasoner.bind(data);
        infgraph.setDerivationLogging(true);
        
        // 執行推理
        Iterator<Triple> tripleIterator = infgraph.find(person, null, null);

        while (tripleIterator.hasNext()) {
            System.out.println(PrintUtil.print(tripleIterator.next()));
        }

輸出結果:

(:person rdf:type :Comedian)
(:person :hasActedIn :movie)

可以看到,已經給person加上了Comedian。

規則推理demo2 -- 關聯交易

我們再來看上一篇文章中提到的那個金融圖譜:

陳華鈞老師PPT裡,有一個推理任務:

1) 執掌一家公司就一定是這家公司的股東;
2) 某人同時是兩家公司的股東,那麼這兩家公司一定有關聯交易;

PPT裡是使用Drools來實現的,具體可以參見PPT。我們這裡使用jena來實現,可以達到同樣的效果。

首先,構造好圖譜,為了方便理解,我們用中文變數:

Model myMod = ModelFactory.createDefaultModel();
        String finance = "http://www.example.org/kse/finance#";
        Resource 孫巨集斌 = myMod.createResource(finance + "孫巨集斌");
        Resource 融創中國 = myMod.createResource(finance + "融創中國");
        Resource 樂視網 = myMod.createResource(finance + "樂視網");
        Property 執掌 = myMod.createProperty(finance + "執掌");
        Resource 賈躍亭 = myMod.createResource(finance + "賈躍亭");
        Resource 地產公司 = myMod.createResource(finance + "地產公司");
        Resource 公司 = myMod.createResource(finance + "公司");
        Resource 法人實體 = myMod.createResource(finance + "法人實體");
        Resource 人 = myMod.createResource(finance + "人");
        Property 主要收入 = myMod.createProperty(finance + "主要收入");
        Resource 地產事業 = myMod.createResource(finance + "地產事業");
        Resource 王健林 = myMod.createResource(finance + "王健林");
        Resource 萬達集團 = myMod.createResource(finance + "萬達集團");
        Property 主要資產 = myMod.createProperty(finance + "主要資產");


        Property 股東 = myMod.createProperty(finance + "股東");
        Property 關聯交易 = myMod.createProperty(finance + "關聯交易");
        Property 收購 = myMod.createProperty(finance + "收購");

        // 加入三元組
        myMod.add(孫巨集斌, 執掌, 融創中國);
        myMod.add(賈躍亭, 執掌, 樂視網);
        myMod.add(王健林, 執掌, 萬達集團);
        myMod.add(樂視網, RDF.type, 公司);
        myMod.add(萬達集團, RDF.type, 公司);
        myMod.add(融創中國, RDF.type, 地產公司);
        myMod.add(地產公司, RDFS.subClassOf, 公司);
        myMod.add(公司, RDFS.subClassOf, 法人實體);
        myMod.add(孫巨集斌, RDF.type, 人);
        myMod.add(賈躍亭, RDF.type, 人);
        myMod.add(王健林, RDF.type, 人);
        myMod.add(萬達集團,主要資產,地產事業);
        myMod.add(融創中國,主要收入,地產事業);
        myMod.add(孫巨集斌, 股東, 樂視網);
        myMod.add(孫巨集斌, 收購, 萬達集團);

        PrintUtil.registerPrefix("", finance);

        // 輸出當前模型
        StmtIterator i = myMod.listStatements(null,null,(RDFNode)null);
        while (i.hasNext()) {
            System.out.println(" - " + PrintUtil.print(i.nextStatement()));
        }

上圖所示的圖譜,包含如下的三元組:

 - (:公司 rdfs:subClassOf :法人實體)
 - (:萬達集團 :主要資產 :地產事業)
 - (:萬達集團 rdf:type :公司)
 - (:地產公司 rdfs:subClassOf :公司)
 - (:融創中國 :主要收入 :地產事業)
 - (:融創中國 rdf:type :地產公司)
 - (:孫巨集斌 :股東 :樂視網)
 - (:孫巨集斌 rdf:type :人)
 - (:孫巨集斌 :執掌 :融創中國)
 - (:樂視網 rdf:type :公司)
 - (:賈躍亭 rdf:type :人)
 - (:賈躍亭 :執掌 :樂視網)
 - (:王健林 rdf:type :人)
 - (:王健林 :執掌 :萬達集團)

我們來定義推理規則:

1) 執掌一家公司就一定是這家公司的股東;
2) 收購一家公司,就是這家公司的股東
3) 某人同時是兩家公司的股東,那麼這兩家公司一定有關聯交易;

用jena規則來表示:

[ruleHoldShare: (?p :執掌 ?c) -> (?p :股東 ?c)] 
[[ruleHoldShare2: (?p :收購 ?c) -> (?p :股東 ?c)] 
[ruleConnTrans: (?p :股東 ?c) (?p :股東 ?c2) -> (?c :關聯交易 ?c2)] 

執行推理:

         GenericRuleReasoner reasoner = (GenericRuleReasoner) GenericRuleReasonerFactory.theInstance().create(null);
        reasoner.setRules(Rule.parseRules(
                "[ruleHoldShare: (?p :執掌 ?c) -> (?p :股東 ?c)] \n"
                        + "[ruleConnTrans: (?p :收購 ?c) -> (?p :股東 ?c)] \n"
                        + "[ruleConnTrans: (?p :股東 ?c) (?p :股東 ?c2) -> (?c :關聯交易 ?c2)] \n"
                        + "-> tableAll()."));
        reasoner.setMode(GenericRuleReasoner.HYBRID);

        InfGraph infgraph = reasoner.bind(myMod.getGraph());
        infgraph.setDerivationLogging(true);

        System.out.println("推理後...\n");

        Iterator<Triple> tripleIterator = infgraph.find(null, null, null);
        while (tripleIterator.hasNext()) {
            System.out.println(" - " + PrintUtil.print(tripleIterator.next()));
        }

輸出結果:


推理後...

 - (:萬達集團 :關聯交易 :樂視網)
 - (:萬達集團 :關聯交易 :融創中國)
 - (:萬達集團 :關聯交易 :萬達集團)
 - (:孫巨集斌 :股東 :萬達集團)
 - (:孫巨集斌 :股東 :融創中國)
 - (:融創中國 :關聯交易 :萬達集團)
 - (:融創中國 :關聯交易 :樂視網)
 - (:融創中國 :關聯交易 :融創中國)
 - (:樂視網 :關聯交易 :萬達集團)
 - (:樂視網 :關聯交易 :融創中國)
 - (:樂視網 :關聯交易 :樂視網)
 - (:賈躍亭 :股東 :樂視網)
 - (:王健林 :股東 :萬達集團)
 - (:公司 rdfs:subClassOf :法人實體)
 - (:萬達集團 :主要資產 :地產事業)
 - (:萬達集團 rdf:type :公司)
 - (:地產公司 rdfs:subClassOf :公司)
 - (:融創中國 :主要收入 :地產事業)
 - (:融創中國 rdf:type :地產公司)
 - (:孫巨集斌 :收購 :萬達集團)
 - (:孫巨集斌 :股東 :樂視網)
 - (:孫巨集斌 rdf:type :人)
 - (:孫巨集斌 :執掌 :融創中國)
 - (:樂視網 rdf:type :公司)
 - (:賈躍亭 rdf:type :人)
 - (:賈躍亭 :執掌 :樂視網)
 - (:王健林 rdf:type :人)
 - (:王健林 :執掌 :萬達集團)

我們看到,推理後孫巨集斌是三家公司的股東,三家公司都有關聯交易。


作者:Jadepeng
出處:jqpeng的技術記事本--http://www.cnblogs.com/xiaoqi
您的支援是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。