RDFをベースとするセマンティックWebの世界は、基本はとても単純です。 本文書では、簡略化された教育用言語である "Notation 3"(N3)を使って、 セマンティックWebの基礎について説明します。 N3は、XML文法であるRDFと、基本的にほぼ同じ機能を持っていますが、 初心者にとって、「走り書き」しやすい言語になっています。
RDFでは、「主語」と「述語」と「目的語」から成る 単純なステートメントの集合によって、情報を表します。 N3では、このRDFにおける3つの要素を、以下の様に、 末尾にピリオドをつけて記述します。
<#pat> <#knows> <#jo> .
主語、述語、目的語は全て、URI(Universal Resource Identifier)によって 表されます。URIとは、<http://www.w3.org/>や <http://www.w3.org/2000/10/swap/test/s1.n3#includes>のようなもの です。 なお、<#pat>のように、#の前に何も書かれていない場合には、 現在の文書の中を指定します。
例外として、目的語(だけ)は、「値」を表す文字列を 使用することもできます。
<#pat> <#knows> <#jo> . <#pat> <#age> "24" .
"knows"という動詞は、RDFでは「プロパティ」と呼ばれ、2つのものの関係を
表す名詞にあたります。
また、以下の文:
<#pat> <#child> <#al> .
は、簡単かつ読みやすくするために、
<#pat> has <#child> <#al> .
または
<#al> is <#child> of <#pat> .
のように書くこともできます。
また、以下の2つの、省略記法(ショートカット)を使うことができます。 同じ主語に関するステートメントが複数ある場合、セミコロン";"を使って、 同じ主語に対するプロパティを複数記述することが出来ます。 また、カンマ","を使用すれば、 同じ主語と述語に対して、複数の目的語を記述することもできます。
<#pat> <#child> <#al>, <#chaz>, <#mo> ; <#age> "24" ; <#eyecolor> "blue" .
したがって、例えば以下の表のようなデータは、
age | eyecolor | |
pat | 24 | blue |
al | 3 | green |
jo | 5 | green |
以下のように記述することができます。
<#pat> <#age> "24"; <#eyecolor> "blue" . <#al> <#age> "3"; <#eyecolor> "green" . <#jo> <#age> "5"; <#eyecolor> "green" .
ときどき、ステートメントに含まれるものの中に、 識別子を与えず、プロパティのみを与えたい場合があります。 このような場合には、大かっこ"[]"の中に、プロパティのみを記述します。
<#pat> <#child> [ <#age> "4" ] , [ <#age> "3" ].
上の文章は、「#patは、#ageが"4"である#childと、#ageが"3"である#child
を持っている。」という意味を表します。
ここで、以下の2つの大事な点があります。
(上記の1番目の点に関して)、もし実際に「名前」を使うのであれば、 上の表のデータは、以下の様に書くことができます。
[ <#name> "Pat"; <#age> "24"; <#eyecolor> "blue" ]. [ <#name> "Al" ; <#age> "3"; <#eyecolor> "green" ]. [ <#name> "Jo" ; <#age> "5"; <#eyecolor> "green" ].
大かっこには、いろいろな組み合わせ方がありますので、
後ほどいくつか、例を示します。
N3を使ったデータの表現方法については、これまで説明した以外には、
学ぶことはさほどありません。
次に進みましょう。
セマンティックWebでは、1つのドキュメントだけで、 何かの意味を定義しきれるとは限りません。 これは、英語でも(場合によっては数学でも)そうですが、 我々が、(国会図書館のカタログカードやWebページなどの) 「タイトル」という概念を使ってコミュニケーションをする場合には、 我々は「タイトル」という共有した概念に頼ることになる、ということです。 セマンティックWebでは、「タイトル」の概念を、 全く同じURIを使用することによって、 極めて正確に共有します。
N3のドキュメントのタイトルは、以下のようにして与えることができます。
<> <#title> "A simple example of N3".
(<>のような空のURIは、常にそれが書かれているドキュメント自身を指 します。) <#title>は、そのドキュメントで定義された、#titleの概念を示します。 これだけでは、読者にとっては、あまり大きな意味は無いかも知れません。 しかし、 Dublin Core と呼ばれるプロパティのリストを作ったグループは、 その中に彼らの「タイトル」という概念を著して、 これに<http://purl.org/dc/elements/1.1/title>という識別子を与えました。
したがって、これを使えば、以下のように、 より厳密に定義されたステートメントを書くことができます。
<> <http://purl.org/dc/elements/1.1/title> "Primer - Getting into the Semantic Web and RDF using N3".
もちろん、 前出の#ageや#eyecolorなどの全てに対して、このような長い識別子を使うのは、 少し冗長です。 N3では、この長い部分に対して、短いプリフィックスを設定することができるよ うになっています。これは、名前空間識別子(namespace identifier)と呼ばれる もので、"@prefix"を使用して、以下のように設定します。
@prefix dc: <http://purl.org/dc/elements/1.1/> . <> dc:title "Primer - Getting into the semantic web and RDF using N3".
プリフィックスを使う際には、"dc"と"title"の間に、 ハッシュ"#"の代わりにコロン":"を使うこと、 全体をアングルブラケット"<>"では囲まないことに注意して下さい。 プリフィックスは、非常に便利なので、 殆ど全てのN3記述において使われます。 プリフィックスは、一度設定したら、 同一ファイル内の以降において有効です。 (この仕様は、将来変更されるかもしれません)
RDFボキャブラリは、非常にたくさんあります。 また、それらは現在でも増えつづけています。 RDFのホームページ とそのリンク先を確認してみて下さい。 また、自分のアプリケーションをシンプルにするために、 独自のボキャブラリを作成することもできます。
これより先の説明では、スペースの節約のために、 よく知られたnamespaceを、いくつか使用します。 以下のプリフィックスを設定します。
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix ont: <http://www.daml.org/2001/03/daml-ont#> .
これらはそれぞれ、RDF、RDFスキーマ、DAMLオントロジ名前空間です。 これらでは、セマンティックWebを始めるための、基本用語が提供されます。 更に、プリフィックス無しの場合には、 このドキュメント自身を指すこととするため、以下のN3記述を行います。
@prefix : <#> .
これにより、上記の例は、以下のように書けます。
:pat :child [ :age "4" ] , [ :age "3" ].
これで、少し文字数が削減されました。 ここまでで、N3でのデータの書き方が理解できたかと思います。 次の章では、独自のボキャブラリの作成を行います。 ボキャブラリとは、単なるデータでしかありません。
前出の例における、"dc:title"は、RDFの プロパティです。 新しいボキャブラリやオントロジを定義する場合には、 新しいクラスと、そのプロパティを定義します。 何かが何の型であるかということを、それが クラス に属している、と言います。
何かが何かの型であるということを表すプロパティは、
"rdf:type
"です。
これは、N3では、単に"a
"と略して表記されます。
したがって、「人」というクラスは、以下のように定義できます。
:Person a rdfs:Class.
これにより、同じドキュメント内で、以下のように、 実際の人を紹介することができます。
:Pat a :Person.
「クラス」は、それが何に属するかを表します。 1つのオブジェクトは、複数のクラスに属することができます。 それらのクラスは階層構造を持つ必要はありません。 人(Person)、生物(AnimateObject)、動物(Animal)、 背の高い人(TallPerson)、友人(Friend) などのクラスを考えてみてください。 なお、以下のように2つのクラスに関連があることを記述することもできます。 これについては、RDFスキーマの(クラスの)プロパティと、 DAMLオントロジのボキャブラリを 確認してみて下さい。
:Woman a rdfs:Class; rdfs:subClassOf :Person .
rdf:Propertyは、2つのものの関係を宣言する場合に使われます。
:sister a rdf:Property.
2つのものの間に関係が存在する場合、 その関係に関する情報は、すぐさまクラスとして表現することができます。 あるプロパティの主語が、常に1つのクラスに属さなければならない場合、 そのクラスは、そのプロパティの ドメイン と呼ばれます。 同様に、あるプロパティの目的語が、 常に1つのクラスに属さなければならない場合、 そのクラスは、 レンジ と呼ばれます。 1つのプロパティは、複数のドメインとレンジを持つことが出来ますが、 通常は、1つのドメインやレンジを指定します。
:sister rdfs:domain :Person; rdfs:range :Woman.
クラス識別子は大文字で始まり、プロパティは小文字で始まります。 これは、規則ではありませんが、これらを続けて表記するのに好都合です。 なお、rdfs:rangeおよびrdfs:domain自身のドメインは、 rdf:Propertyであるので、:sisterは、明示的に書かれなくても rdf:Propertyに属します。
1つもしくは複数の用語を含むボキャブラリの定義を行う場合、 (定義を始めた時に、気がつくかどうかはともかく)、 実際には、しばしば、それが他のボキャブラリにある他の用語と、 全く同値である場合があります。 これは、人もしくはマシンが情報を扱う際において、非常に有用な情報です。 2つの用語が同値であるというプロパティは、 とても基本的かつ有用であり、 N3には、"="という、特別な省略記法があります。
:Woman = foo:FemaleAdult . :Title a rdf:Property; = dc:title .
ヒント: 可能なかぎり、他の人のボキャブラリを使って下さい。 そうすることによって、情報の交換が、より行いやすくなります。 また、類義語を含んだボキャブラリを定義する場合には、 それらが同値であることを、記述するようにして下さい。 そうすることによって、現在および将来のプロセッサが、 あなたおよび他の人のデータを、 より「意味ある方法」で処理することができるようになります。
クラスとプロパティを定義するドキュメントは、スキーマと呼ばれます。
スキーマでは、特定の名前空間における、識別子の意味が定義されます。
名前空間URIは、あなたのコントロール下で、
他の人が再利用することがないと保証できる何でもよいです。
名前空間URIを決める1つの方法は、その名前空間に関して
電子メールを送ることです。
そうすれば、そのメールのヘッダには、メーラが生成した一意な識別子である
Message-Id:
ヘッダが含まれるので、
これを名前空間URIとして使用することができます。
また、他の方法としては、きちんと管理されていて、
あなた以外の誰も変更することができないWebスペースに、
そのドキュメントを置いて下さい。
あるいは、もしあなたが今すでに、
そのWebサーバを使っているのであれば、
他のドキュメントが置いてあるディレクトリと同じディレクトリに、
(例えばmydb.n3
というファイル名で)、
そのドキュメントを置いて下さい。
その場合には、名前空間識別子として、
<mydb.n3#>
を使用することができます。
あなたが定義した用語のN3情報を、 Web上のドキュメントに格納して使う場合(よい習慣です!)、 名前空間の末尾は、ハッシュ"#"となります。
あなたが定義したスキーマを、世界中に向けて公開する必要はありませんが、
それを公開することによって、あなたのボキャブラリを使用して書かれた
ドキュメントを、マシンによって処理しやすくなります。
電子メールメッセージやWebページがRDFで(XMLやN3で)書かれていて、
かつそれ自身が、そのスキーマ情報も含んでいれば、
そのメールやWebページを見つけたプログラムは、
それらの2つの情報を、簡単に関連付けることができるという、
大きなメリットがあります。
なお、その場合には名前空間は、
ドキュメントのURIの末尾に"#
"を付けたものになります。
これは、非常に大きなテーマです。 例えば、以下の例を参照してみて下さい。
以上で、自分のボキャブラリやオントロジを作るために必要なことは、 全て説明しました。 また、それらをより充実させていくために必要な情報へのリンクも示しました。 これ以上、特に学ぶ必要はありません。 ここまでに説明したことによって、あなたは、 セマンティックWebのデータの操作や交換をする、 新しいアプリケーションや、スキーマ、データファイル、 プログラム等を、自由に作成することができます。
あなたが書いたスキーマや、あなた(もしくはあなたのプログラムが) 書いたデータファイルは、 すべてRDFステートメントの単純なセットです。 それらは、全て、シンプルな丸と矢印から成るダイアグラムで、 表すことができます。
RDFステートメントのセットを、 式(formula) と呼びます。 また、ステートメントが1つだけ含まれる式のことを、 コンテキスト と呼びます。 式においては、以下のことが成り立ちます。
複数の式について、考慮する必要がある場合もあります。 例えば、2つの式のうちのいずれか片方だけが正しいとき、 我々は、それらを同じ式の中に格納することはできません。
N3の式では、任意の式を中かっこ"{}"で囲って、使用することができます。
<x.rdf> :says { :pat a :Person . } .
中かっこ内の式は、構文の残りの部分に対して、識別子として振舞います。 これは、リテラル表現のように、そのコンテンツによってのみ定義されます。 上の例は、単に、ドキュメント"x.rdf"に、 "pat"が"person"であるという「仮想の式」が、 記述されていることを宣言しているだけです。 上のステートメントは、決して"pat"が"person"であるということは言っていません。 (これを、XMLシリアライゼーションした場合には、問題が発生します。)
式を記述する一番大きな理由は、ルールを記述するためです。
ルールを使うことによって、仮定から結論を導き出すことができます。
ルールとは、プログラムによって処理が可能な形式のステートメントです。
最も単純なルールは、log:implies
という
1つのプロパティのみを使用することです。
(ルールを処理するためのプログラムエンジンの1つとして、
Eulerの作成したcwmがあります。)
これから紹介する、論理的でむしろ実験的なものの名前空間は、
以下の通りとなります。
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
単純なルールの例として、以下を考えてみます。
{ sensor:thermostat math:greaterThan "30" . } log:implies { control:furnace control:setTo "1" . } .
上の全ての部分を理解できるシステムが構築されていた場合、 以下を与えると、
sensor:thermostat math:greaterThan "30" .
以下の結論が導き出されます。
control:furnace control:setTo "1".
このルールは、単純かつ便利ですが、実際には、よりパワフルなルールとして、 どんな識別子に対しても、あてはめることができるルールもあります。 識別子が置き替えられた場合でも、コンテキスト全体を通じて それが真となるような式です。 このような式は、"log:forAll"プロパティという 「魔法の」プロパティを使って、記述することができます。
this log:forAll :x, :y. {:x :parent :y} log:implies {:y :child :x}.
上記は、「これはxとyがいかなる値をとる場合でも真である。 もしxがyの親であるならば、yはxの子供である。 」という意味を表します。 "this"は、それを直接囲ったブレスの中の式を指します。 上記のように、外側のレベルに存在する場合には、 そのドキュメントに書かれた式を指します。
実際にオントロジプロパティを使用して、 「親」と「子」とが反対であるということを、 定義することができます。
:parent ont:inverse :child .
マシンに、一般的なルールを与えることによって、 上と同じ事を、推論させることもできます。
this log:forAll :p, :q . { :p ont:inverse :q . } log:implies { this log:forAll :x, :y. { :x :p :y. } log:implies { :y :q :x. } } .
上記の外側のルールは、 「もしpとqが反対であるならば、内側のルールが真である。 」という意味です。 内側のルールは、 「もしxがyのpであるならば、yはxのqである。」 という意味です。 内側のルールは、どんなxとyに対しても適用されます。 そして上記全体は、どんなpとqに対しても適用されます。 一度、上記のメインルールを記述しておけば、 "inverse"プロパティは、 毎回特別なルールを記述することなく、 使用することができます。
これまでの説明では、これらのルールを、 マシンがどのように実行するのかについては、触れていません。 たくさんの種類のエンジンがあり、それらは異なった方法で決定を行います。 これについては、あなたのまわりにある、 処理エンジンソフトウェアのドキュメントを参照して下さい。
私が共同開発したプログラムの1つにcwm (短い"oo"を伴う、ウェールズ語の発音です)と呼ばれるものがあります。 以下の例では、このcwmを使用します。
ここまでの説明で、N3によって、どうやってステートメントを記述するかが 理解できたかと思います。では、これらを使って、何ができるのでしょうか? 1つの例として、推論結果を、既存のデータに対して、追加することができます。 また、もう1つの例としては、 フィルタ と呼ばれる検索フォームを使用して、有用な情報を取り出すことができます。
uncle.n3 には、FredはJoeの父であり、BobはFredの兄弟であることが記述されています。 この情報に対して、 「おじ」の関係を表す論理的なルールを記述することにします。
@prefix : <uncle#>. :Fred is :father of :Joe. :Bob is :brother of :Fred. @prefix log: <http://www.w3.org/2000/10/swap/log#> . this log:forAll :who1, :who2. { :who1 :father [ :brother :who2 ] } log:implies { :who1 :uncle :who2 }.
上のルールは、「誰かの父に兄弟がいるならば、その人は彼らのおじである」
ということを意味します。
このルールを、cwmに格納すれば、
cwm
は、コマンドラインオプション"--think.
"
を指定することにより、
おじの情報を推論することができるようになります。
別のファイル (uncleF.n3) で、上記のルールをフィルタとして使ってみます。 "--think"オプションを使用するのではなく、 フィルタとして動作させることによって、 ルールに合致した情報のみを選択し、 残りは全て破棄されます。 フィルタを使うことによって、 以下の既知の情報の中から、欲しい論理的な関係を抽出することができます。
@prefix : <uncle#>. @prefix log: <http://www.w3.org/2000/10/swap/log#> . this log:forAll :p. # What is the relationship between Joe and Bob { :Joe :p :Bob } log:implies { :p a :RelationshipBetweeJoeAndBob }. # Is Bob an Uncle of Joe? { :Joe :uncle :Bob } log:implies { :Joe :uncle :Bob }.
上記を使用して、cwmに対して、論理的な関係を考えさせると、 以下の結論となります。
> python cwm.py uncle.n3 --think --filter=uncleF.n3 :Joe :uncle :Bob . :uncle a :RelationshipBetweeJoeAndBob .
上記コマンドラインは、「 uncle.n3を読み込んで、それから得ることが出来るルールを推論しなさい。 そして、uncleF.n3フィルタによって選択される情報を出力しなさい。 」という意味になります。
上記については、今後も検討を続け、資料を作成していく必要があります。 更なるアイデアのために、 いろいろな複雑な例の膨大なリスト があります。 ただし、これらにはあまりチュートリアル的な説明はついていません。
Have fun!
以下は、正式な定義ではありません。 これらの用語がどういう意味であるかを把握するためのヘルプです。 以下の各用語からは、それらに関する更に詳しい情報へのリンクが張られています。
log:implies
"という動詞を持つ
ステートメント(statements)
である。