A la fin de ce notebook, vous saurez : 1. Comprendre le vocabulaire RDFS et son rôle dans le Web Sémantique 2. Construire des hiérarchies de classes et de proprietes avec rdfs:subClassOf et rdfs:subPropertyOf 3. Utiliser rdfs:domain et rdfs:range pour contraindre les proprietes 4. Appliquer l’inference RDFS pour deduire de nouveaux triplets 5. Interroger des données enrichies par le raisonnement RDFS
Prerequis
.NET SDK 9.0+ et .NET Interactive
Notebooks SW-1 a SW-4 (bases RDF, graphes, SPARQL)
Duree estimee : 40 minutes
Installation et imports
Chargeons dotNetRDF et les espaces de noms necessaires pour travailler avec RDFS.
RDF Schema (RDFS) est une extension de RDF qui fournit un vocabulaire pour decrire la structure des données RDF. Tandis que RDF permet d’exprimer des faits sous forme de triplets (sujet, predicat, objet), RDFS ajoute la capacite de définir :
RDF Schema (RDFS) est standardise par le W3C dans RDF Schema 1.1 (Brickley & Guha, W3C Recommendation 2014). C’est une extension de RDF qui fournit un vocabulaire pour decrire la structure des données RDF – classes, hiérarchies, domaines/portees de proprietes et règles d’inference.
La même taxonomie, rendue sous forme de graphe : chaque flèche rdfs:subClassOf pointe de la sous-classe vers sa super-classe (sens de lecture « est une sorte de »).
Lecture. Les 5 relations rdfs:subClassOf (Dog/Cat < Mammal, Parrot < Bird, Mammal/Bird < Animal) forment un arbre d’héritage. RDFS garantit que cette relation est transitive : Dog ⊑ Mammal et Mammal ⊑ Animal impliquent Dog ⊑ Animal. C’est cette transitivité que le raisonneur exploite pour typer les instances par toutes leurs classes ancêtres (diagramme suivant).
// Creer un graphe et definir les namespacesIGraph schema =newGraph();schema.NamespaceMap.AddNamespace("ex",newUri("http://example.org/animals#"));schema.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));schema.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));schema.NamespaceMap.AddNamespace("xsd",newUri("http://www.w3.org/2001/XMLSchema#"));// Noeuds predicats RDFSIUriNode rdfType = schema.CreateUriNode("rdf:type");IUriNode rdfsClass = schema.CreateUriNode("rdfs:Class");IUriNode subClassOf = schema.CreateUriNode("rdfs:subClassOf");IUriNode rdfsLabel = schema.CreateUriNode("rdfs:label");IUriNode rdfsComment = schema.CreateUriNode("rdfs:comment");IUriNode rdfsDomain = schema.CreateUriNode("rdfs:domain");IUriNode rdfsRange = schema.CreateUriNode("rdfs:range");IUriNode rdfProperty = schema.CreateUriNode("rdf:Property");// --- Definition des classes ---IUriNode animal = schema.CreateUriNode("ex:Animal");IUriNode mammal = schema.CreateUriNode("ex:Mammal");IUriNode bird = schema.CreateUriNode("ex:Bird");IUriNode dog = schema.CreateUriNode("ex:Dog");IUriNode cat = schema.CreateUriNode("ex:Cat");IUriNode parrot = schema.CreateUriNode("ex:Parrot");// Declarer les classesschema.Assert(newTriple(animal, rdfType, rdfsClass));schema.Assert(newTriple(mammal, rdfType, rdfsClass));schema.Assert(newTriple(bird, rdfType, rdfsClass));schema.Assert(newTriple(dog, rdfType, rdfsClass));schema.Assert(newTriple(cat, rdfType, rdfsClass));schema.Assert(newTriple(parrot, rdfType, rdfsClass));// Hierarchie de classes (rdfs:subClassOf)schema.Assert(newTriple(mammal, subClassOf, animal));schema.Assert(newTriple(bird, subClassOf, animal));schema.Assert(newTriple(dog, subClassOf, mammal));schema.Assert(newTriple(cat, subClassOf, mammal));schema.Assert(newTriple(parrot, subClassOf, bird));// Etiquettes (rdfs:label)schema.Assert(newTriple(animal, rdfsLabel, schema.CreateLiteralNode("Animal","fr")));schema.Assert(newTriple(mammal, rdfsLabel, schema.CreateLiteralNode("Mammifere","fr")));schema.Assert(newTriple(bird, rdfsLabel, schema.CreateLiteralNode("Oiseau","fr")));schema.Assert(newTriple(dog, rdfsLabel, schema.CreateLiteralNode("Chien","fr")));schema.Assert(newTriple(cat, rdfsLabel, schema.CreateLiteralNode("Chat","fr")));schema.Assert(newTriple(parrot, rdfsLabel, schema.CreateLiteralNode("Perroquet","fr")));// Commentaire sur la classe racineschema.Assert(newTriple(animal, rdfsComment, schema.CreateLiteralNode("Classe racine de tous les animaux","fr")));Console.WriteLine($"Schema RDFS cree : {schema.Triples.Count} triplets");Console.WriteLine();// Afficher la hierarchieConsole.WriteLine("Hierarchie de classes :");foreach(Triple t in schema.GetTriplesWithPredicate(subClassOf)){string child =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string parent =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {child} rdfs:subClassOf {parent}");}
Nous avons créé un schema RDFS complet avec : - 6 classes organisees en hiérarchie - 5 relations rdfs:subClassOf definissant l’arbre taxonomique - 6 etiquettes rdfs:label en francais
Points cles : 1. Chaque classe est declaree comme instance de rdfs:Class via rdf:type 2. rdfs:subClassOf est transitif : Dog < Mammal < Animal, donc Dog < Animal 3. Les etiquettes multilingues utilisent les tags de langue ("fr", "en", etc.)
Ajout de proprietes avec domaine et portee
Definissons des proprietes pour notre taxonomie, avec rdfs:domain (type du sujet) et rdfs:range (type de l’objet/valeur).
// Definir des proprietes avec domaine et porteeIUriNode nameProp = schema.CreateUriNode("ex:name");IUriNode ageProp = schema.CreateUriNode("ex:age");IUriNode soundProp = schema.CreateUriNode("ex:sound");IUriNode canFlyProp = schema.CreateUriNode("ex:canFly");// ex:name - propriete avec domaine Animal, portee xsd:stringschema.Assert(newTriple(nameProp, rdfType, rdfProperty));schema.Assert(newTriple(nameProp, rdfsDomain, animal));schema.Assert(newTriple(nameProp, rdfsRange, schema.CreateUriNode("xsd:string")));schema.Assert(newTriple(nameProp, rdfsLabel, schema.CreateLiteralNode("nom","fr")));// ex:age - propriete avec domaine Animal, portee xsd:integerschema.Assert(newTriple(ageProp, rdfType, rdfProperty));schema.Assert(newTriple(ageProp, rdfsDomain, animal));schema.Assert(newTriple(ageProp, rdfsRange, schema.CreateUriNode("xsd:integer")));schema.Assert(newTriple(ageProp, rdfsLabel, schema.CreateLiteralNode("age","fr")));// ex:sound - propriete avec domaine Animal, portee xsd:stringschema.Assert(newTriple(soundProp, rdfType, rdfProperty));schema.Assert(newTriple(soundProp, rdfsDomain, animal));schema.Assert(newTriple(soundProp, rdfsRange, schema.CreateUriNode("xsd:string")));schema.Assert(newTriple(soundProp, rdfsLabel, schema.CreateLiteralNode("cri","fr")));// ex:canFly - propriete avec domaine Animal, portee xsd:booleanschema.Assert(newTriple(canFlyProp, rdfType, rdfProperty));schema.Assert(newTriple(canFlyProp, rdfsDomain, animal));schema.Assert(newTriple(canFlyProp, rdfsRange, schema.CreateUriNode("xsd:boolean")));schema.Assert(newTriple(canFlyProp, rdfsLabel, schema.CreateLiteralNode("peut voler","fr")));Console.WriteLine($"Schema enrichi : {schema.Triples.Count} triplets");Console.WriteLine();// Afficher les proprietes et leurs contraintesConsole.WriteLine("Proprietes definies :");foreach(Triple t in schema.GetTriplesWithPredicateObject(rdfType, rdfProperty)){string propName =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');// Chercher le domainevar domainTriples = schema.GetTriplesWithSubjectPredicate(t.Subject, rdfsDomain);string domain = domainTriples.Any()?((IUriNode)domainTriples.First().Object).Uri.Fragment.TrimStart('#'):"?";// Chercher la porteevar rangeTriples = schema.GetTriplesWithSubjectPredicate(t.Subject, rdfsRange);string range = rangeTriples.Any()?((IUriNode)rangeTriples.First().Object).Uri.Fragment.TrimStart('#'):"?"; Console.WriteLine($" ex:{propName} domain={domain} range={range}");}
Consequence de rdfs:domain : si on ecrit ex:rex ex:name "Rex", alors un raisonneur RDFS peut inferer que ex:rex rdf:type ex:Animal (car ex:name a pour domaine ex:Animal).
Attention : rdfs:domain et rdfs:range ne sont pas des contraintes de validation (comme en SQL). Ce sont des règles d’inference. Si le domaine est viole, RDFS ne signale pas d’erreur – il infere un type supplementaire.
3. Charger et explorer animals.ttl
Le fichier data/animals.ttl contient la hiérarchie complete avec des instances. Chargeons-le et explorons sa structure.
// Charger le fichier animals.ttlIGraph animals =newGraph();TurtleParser ttlParser =newTurtleParser();ttlParser.Load(animals,"data/animals.ttl");Console.WriteLine($"Graphe charge : {animals.Triples.Count} triplets");Console.WriteLine($"Namespaces : {string.Join(",", animals.NamespaceMap.Prefixes)}");Console.WriteLine();// Lister toutes les classesIUriNode rdfTypeNode = animals.CreateUriNode(newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"));IUriNode rdfsClassNode = animals.CreateUriNode(newUri("http://www.w3.org/2000/01/rdf-schema#Class"));Console.WriteLine("Classes definies :");foreach(Triple t in animals.GetTriplesWithPredicateObject(rdfTypeNode, rdfsClassNode)){string className =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#'); Console.WriteLine($" - {className}");}
Explorons maintenant la hiérarchie de classes et les instances.
// Explorer la hierarchie de classesIUriNode subClassOfNode = animals.CreateUriNode(newUri("http://www.w3.org/2000/01/rdf-schema#subClassOf"));Console.WriteLine("Hierarchie de classes (rdfs:subClassOf) :");foreach(Triple t in animals.GetTriplesWithPredicate(subClassOfNode)){string child =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string parent =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {child} --> {parent}");}Console.WriteLine();// Lister les instances par classeConsole.WriteLine("Instances par classe :");var classes =new[]{"Dog","Cat","Parrot"};foreach(string cls in classes){ IUriNode classNode = animals.CreateUriNode(newUri($"http://example.org/animals#{cls}"));var instances = animals.GetTriplesWithPredicateObject(rdfTypeNode, classNode); Console.WriteLine($" {cls} :");foreach(Triple t in instances){string name =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#'); Console.WriteLine($" - {name}");}}
Hierarchie de classes (rdfs:subClassOf) :
Mammal --> Animal
Bird --> Animal
Dog --> Mammal
Cat --> Mammal
Parrot --> Bird
Instances par classe :
Dog :
- rex
- buddy
Cat :
- minou
Parrot :
- coco
Interpretation : Structure du fichier animals.ttl
Le fichier Turtle contient :
Élément
Quantite
Exemples
Classes
6
Animal, Mammal, Bird, Dog, Cat, Parrot
Proprietes
4
name, age, sound, canFly
Instances
4
rex (Dog), minou (Cat), coco (Parrot), buddy (Dog)
Relations subClassOf
5
Dog < Mammal < Animal, etc.
Observation importante : Les instances sont typees avec leur classe la plus spécifique (ex: ex:rex a ex:Dog), mais pas avec les classes parentes. Un raisonneur RDFS deduira que Rex est aussi un Mammal et un Animal.
4. Inference RDFS
L’inference RDFS permet de deduire automatiquement de nouveaux triplets a partir des règles RDFS. Le mécanisme principal est la transitivite de rdfs:subClassOf.
Les règles d’inference RDFS presentees ci-dessous (rdfs2, rdfs3, rdfs5, rdfs9, rdfs11) sont les règles d’entailment formellement specifiees dans RDF 1.1 Semantics (Hayes & Patel-Schneider, W3C Recommendation 2014). Elles definissent quels triplets peuvent etre deduits a partir d’un graphe RDFS.
ex:rex rdf:type ex:Mammal . # Par rdfs9 (Dog < Mammal)
ex:rex rdf:type ex:Animal . # Par rdfs9 (Mammal < Animal)
ex:Dog rdfs:subClassOf ex:Animal . # Par rdfs11 (transitivite)
Demonstration : inference manuelle
Implementons d’abord l’inference manuellement pour bien comprendre le mécanisme, puis nous utiliserons le raisonneur integre.
// Inference manuelle : heritage de type par rdfs:subClassOf// Recree un graphe simple pour la demonstrationGraph data =newGraph();data.NamespaceMap.AddNamespace("ex",newUri("http://example.org/animals#"));data.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));data.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));IUriNode rdfT = data.CreateUriNode("rdf:type");IUriNode subC = data.CreateUriNode("rdfs:subClassOf");// SchemaIUriNode animalN = data.CreateUriNode("ex:Animal");IUriNode mammalN = data.CreateUriNode("ex:Mammal");IUriNode dogN = data.CreateUriNode("ex:Dog");data.Assert(newTriple(dogN, subC, mammalN));data.Assert(newTriple(mammalN, subC, animalN));// InstanceIUriNode rex = data.CreateUriNode("ex:rex");data.Assert(newTriple(rex, rdfT, dogN));Console.WriteLine("=== AVANT inference ===");Console.WriteLine($"Nombre de triplets : {data.Triples.Count}");Console.WriteLine("Types de rex :");foreach(Triple t in data.GetTriplesWithSubjectPredicate(rex, rdfT)){ Console.WriteLine($" rex rdf:type {((IUriNode)t.Object).Uri.Fragment.TrimStart('#')}");}Console.WriteLine();// --- Inference manuelle : regle rdfs9 ---// Pour chaque "?X rdf:type ?C" et "?C rdfs:subClassOf ?D", ajouter "?X rdf:type ?D"var newTriples =new List<Triple>();bool changed =true;int iteration =0;while(changed){ changed =false; iteration++;var typeTriples = data.GetTriplesWithPredicate(rdfT).ToList();foreach(Triple typeTriple in typeTriples){// typeTriple = (?X rdf:type ?C)var superClasses = data.GetTriplesWithSubjectPredicate(typeTriple.Object, subC);foreach(Triple scTriple in superClasses){// scTriple = (?C rdfs:subClassOf ?D) Triple inferred =newTriple(typeTriple.Subject, rdfT, scTriple.Object);if(!data.ContainsTriple(inferred)){ newTriples.Add(inferred); changed =true;}}}foreach(Triple t in newTriples){ data.Assert(t);}if(newTriples.Count>0){ Console.WriteLine($"Iteration {iteration} : {newTriples.Count} triplet(s) infere(s)");foreach(Triple t in newTriples){string s =((IUriNode)t.Subject).Uri.Fragment.TrimStart('#');string o =((IUriNode)t.Object).Uri.Fragment.TrimStart('#'); Console.WriteLine($" + {s} rdf:type {o}");}} newTriples.Clear();}Console.WriteLine();Console.WriteLine("=== APRES inference ===");Console.WriteLine($"Nombre de triplets : {data.Triples.Count}");Console.WriteLine("Types de rex :");foreach(Triple t in data.GetTriplesWithSubjectPredicate(rex, rdfT)){ Console.WriteLine($" rex rdf:type {((IUriNode)t.Object).Uri.Fragment.TrimStart('#')}");}
=== AVANT inference ===
Nombre de triplets : 3
Types de rex :
rex rdf:type Dog
Iteration 1 : 1 triplet(s) infere(s)
+ rex rdf:type Mammal
Iteration 2 : 1 triplet(s) infere(s)
+ rex rdf:type Animal
=== APRES inference ===
Nombre de triplets : 5
Types de rex :
rex rdf:type Dog
rex rdf:type Mammal
rex rdf:type Animal
Interpretation : Inference manuelle
Sortie obtenue : A partir de 3 triplets initiaux, l’inference a deduit 2 triplets supplementaires.
Itération
Triplet infere
Règle appliquee
1
rex rdf:type Mammal
rdfs9 : rex est Dog, Dog < Mammal
2
rex rdf:type Animal
rdfs9 : rex est Mammal, Mammal < Animal
Points cles : 1. L’algorithme est un point fixe : on itere jusqu’a ce qu’aucun nouveau triplet ne soit infere 2. Chaque itération propage les types d’un niveau dans la hiérarchie 3. Le nombre d’itérations depend de la profondeur de la hiérarchie de classes
En pratique, on utilise les raisonneurs integres plutot que cette implementation manuelle. Mais comprendre le mécanisme est essentiel.
La règle d’inférence rdfs9 (héritage de type par rdfs:subClassOf), rendue sous forme de graphe pour l’instance ex:rex. En trait plein : les 3 triplets assertés ; en tireté ambre : les 2 triplets inférés par transitivité.
graph BT
rex["ex:rex<br/>(instance)"]
Dog["ex:Dog"]
Mammal["ex:Mammal"]
Animal["ex:Animal"]
rex -->|"rdf:type (asserté)"| Dog
Dog -->|"rdfs:subClassOf"| Mammal
Mammal -->|"rdfs:subClassOf"| Animal
rex -.->|"rdf:type (inféré)"| Mammal
rex -.->|"rdf:type (inféré)"| Animal
classDef inst fill:#cfe2ff,stroke:#084298,color:#052c65
classDef cls fill:#d1e7dd,stroke:#0f5132,color:#052e16
class rex inst
class Dog,Mammal,Animal cls
linkStyle 3,4 stroke:#b8860b,stroke-width:2px
Lecture. Partant de 3 triplets (rex rdf:type Dog, Dog ⊑ Mammal, Mammal ⊑ Animal), la règle rdfs9 — « si ?x rdf:type ?C et ?C rdfs:subClassOf ?D, alors ?x rdf:type ?D » — déduit 2 triplets supplémentaires : rex rdf:type Mammal puis, en réappliquant la règle, rex rdf:type Animal. Le point-fixe est atteint quand plus aucun nouveau triplet n’apparaît. C’est exactement la sortie de la cellule d’inférence manuelle ci-dessus (3 initiaux → 2 inférés) et ce que le raisonneur RDFS intégré automatise dans la section suivante.
Utilisation du raisonneur RDFS de dotNetRDF
dotNetRDF fournit un StaticRdfsReasoner qui applique l’ensemble des règles RDFS.
using VDS.RDF.Query.Inference;// Charger animals.ttl dans un nouveau grapheGraph animalGraph =newGraph();TurtleParser parser =newTurtleParser();parser.Load(animalGraph,"data/animals.ttl");int tripletsBefore = animalGraph.Triples.Count;Console.WriteLine($"Avant inference : {tripletsBefore} triplets");// Verifier les types de rex avant inferenceIUriNode rexNode = animalGraph.CreateUriNode(newUri("http://example.org/animals#rex"));IUriNode typeNode = animalGraph.CreateUriNode(newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"));Console.WriteLine("Types de rex avant inference :");foreach(Triple t in animalGraph.GetTriplesWithSubjectPredicate(rexNode, typeNode)){ Console.WriteLine($" - {t.Object}");}Console.WriteLine();// Appliquer l'inference RDFSStaticRdfsReasoner reasoner =newStaticRdfsReasoner();reasoner.Initialise(animalGraph);// Le schema est dans le meme graphereasoner.Apply(animalGraph);int tripletsAfter = animalGraph.Triples.Count;Console.WriteLine($"Apres inference : {tripletsAfter} triplets (+{tripletsAfter - tripletsBefore} inferes)");Console.WriteLine("Types de rex apres inference :");foreach(Triple t in animalGraph.GetTriplesWithSubjectPredicate(rexNode, typeNode)){ Console.WriteLine($" - {t.Object}");}Console.WriteLine();// Verifier aussi pour coco (Parrot)IUriNode cocoNode = animalGraph.CreateUriNode(newUri("http://example.org/animals#coco"));Console.WriteLine("Types de coco apres inference :");foreach(Triple t in animalGraph.GetTriplesWithSubjectPredicate(cocoNode, typeNode)){ Console.WriteLine($" - {t.Object}");}
Avant inference : 51 triplets
Types de rex avant inference :
- http://example.org/animals#Dog
Apres inference : 59 triplets (+8 inferes)
Types de rex apres inference :
- http://example.org/animals#Dog
- http://example.org/animals#Mammal
- http://example.org/animals#Animal
Types de coco apres inference :
- http://example.org/animals#Parrot
- http://example.org/animals#Bird
- http://example.org/animals#Animal
Interpretation : Raisonneur RDFS
Le StaticRdfsReasoner a enrichi le graphe automatiquement :
Instance
Types explicites
Types inferes
rex
Dog
Mammal, Animal
minou
Cat
Mammal, Animal
coco
Parrot
Bird, Animal
buddy
Dog
Mammal, Animal
Comparaison : - Sans inference : seul le type le plus spécifique est present - Avec inference : tous les super-types sont materialises dans le graphe
Note technique : StaticRdfsReasoner materialise les triplets inferes directement dans le graphe. C’est une approche de materialisation (par opposition au raisonnement a la requête).
5. Interroger les données enrichies par l’inference
Maintenant que le graphe a ete enrichi par le raisonneur RDFS, utilisons SPARQL pour interroger les données inferees.
// Requete SPARQL : trouver tous les animaux (grace a l'inference)string query1 = @"PREFIX ex:<http://example.org/animals#>PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>SELECT ?animal ?name ?typeWHERE {?animal rdf:type ex:Animal .?animal ex:name ?name .?animal rdf:type ?type .FILTER(?type != ex:Animal)}ORDER BY ?name";var dataset =newInMemoryDataset(animalGraph);var queryProcessor =newLeviathanQueryProcessor(dataset);var sparqlParser =newSparqlQueryParser();SparqlQuery q1 = sparqlParser.ParseFromString(query1);SparqlResultSet results1 =(SparqlResultSet)queryProcessor.ProcessQuery(q1);Console.WriteLine("Tous les animaux (grace a l'inference rdf:type Animal) :");Console.WriteLine("---------------------------------------------------");foreach(SparqlResult r in results1){string name = r["name"].ToString();string type =((IUriNode)r["type"]).Uri.Fragment.TrimStart('#'); Console.WriteLine($" {name} (type: {type})");}
La requête ?animal rdf:type ex:Animal retourne tous les animaux, y compris ceux qui sont seulement types comme Dog, Cat ou Parrot. C’est la puissance de l’inference RDFS.
Comparons avec une requête qui exploite la hiérarchie.
// Requete 2 : trouver tous les mammiferesstring query2 = @"PREFIX ex:<http://example.org/animals#>PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>SELECT ?animal ?name ?soundWHERE {?animal rdf:type ex:Mammal .?animal ex:name ?name . OPTIONAL {?animal ex:sound ?sound .}}ORDER BY ?name";SparqlQuery q2 = sparqlParser.ParseFromString(query2);SparqlResultSet results2 =(SparqlResultSet)queryProcessor.ProcessQuery(q2);Console.WriteLine("Tous les mammiferes (Dogs + Cats, grace a l'inference) :");Console.WriteLine("-------------------------------------------------------");foreach(SparqlResult r in results2){string name = r["name"].ToString();string sound = r.HasBoundValue("sound")? r["sound"].ToString():"(inconnu)"; Console.WriteLine($" {name} : {sound}");}Console.WriteLine();// Requete 3 : compter les instances par classestring query3 = @"PREFIX ex:<http://example.org/animals#>PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#>PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>SELECT ?class(COUNT(?instance) AS ?count)WHERE {?class rdf:type rdfs:Class .?instance rdf:type ?class.}GROUP BY ?classORDER BY DESC(?count)";SparqlQuery q3 = sparqlParser.ParseFromString(query3);SparqlResultSet results3 =(SparqlResultSet)queryProcessor.ProcessQuery(q3);Console.WriteLine("Nombre d'instances par classe (avec inference) :");Console.WriteLine("------------------------------------------------");foreach(SparqlResult r in results3){string cls =((IUriNode)r["class"]).Uri.Fragment.TrimStart('#');string count =((ILiteralNode)r["count"]).Value; Console.WriteLine($" {cls} : {count} instance(s)");}
Tous les mammiferes (Dogs + Cats, grace a l'inference) :
-------------------------------------------------------
Buddy^^http://www.w3.org/2001/XMLSchema#string : Woof woof^^http://www.w3.org/2001/XMLSchema#string
Minou^^http://www.w3.org/2001/XMLSchema#string : Miaou^^http://www.w3.org/2001/XMLSchema#string
Rex^^http://www.w3.org/2001/XMLSchema#string : Woof^^http://www.w3.org/2001/XMLSchema#string
Nombre d'instances par classe (avec inference) :
------------------------------------------------
Animal : 4 instance(s)
Mammal : 3 instance(s)
Dog : 2 instance(s)
Bird : 1 instance(s)
Cat : 1 instance(s)
Parrot : 1 instance(s)
Interpretation : Requêtes sur données inferees
Classe
Instances directes
Instances inferees
Total
Animal
0
4 (rex, minou, coco, buddy)
4
Mammal
0
3 (rex, minou, buddy)
3
Bird
0
1 (coco)
1
Dog
2 (rex, buddy)
0
2
Cat
1 (minou)
0
1
Parrot
1 (coco)
0
1
Points cles : 1. La classe Animal n’a aucune instance directe mais 4 par inference 2. Les requêtes SPARQL fonctionnent sur le graphe materialise, incluant les triplets inferes 3. C’est un avantage majeur de RDFS : les données sont flexibles mais interrogeables a différents niveaux d’abstraction
Exercices
Exercice 1 : Créer un vocabulaire RDFS pour une bibliotheque
Créez un schema RDFS pour un domaine de bibliotheque avec : - Classes : Publication, Book, Article, Author, Publisher - Hiérarchie : Book et Article sont des sous-classes de Publication - Proprietes : title (domain: Publication, range: string), writtenBy (domain: Publication, range: Author), publishedBy (domain: Book, range: Publisher), year (domain: Publication, range: integer) - Instances : au moins 2 livres et 1 article avec leurs auteurs - Appliquez le raisonneur RDFS et verifiez que les types sont bien inferes
Exercice : Requête SPARQL avec inference RDFS
Les sections précédentes ont montre le raisonneur RDFS et les requêtes SPARQL. Utilisez le raisonneur pour trouver toutes les instances d’une superclasse.
Indices :
Chargez animals.ttl, appliquez rdfsReasoner.Initialise(g) puis executez SPARQL
?animal rdf:type ex:Animal avec inference retourne aussi les chiens et chats
// Exercice : Requete SPARQL avec inference RDFS// TODO etudiant : chargez animals.ttl et trouvez toutes les instances d'une superclasse//// Etape 1 : charger le fichier animals.ttl dans un graphe// Etape 2 : initialiser le raisonneur RDFS et l'appliquer au graphe// Etape 3 : ecrire une requete SPARQL pour trouver toutes les instances d'ex:Animal// Etape 4 : afficher les resultats (y compris les instances inferees par rdfs:subClassOf)Console.WriteLine("Exercice a completer");
Exercice a completer
// Exercice 1 : Vocabulaire RDFS pour une bibliotheque// Completez le code ci-dessousGraph library =newGraph();library.NamespaceMap.AddNamespace("lib",newUri("http://example.org/library#"));library.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));library.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));library.NamespaceMap.AddNamespace("xsd",newUri("http://www.w3.org/2001/XMLSchema#"));// TODO: Definir les classes (Publication, Book, Article, Author, Publisher)// TODO: Definir la hierarchie (Book < Publication, Article < Publication)// TODO: Definir les proprietes (title, writtenBy, publishedBy, year)// TODO: Creer des instances// TODO: Appliquer le raisonneur RDFS// TODO: Verifier les types inferesConsole.WriteLine("Exercice 1 : a completer");
Exercice 1 : a completer
Exercice 2 : Chaîne d’inference par sous-classes
Créez une hiérarchie de classes plus profonde pour tester la transitivite : - LivingThing > Animal > Vertebrate > Mammal > Primate > Human - Créez une instance alice rdf:type Human - Appliquez l’inference et verifiez que alice est aussi de type LivingThing, Animal, Vertebrate, Mammal et Primate - Comptez le nombre de triplets avant et après inference
// Exercice 2 : Chaine d'inference profonde// Completez le code ci-dessousGraph taxonomyGraph =newGraph();taxonomyGraph.NamespaceMap.AddNamespace("bio",newUri("http://example.org/biology#"));taxonomyGraph.NamespaceMap.AddNamespace("rdf",newUri("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));taxonomyGraph.NamespaceMap.AddNamespace("rdfs",newUri("http://www.w3.org/2000/01/rdf-schema#"));// TODO: Creer la hierarchie LivingThing > Animal > Vertebrate > Mammal > Primate > Human// TODO: Creer une instance alice de type Human// TODO: Afficher les types de alice AVANT inference// TODO: Appliquer le raisonneur RDFS// TODO: Afficher les types de alice APRES inference// TODO: Comparer le nombre de triplets avant/apresConsole.WriteLine("Exercice 2 : a completer");
Exercice 2 : a completer
Resume
Vocabulaire RDFS essentiel
Terme
Utilisation
Exemple
rdfs:Class
Declarer une classe
ex:Dog rdf:type rdfs:Class
rdfs:subClassOf
Hiérarchie de classes
ex:Dog rdfs:subClassOf ex:Mammal
rdfs:subPropertyOf
Hiérarchie de proprietes
ex:father rdfs:subPropertyOf ex:parent
rdfs:domain
Type du sujet
ex:name rdfs:domain ex:Animal
rdfs:range
Type de l’objet/valeur
ex:name rdfs:range xsd:string
rdfs:label
Etiquette lisible
ex:Dog rdfs:label "Chien"@fr
rdfs:comment
Description
ex:Dog rdfs:comment "Un canide domestique"
Ce que nous avons appris
RDFS enrichit RDF avec un vocabulaire pour decrire la structure des données
Les hiérarchies de classes (rdfs:subClassOf) permettent l’heritage de type
L’inference RDFS deduit automatiquement des triplets implicites
Le raisonneur StaticRdfsReasoner de dotNetRDF materialise les triplets inferes
SPARQL sur données inferees permet d’interroger a différents niveaux d’abstraction
Limites de RDFS
RDFS est volontairement simple. Il ne permet pas de : - Définir des classes disjointes (Dog et Cat ne peuvent pas etre la même instance) - Exprimer des cardinalites (une personne a exactement 2 parents) - Declarer des proprietes inverses (teaches / taughtBy) - Exprimer des classes par union/intersection
Pour ces fonctionnalites, il faut passer a OWL (notebook suivant).