Schema.org の語彙で RDFS 推論してみる(1):準備

本記事ではWebから得られる現実の簡単なデータを例に、ツールを使って SemanticWeb 技術(特に情報統合や推論)を概観します。

【目次】

[1]はじめに

以前の記事で、Webページに埋め込まれたJSON-LDファイルをRDFLibに読み込むところまで見ました。

しかし JSON-LD は、「そもそも扱いやすいJSON形式なのだから、JSONとして扱えば十分じゃない?」といわれれば、(特定の構造を扱うだけなら、)それはその通りです。

一方で、多くのWebサイトから情報を収集して、様々な再利用を考えるなら(多様なデータを扱うなら)、その考え方はかなり面倒なことも背負うことになります。

具体的には、Schema.org の語彙を使ってマークアップされた JSON-LD を扱うとしても、本記事の執筆時点で、Schema.org のサイトによると797のクラス、1453のプロパティが定義されているようです。

Wikipediaやブログの記事だけではなく、レストランや観光地の情報、イベントや求人その他、多様な情報が Web にはあふれています。

そして時代に応じて Schema.org の語彙は増えていきますし、それに伴って多様性も増していきます。

しかも、Web コンテンツに含まれる JSON-LD に記載される内容は、Web コンテンツ制作側が決めるものなので、開発側がコントロールできるものではありません。

こうなってくると、与えられた情報構造を直接解釈する方式で対応するのは厳しいと思います。(厳密なスキーマに基づく手法は厳しくなってくると思います。)

これに対して、与えられた情報構造をそのまま扱うのではなく、もう少しメタレベルの構造を基礎として多様な情報に対応していくアプローチが考えられます。

そのような技術に SemanticWeb があります。

SemanticWeb 技術も『銀の弾』ではないと思いますが、シナリオによっては非常に有効な技術だと思っています。

そこで本記事では、Webから得られる簡単な現実のデータを例に、ツールを使って SemanticWeb 技術を概観してみたいと思います。

特に、多様なデータの統合や推論について、(小さな)具体例を見ていきます。

SemanticWeb の技術は特定のプログラミング言語に依存するものではありませんが、お手軽に体感してみる、ということで、本記事では Python のツールを使って、Google Colaboratory で動作確認していきます。

ところで、記事を書き始めると予想した以上に長くなりましたので、技術的な詳細は割愛して雰囲気で乗り切ることにしました。しかし、それでも長いので記事を分けることにしました。

本記事はその第1回目です。

[2]Webから取得したJSON-LDをRDFLibで利用する

まずは、Schema.org の語彙を使って記述されている Web コンテンツに埋め込まれた JSON-LD を、RDF を扱える Python ライブラリ RDFLib に読み込む方法を見ておきます。

(1)RDFLibへ読み込むコードの準備

プログラムの内容は、前の2つの記事でやっていることと同じですので、詳細は必要に応じて以下の記事を参照して下さい。

本記事では、上記記事のコードを少しまとめた形で利用します。

まず、利用するパッケージをインストールします。
!pip install requests
!pip install rdflib
!pip install PyLD

インポートを以下のように宣言しておきます。
import json
import requests
from pyld import jsonld
from rdflib import Graph

先の記事に書いた、URL を指定すると Web ページに組み込まれている JSON-LD を抽出する関数を再利用します。

def get_jsonld_from_html_url(url, extractAllScripts):
    # 指定のURLのHTMLからJSON-LDを抽出する。
    # extractAllScripts=Trueのとき、JSON-LDのリストを返す。
    # それ以外は先頭のJSON-LD。

    # HTMLの取得
    res = requests.get(url)
    if res.status_code == 200:
        options = {}
        if extractAllScripts:
            options["extractAllScripts"] = True
        # PyLDを利用してHTMLからJSON-LDを抽出
        return jsonld.load_html(res.text,url, None, options)
    else:
        return None

本記事の利用シナリオは、複数のサイトから JSON-LD を取得、マージして利用しますので、RDFLib の Graph にロード(マージ)するための簡単なクラスを用意します。(後の記事で、このクラスを継承して再利用します。)

class SampleGraph:
  # RDFLibのGraphを保持して、JSON-LDの読み込みを定義する。
    def __init__(self) -> None:
        # ロード/マージ対象のGraph
        self.graph = Graph()
    
    def import_jsonld_from_html(self, url):
        # 指定のURLのHTML内にあるJSON-LDをGraphにロード(マージ)する
        self.import_jsonld( get_jsonld_from_html_url(url, False) )
        return self

    def import_jsonld(self, jsonld_data):
        # JSON-LDデータをGraphにロード(マージ)する
        self.graph.parse(data=jsonld_data, format='json-ld')
        return self

(2)実行して結果を可視化してみる

先に定義した関数やクラスを使ってWebページから JSON-LD を取得して Graph に取り込んでみます。

最初に多様なデータを扱うには云々と書きながら、恐縮ですが、結果の確認を行いやすいように、本記事では下記の2つのコンテンツを例とします。

そして、その内容を Turtle 形式で出力してみます。

以下がそのコードです。

# 2つのページから JSON-LD を取り込んだ Graph を得る
g = SampleGraph() \
    .import_jsonld_from_html( "https://en.wikipedia.org/wiki/JSON-LD" ) \
    .import_jsonld_from_html( "https://technodaifuku.blogspot.com/2022/04/protege.html" ) \
    .graph

# turtle 形式で出力
print( g.serialize( format='turtle' ))

実行すると、本記事執筆時点で以下の結果が得られました。(ページが更新されたら項目の値が変わる可能性があります。)

@prefix schema: <http://schema.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://technodaifuku.blogspot.com/2022/04/protege.html> a schema:WebPage .

[] a schema:BlogPosting ;
    schema:author [ a schema:Person ;
            schema:name "テクノ大福" ] ;
    schema:dateModified "2022-07-27T18:28:10+09:00"^^schema:Date ;
    schema:datePublished "2022-04-27T20:55:00+09:00"^^schema:Date ;
    schema:description "本記事では&#12289;デスクトップ版の Protégé のセットアップと&#12289;オントロジーの可視化&#12289;推論などの使い方を簡単に見ていきます&#12290;   &#12304;目次&#12305;  &#65339;1&#65341;はじめに &#65339;2&#65341;Protégé Desktop のセットアップ &#65288;1&#65289;Ubuntu でのインストール手順 &#65288;2&#65289;起動確認 &#65288;3&#65289;起動..." ;
    schema:headline "オントロジーエディタ Protégé を使ってみる" ;
    schema:image [ a schema:ImageObject ;
            schema:height 630 ;
            schema:url <https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM1XoAmXI7mKT_9hFYwfU1uzlMJYtzZ4Irw_ZJ6kf0wpTj1UQAeYx3nvpQ6LFXJJtMo6XZLxHDxnvoz-y2Yn_fyv8frt4klSRmnTGE0Iz39nxbDqn78NRS4iYyqLx5418EaPxfYyoNjs27hqnY819tjhT75udNmd27GhgZOqwk-6oWnxPX8bQyNwiR/w1200-h630-p-k-no-nu/protegeinit.png> ;
            schema:width 1200 ] ;
    schema:mainEntityOfPage <https://technodaifuku.blogspot.com/2022/04/protege.html> ;
    schema:publisher [ a schema:Organization ;
            schema:logo [ a schema:ImageObject ;
                    schema:height 60 ;
                    schema:url <https://blogger.googleusercontent.com/img/b/U2hvZWJveA/AVvXsEgfMvYAhAbdHksiBA24JKmb2Tav6K0GviwztID3Cq4VpV96HaJfy0viIu8z1SSw_G9n5FQHZWSRao61M3e58ImahqBtr7LiOUS6m_w59IvDYwjmMcbq3fKW4JSbacqkbxTo8B90dWp0Cese92xfLMPe_tg11g/h60/> ;
                    schema:width 206 ] ;
            schema:name "Blogger" ] .

[] a schema:Article ;
    schema:author [ a schema:Organization ;
            schema:name "Contributors to Wikimedia projects" ] ;
    schema:dateModified "2022-06-15T04:04:15Z"^^schema:Date ;
    schema:datePublished "2011-12-30T17:43:17Z"^^schema:Date ;
    schema:headline "a method of encoding Linked Data using JSON" ;
    schema:mainEntity "http://www.wikidata.org/entity/Q6108942" ;
    schema:name "JSON-LD" ;
    schema:publisher [ a schema:Organization ;
            schema:logo [ a schema:ImageObject ;
                    schema:url <https://www.wikimedia.org/static/images/wmf-hor-googpub.png> ] ;
            schema:name "Wikimedia Foundation, Inc." ] ;
    schema:sameAs <http://www.wikidata.org/entity/Q6108942> ;
    schema:url <https://en.wikipedia.org/wiki/JSON-LD> .

2つのサイトから取得した JSON-LD の内容が格納されていることが確認できます。

この情報をちゃんと理解するためには、RDFの理解、Turtleでの表現、Schema.orgの語彙の意味などを知る必要がありますが、本記事ではこれらは割愛しますので、雰囲気で乗り切ります。(前の記事にある元のJSON表現を見たほうが直感的に分かりやすいかも。)

ところで、(個人的には)Turtle 形式は可読性は良いと思っていますが、それでも全体としてどのような繋がりがあるのかは詳しく見ていかないとわかりません。

そこで、このデータを可視化してみます。

可視化の方法もいろいろありますが、ここでは下記サイトを利用させていただきました。

使い方は簡単で、上記サイトのテキストエリアに Turtle 形式の内容を張り付けて、「グラフ描画/Draw」ボタンをクリックします。

すると以下の画像が表示されます。

RDFデータの可視化

文字などは拡大表示しないと見えないと思いますが、概念や値が矢印で繋がっていることが分かります。

つまり、数学でいうグラフ構造(棒グラフなどの図ではありません)として JSON-LD の情報が表現されています。

これを見ると、2つのサイトの情報から同じ概念に線が繋がっているもの(OrganizationとImageObject)も確認できます。

ちなみに、サンプルサイトを2つにした理由の一つは、可視化した時に図が複雑になりすぎるからでした。。。(可視化も深いテーマであることがよくわかります。)

(3)SPARQLを使った検索例

データをマージして眺めるだけではもったいないので、情報抽出を行ってみます。

広く利用されているリレーショナルデータベース(RDB)にはデータアクセスや操作のための言語 SQL があるように、RDF にも SPARQL があります。

RDFLib のAPIを使って情報抽出は可能ですが、SPARQLを利用すると、複雑なクエリを宣言的に書けたり、実装依存を下げることができます。

本記事では SPARQL についての説明は割愛して先に進みますが、SPARQLをご存知ない方も、雰囲気を見て頂ければと思います。

ちなみに、SPARQL は SQL と文法が似たような感じに見えますが、扱うデータモデルが異なるため、SQLを知っていれば SPARQL も同様、ということではありません。

ざっくり言えば、SPARQL の検索条件(WHERE)には、先に可視化した図の矢印と楕円などに見られる繋がりパターンを指定して情報の抽出を行います。

①記事とそのURLを抽出してみる

本例では2つのサイト(記事)のJSON-LDを取り込んでいますので、記事のタイトルにあたるものとそのURLを抽出してみます。

いろいろな方法が考えられますが、以下はその一例です。
# 問合せ内容
query_str = """
PREFIX schema: <http://schema.org/>

SELECT ?title ?url
WHERE {
    {
        ?node schema:name ?title .
        ?node a schema:Article .
        ?node schema:url ?url .
    }
    UNION
    {
        ?node schema:headline ?title .
        ?node a schema:BlogPosting .
        ?node schema:mainEntityOfPage ?url .
    }
}
"""

# 問合せを実行して結果を表示
qres = g.query(query_str)
for row in qres:
    print(f"{row.title}\n    url = {row.url}")

結果は以下のようになりました。
JSON-LD
    url = https://en.wikipedia.org/wiki/JSON-LD
オントロジーエディタ Protégé を使ってみる
    url = https://technodaifuku.blogspot.com/2022/04/protege.html

②データに登場する人や組織を抽出してみる

Webサイトの視点ではなく、収集した情報に登場する人や組織を横断的に抽出してみます。加えて、どのような役割で登場しているのかも見てみます。

これもいろいろな方法が考えられますが、以下はその一例です。
# 問合せ内容
query_str = """
PREFIX schema: <http://schema.org/>

SELECT DISTINCT ?auth_type ?auth_name ?obj_rel
WHERE {
    ?auth a ?auth_type .
    FILTER (?auth_type = schema:Person || ?auth_type = schema:Organization)
    ?auth schema:name ?auth_name .
    ?obj ?obj_rel ?auth.
}
"""

# 問合せを実行して結果を表示
qres = g.query(query_str)
for row in qres:
    print(f"{row.auth_name}\n    type={row.auth_type})\n    rel={row.obj_rel}")

以下のような結果が得られました。
Contributors to Wikimedia projects
    type=http://schema.org/Organization)
    rel=http://schema.org/author
Wikimedia Foundation, Inc.
    type=http://schema.org/Organization)
    rel=http://schema.org/publisher
Blogger
    type=http://schema.org/Organization)
    rel=http://schema.org/publisher
テクノ大福
    type=http://schema.org/Person)
    rel=http://schema.org/author

ここで type は人か組織を表していて、rel は役割(著者、出版者)を表しています。

③その他補足

上記の例はSPARQLの検索結果を簡単に表示しているだけですが、情報の分析などを行う場合は、検索結果を Pandas の DataFrame に入れると、Colaboratory では奇麗に表示してくれますし、少々複雑なデータ処理が簡単に書けたりして便利です。(ある意味で、複雑な SPARQL を書くより、ざっくりと抽出して Pandas 等で処理したほうがスッキリする場合もあると思います。)

(参考)
記事『Oxigraph を利用して RDFLib の SPARQL クエリを高速化してみる』を書きました。よろしかったら参考にしてください。

[3]ここまでのまとめ:推論に向けて

ここまで、Schema.org の語彙を使って記述されている Web コンテンツに埋め込まれた JSON-LD を、RDF を扱える Python ライブラリ RDFLib に読み込んで、RDFの可視化や情報抽出の例を見てきました。

(個人的に)特に注目しておきたい点は以下の通りです。
  • 構造が異なる情報を一つのRDFストアにマージ(統合)できました。
    • 変換アルゴリズムを自作することなく統合できました。
  • もとのJSONは木構造表現のため、情報にアクセスする場合は(多くの場合、)ルートからのパスを意識する必要があります。一方で、RDFはグラフ(ネットワーク)構造に統合されて、項目間の繋がりのパターンでアクセスすることができます。
  • 汎用の検索言語SPARQLで宣言的に情報抽出を記述できます。
  • 書くコードはとても少ないのに、多くのことができます。
    • ライブラリ(と基礎理論)が多くの仕事をしてくれます。

これだけでも RDF で扱うメリットは十分あると思いますが、それでも不満?が無いわけではありません。

例えば、SPARQLの最初の検索例では、記事(Article)の抽出を行おうとしましたが、検索条件には Article と BlogPosting の二つのクラスを指定しています。

SPARQL の検索条件に Article クラスを指定すればよさそうなものですが、取得したデータだけで Article 指定で検索してしまうとブログ記事が抜けてしまいます。

なぜなら取得したデータは、ブログ記事は BlogPosting クラスに属しているとしか言っていないからです。

このため、先の例では Article と BlogPosting の2つのクラスを明示的に指定して検索しています。

人間はデータに書かれていない事でも常識的な(暗黙的な)知識を使って、ブログ投稿も記事の一種であると「推論」して、記事として扱うことができますが、コンピュータではできません。

具体的には、人間は「Article」と「BlogPosting」の単語の意味とその関係を知っているので「Article」と「BlogPosting」を同様に扱うことができますが、コンピュータ内部では「X」と「Y」という記号と同レベルの認識であり、これらの記号を関連付けるための背景知識が無いため、人間のように考えることができません。(人間でも背景知識が無ければ関連付けて考えることができないのは同じですが。)

ならば、この X(Argicle)と Y(BlogPosting)を関連付けるための知識をコンピュータに教えてあげればよいということになります。

このため Schema.org は RDFS にもとづく語彙定義(オントロジー)を提供しています。

これはクラス間の階層構造やプロパティの性質などが機械可読な形で定義されており、これを用いて推論することで、データに書かれていることより一歩進んだ情報検索などが可能になります。(XとYを関連付けられる可能性が増えます。)

以降の記事では、Schema.org が提供する RDFS を使った推論を見ていきます。

コメント

このブログの人気の投稿

VirtualBoxのスナップショット機能

Google Document AIで画像から表形式データを抽出する(Vision API OCRとの違い)

Ubuntu/Colab環境でPDFファイルのページを画像化する(pdf2image、pdftoppm、pdftocairo)