Milch (DE)
Contents
Milch (DE)#
Die maßgebliche Motivation für GenDifS ist das Konzept der skos:Collection
-Klasse, veranschaulicht in dem Milch-Beispiel aus dem SKOS-Primer 4 Advanced SKOS: When KOSs are not Simple Anymore > 4.1 Collections of Concepts:
milk
<milk by source animal>
cow milk
goat milk
buffalo milk
In GenDifS wird diese Differenzierung wir folgt modelliert:
from gd05 import GenDifS_Map
mm = "Milch_DE"
o = GenDifS_Map(f"../mm/{mm}.mm")
cwd into input_dir: /home/dsci/a/l/LA_2022_ws/mm
reading /home/dsci/a/l/LA_2022_ws/mm > Milch_DE.mm (6 nodes)
Parsing XML: #1 start nodes
ontology node IDs: {'ID_1896225313': 'ONTOLOGY'}
RDF: 74 388
warning: no glossary file found.
updated mindmap with backup to /home/dsci/a/l/LA_2022_ws/mm/Milch_DE.mm_backup_2022-11-20T13-50-07
o.rdflib.serialize(destination=f"../ttl/{mm}.ttl")
o.parser_markdown()
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File ~/miniconda3/lib/python3.9/shutil.py:825, in move(src, dst, copy_function)
824 try:
--> 825 os.rename(src, real_dst)
826 except OSError:
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/tmpvymps1gh' -> '../ttl/Milch_DE.ttl'
During handling of the above exception, another exception occurred:
FileNotFoundError Traceback (most recent call last)
Cell In [2], line 1
----> 1 o.rdflib.serialize(destination=f"../ttl/{mm}.ttl")
2 o.parser_markdown()
File ~/miniconda3/lib/python3.9/site-packages/rdflib/graph.py:1210, in Graph.serialize(self, destination, format, base, encoding, **args)
1208 dest = url2pathname(path) if scheme == "file" else location
1209 if hasattr(shutil, "move"):
-> 1210 shutil.move(name, dest)
1211 else:
1212 shutil.copy(name, dest)
File ~/miniconda3/lib/python3.9/shutil.py:845, in move(src, dst, copy_function)
843 rmtree(src)
844 else:
--> 845 copy_function(src, real_dst)
846 os.unlink(src)
847 return real_dst
File ~/miniconda3/lib/python3.9/shutil.py:444, in copy2(src, dst, follow_symlinks)
442 if os.path.isdir(dst):
443 dst = os.path.join(dst, os.path.basename(src))
--> 444 copyfile(src, dst, follow_symlinks=follow_symlinks)
445 copystat(src, dst, follow_symlinks=follow_symlinks)
446 return dst
File ~/miniconda3/lib/python3.9/shutil.py:266, in copyfile(src, dst, follow_symlinks)
264 with open(src, 'rb') as fsrc:
265 try:
--> 266 with open(dst, 'wb') as fdst:
267 # macOS
268 if _HAS_FCOPYFILE:
269 try:
FileNotFoundError: [Errno 2] No such file or directory: '../ttl/Milch_DE.ttl'
Anschauliche Erklärung: Offensichtlich sind in diesem Beispiel zwei parallele Teilmengen-Beziehungen enthalten. Die erste, in Fettdruck wiedergegebene Beziehung besteht zwischen den Hauptklassen Milch und Kuhmilch und spannt insgesamt den für Mindmaps typischen Primärbaum auf:
:Kuhmilch rdfs:subClassOf :Milch
Mit diesem Baum ist aber auch eine zweite, in der Abbildung in unterstrichenem Kursivdruck wiedergegebene Beziehung zwischen charakterisierenden Sekundärbaum eng verflochten:
:Kuh rdfs:subClassOf :Tier
In GenDifS werden zwei wie hier zusammengehörenden Bäume ineinander verflochten modelliert. Dieses Syntax-Detail von GenDifS entspringt der Beobachtung, dass wir oft einen Sekundärbaum verwenden, um einen Primärbaum zu modellieren.
In Protegé sieht man, dass hier (mindestens) die folgenden zwei Begriffsbäume enthalten sind:
T-Box: OWL-Klassen und -Inferencing#
Wir betrachten den Satz:
Kuhmilch ist eine Milch, die von Rindern kommt.
In diesem Satz sind diese Informationen enthalten:
Kuhmilch
: Darum geht es im folgenden; Subjektist
: die Beziehung zum Oberbegriff ist (hier) eine Teilmengenbeziehungeine Milch ...
: erste Chrakterisierung von Kuhmilch, hier durch Kennzeichnung als Teilmenge des OberbegriffsMilch
eine Milch, die ...
: Charakterisierung einer speziellen Teilklasse vonMilch
durch eine zusätzliche chrakterisierende Eigenschaftvon ... kommt.
: chrakteristisches Attribut ist die Herkunft (und nicht z.B. der Fettgehalt)... Rindern ...
: der Wert des chrakteristischen Attributs Herkunft sind Rinder.
An der Formalisierung beteiligte Mengen:
Menge 1: alle Dinge, die eine Milch sind
:Milch a owl:Class .
Menge 2: alle Dinge, die als Herkunft “Kuh” haben
:hat_Herkunft_SOME_:Kuh
a owl:Class ;
owl:equivalentClass [
a owl:Restriction ;
owl:onProperty :hat_Herkunft ;
owl:someValuesFrom :Kuh ] .
Menge 3: alle Dinge, die eine Kuhmilch sind
:Kuhmilch a owl:Class .
Die Mengen 1-3 stehen bis jetzt völlig nebeneinander. Um Inferencing zu ermöglichen, beschreiben wir ihren Zusammenhang mit Axiomen.
Aufwärts-Inferencing#
Um ein relativ triviales Inferencing zu ermöglichen, fügen wir ein Subklassen-Axiom hinzu:
Menge 3 ist eine Teilmenge von Menge 1
:Kuhmilch rdfs:subClassOf :Milch .
Aufgrund dieser Aussage wird ein Reasoner dann, wenn ein Ding X Element der Menge 3: Kuhmilch ist, dieses Ding auch als Element der Menge 1: Milch klassifizieren.
Weil hier von einer Teilmenge auf die übergeordnete Menge geschlossen wird, wollen wir dieses Inferencing “aufwärts” nennen.
Abwärts-Inferencing#
Um ein Ding anhand seiner Eigenschaften genauer klassifizieren zu können, benötigen wir erstens eine weitere Menge:
Menge 4 = Schnittmenge von Menge 1 und Menge 2: Alle Dinge, die eine Milch sind, UND die als Herkunft Kuh haben.
:Milch_AND_:hat_Herkunft_SOME_:Kuh
a owl:Class ;
owl:equivalentClass [
a owl:Class ;
owl:intersectionOf (
:Milch
:hat_Herkunft_SOME_:Kuh ) ] .
Und um das Inferencing letztlich möglich zu machen, fügen wir zweitens noch eine weiteres Axiom hinzu, mit dem Menge 4 als Teilmenge von Menge 3 definiert wird:
Alle Dinge, die aus Menge 4 sind, sind automatisch auch enthalten in Menge 3
:Milch_AND_:hat_Herkunft_SOME_:Kuh
rdfs:subClassOf :Kuhmilch .
Indem wir die Schnittmenge von Menge 1 und 2 als Teilmenge von Menge 3 definieren, wird jetzt das gesuchte Inferencing unterstützt, das uns die Klassifiation erlaubt: Gegeben sei ein Ding X, von dem wir wissen,
dass es eine Milch ist: X ist Element von Menge 1;
dass es von einer Kuh stammt: X ist gleichzeitig auch Element von Menge 2.
Aufgrund dieser Angaben wird der Reasoner X auch als Element der Menge 4 klassifizieren. Und da Menge 4 als eine Teilmenge von Menge 3 definiert wurde, wird der Reasoner dann X ebenfalls als Element der Menge 3 klassifizieren: Damit wäre der Klassifikationsschritt Milch + Herkunft Kuh = Kuhmilch durchgeführt!
Formalisierung des Abwärts-Inferencings in Mengenschreibweise:
Kuhmilch ⊇ {x | Milch(x) ∧ ∃y[hat_Herkunft(x,y) ∧ Kuh(y)] }
Formalisierung in First Order Logic (FOL):
∀x[Kuhmilch(x) ← Milch(x) ∧ ∃y[hat_Herkunft(x,y) ∧ Kuh(y)]
Formalisierung in Description Logic (DL):
Kuhmilch ⊇ X
X == Milch ∧ ∃ hat_Herkunft.Kuh
(Wir haben hier X
als Abkürzung für :Milch_AND_:hat_Herkunft_SOME_:Kuh
verwendet.)
A-Box#
In GenDifS wird eine Ontologie abstrakt in einer Mindmap-basierten Sprache dargestellt. Deshalb ist es nötig, die Mindmap in mindestens eine, und möglich, die Mindmap in mehr als eine Ontologiesprache zu übesetzen.
Tatsächlich übersetzen wir einen einzigen Knoten in 3 verschiedene Objekte, die wir per Namespace unterscheiden:
@prefix ex: <http://mm2ttl.net/namespace/ex#> .
@prefix x: <http://mm2ttl.net/namespace/x#> .
@prefix : <http://mm2ttl.net/namespace/default#> .
Für Kuhmilch
als OWL-Klasse verwenden wir den Namespace :
. In ttl wird das Objekt :Kuhmilch
also expandiert zu http://mm2ttl.net/namespace/default#Kuhmilch
.
# BY/child.1, owl
:Kuhmilch # Abkürzung für http://mm2ttl.net/namespace/default#Kuhmilch
a owl:Class ;
rdfs:subClassOf :milch .
Für jede OWL-Klasse legen wir eine Test-Instanz an, im Beispiel :Kuhmilch
das Onjekt ex::Kuhmilch
. Da das Präfix ex:
zu `` expandiert wird, lautet die URI der Test-Instanz also http://mm2ttl.net/namespace/ex#:Kuhmilch
. (Der verbleibende Doppelpunkt sieht vielleicht etwas komisch aus, ist aber in einer IRI syntaktisch erlaubt).
# BY/child.4, owl-test
ex::Kuhmilch a :Kuhmilch .
ACHTUNG: Protege untedrückt in der Anzeige das Präfix. In der Folge werden http://mm2ttl.net/namespace/default#Kuhmilch
und http://mm2ttl.net/namespace/ex#:Kuhmilch
optisch gleich dargestellt; erst ein hover zeigt die unterschiedlichen Präfixe.
A-Box Inferencing-Test#
Um zu prüfen, ob die Übersetzung von GenDifS nach OWL korrekt erfolgt und in Kombination mit dem vorgesehenen Reasoner - hier dem OWL-RL-Reasoner owlrl - funktioniert, fügen wir den automatisch angelegten Beispiel-Instanzen zusätzliche Features hinzu. Beispiel:
<http://mm2ttl.net/namespace/ex#TEST_:Milch_6509c46dd8374fd8bf2aa5ed1d5f87ee> a :Milch ;
gendifs:classifyLike <http://mm2ttl.net/namespace/ex#:Kuhmilch> ;
:hat_Herkunft <http://mm2ttl.net/namespace/ex#:Kuh> .
Nach dem Inferencing durch owlrl
sind für die Beispiel-Instanz <http://mm2ttl.net/namespace/ex#TEST_:Milch_6509c46dd8374fd8bf2aa5ed1d5f87ee>
u.a. die folgenden Tripel in der ausmaterialisierten ttl-Datei enthalten:
<http://mm2ttl.net/namespace/ex#TEST_:Milch_6509c46dd8374fd8bf2aa5ed1d5f87ee> a
:Kuhmilch,
:Milch,
<http://mm2ttl.net/namespace/default#Milch_AND_:hat_Herkunft_SOME_:Kuh>,
<http://mm2ttl.net/namespace/default#hat_Herkunft_SOME_:Kuh>,
owl:Thing ;
gendifs:classifyLike <http://mm2ttl.net/namespace/ex#:Kuhmilch> ;
:hat_Herkunft <http://mm2ttl.net/namespace/ex#:Kuh> .
SKOS#
Außerdem interpretieren wir die Mindmap als SKOS-Baum. Zum Knoten Kuhmilch
erzeugen wir ein Objekt x::Kuhmilch
, das zu http://mm2ttl.net/namespace/x#:Kuhmilch
expandiert wird.
# BY/child.2, skos
x::Kuhmilch
rdf:type skos:Concept ;
rdfs:broaderTransitive x::Milch .
Alle drei Objekte habe also völlig unterschiedliche IRIS, bezeichnen völlig unterschiedliche Dinge, und können in der selben ttl-Datei (und damit auch im selben named graph) nebeneinaner auftreten und in Beziehung treten, ohne dass man mit Punning etc. arbeiten muss.