01
📖 Introduction
Ce tutoriel vous guide dans la création d'un graphe de connaissances complet, depuis la modélisation jusqu'au déploiement. Nous allons construire ensemble un graphe sur le thème des orateurs et discours historiques.
💡 Prérequis :
- Connaissances de base en RDF et SPARQL
- Python pour les exemples
- Protégé installé (optionnel)
📊 Pipeline de création :
[Sources] → [Extraction] → [Transformation RDF] → [Triple Store] → [SPARQL Endpoint] → [Application]
03
🏗️ Étape 2 : Modéliser l'ontologie
@prefix rdfs: .
@prefix schema: .
@prefix ex: .
# Classes
ex:Orateur a rdfs:Class ;
rdfs:subClassOf schema:Person .
ex:Discours a rdfs:Class ;
rdfs:subClassOf schema:CreativeWork .
ex:Theme a rdfs:Class .
ex:Evenement a rdfs:Class .
# Propriétés
ex:aPrononce a rdf:Property ;
rdfs:domain ex:Orateur ;
rdfs:range ex:Discours .
ex:concerne a rdf:Property ;
rdfs:domain ex:Discours ;
rdfs:range ex:Theme .
ex:cite a rdf:Property ;
rdfs:domain ex:Discours ;
rdfs:range ex:Discours .
💡 Bonnes pratiques :
- Réutilisez des vocabulaires existants (Schema.org, FOAF, Dublin Core)
- Documentez vos classes et propriétés avec rdfs:comment
- Validez votre ontologie avec Protégé
04
📥 Étape 3 : Extraire et transformer les données
Depuis un fichier CSV
import csv
from rdflib import Graph, URIRef, Literal, Namespace
from rdflib.namespace import RDF, RDFS, XSD
# Créer un graphe RDF
g = Graph()
ex = Namespace("https://lemondesemantique.fr/onto/")
schema = Namespace("https://schema.org/")
# Lire le CSV
with open('orateurs.csv', 'r') as f:
reader = csv.DictReader(f)
for row in reader:
orateur = URIRef(ex + row['id'])
g.add((orateur, RDF.type, ex.Orateur))
g.add((orateur, schema.name, Literal(row['nom'])))
g.add((orateur, schema.birthDate, Literal(row['date_naissance'], datatype=XSD.date)))
# Sauvegarder
g.serialize(destination="orateurs.rdf", format="turtle")
Extraction depuis JSON
import json
with open('discours.json', 'r') as f:
discours_data = json.load(f)
for discours in discours_data:
discours_uri = URIRef(ex + discours['id'])
g.add((discours_uri, RDF.type, ex.Discours))
g.add((discours_uri, schema.title, Literal(discours['titre'])))
g.add((discours_uri, schema.date, Literal(discours['date'], datatype=XSD.date)))
# Relation avec l'orateur
orateur_uri = URIRef(ex + discours['orateur_id'])
g.add((orateur_uri, ex.aPrononce, discours_uri))
05
💾 Étape 4 : Stocker le graphe (Triple Store)
Option 1 : Fichier RDF (démarrage rapide)
# Sauvegarder en Turtle
g.serialize(destination="knowledge_graph.ttl", format="turtle")
# Sauvegarder en RDF/XML
g.serialize(destination="knowledge_graph.rdf", format="xml")
Option 2 : Apache Jena Fuseki (serveur SPARQL)
# Lancer Fuseki
./fuseki-server --update --mem /ds
# Upload du fichier RDF via l'interface web
# http://localhost:3030
# Ou via curl
curl -X POST http://localhost:3030/ds/data \
-F "file=@knowledge_graph.ttl"
Option 3 : Neo4j avec plugin neosemantics
# Installer le plugin neosemantics
# Importer le RDF
CALL n10s.rdf.import.fetch("file:///knowledge_graph.ttl", "Turtle");
06
🔍 Étape 5 : Interroger le graphe avec SPARQL
# Requête : Tous les discours de Charles de Gaulle
PREFIX ex:
PREFIX schema:
SELECT ?titre ?date
WHERE {
ex:CharlesDeGaulle ex:aPrononce ?discours .
?discours schema:title ?titre ;
schema:date ?date .
}
ORDER BY ?date
# Requête : Graphe de citations (2 niveaux)
SELECT ?discours1 ?discours2 ?discours3
WHERE {
?discours1 ex:cite ?discours2 .
?discours2 ex:cite ?discours3 .
}
LIMIT 20
# Requête : Orateurs et nombre de discours
SELECT ?nom (COUNT(?discours) AS ?nbDiscours)
WHERE {
?orateur a ex:Orateur ;
schema:name ?nom ;
ex:aPrononce ?discours .
}
GROUP BY ?orateur ?nom
ORDER BY DESC(?nbDiscours)
💡 Testez vos requêtes : Utilisez l'interface web de Fuseki (http://localhost:3030) ou le playground SPARQL en ligne.
07
📊 Étape 6 : Visualiser le graphe
Avec Python (NetworkX + matplotlib)
import networkx as nx
import matplotlib.pyplot as plt
from rdflib import Graph
# Charger le graphe RDF
g = Graph()
g.parse("knowledge_graph.ttl", format="turtle")
# Convertir en NetworkX
nx_graph = nx.DiGraph()
for s, p, o in g:
nx_graph.add_edge(str(s), str(o), label=str(p))
# Visualiser
pos = nx.spring_layout(nx_graph, k=2, iterations=50)
plt.figure(figsize=(20, 15))
nx.draw(nx_graph, pos, node_size=500, font_size=8, with_labels=True)
plt.show()
Avec pyvis (interactif)
from pyvis.network import Network
net = Network(height="750px", width="100%", notebook=True)
for u, v, data in nx_graph.edges(data=True):
net.add_node(str(u), label=u.split('/')[-1])
net.add_node(str(v), label=v.split('/')[-1])
net.add_edge(str(u), str(v), title=data['label'])
net.show("knowledge_graph.html")
📊 Outils de visualisation :
- WebVOWL : Visualisation d'ontologies
- Neo4j Browser : Intégré à Neo4j
- GraphDB Workbench : Visualisation interactive
- Pyvis : Graphes interactifs en HTML
08
🚀 Étape 7 : Déployer en production
API REST avec Python
from flask import Flask, request, jsonify
from SPARQLWrapper import SPARQLWrapper, JSON
app = Flask(__name__)
sparql = SPARQLWrapper("http://localhost:3030/ds/sparql")
@app.route('/api/speakers')
def get_speakers():
query = """
PREFIX schema:
SELECT ?id ?nom WHERE {
?orateur a ex:Orateur ;
schema:name ?nom .
BIND(STR(REPLACE(STR(?orateur), "https://lemondesemantique.fr/onto/", "")) AS ?id)
}
"""
sparql.setQuery(query)
sparql.setReturnFormat(JSON)
results = sparql.query().convert()
return jsonify(results['results']['bindings'])
@app.route('/api/speeches/')
def get_speeches(speaker_id):
query = f"""
PREFIX schema:
SELECT ?titre ?date WHERE {{
ex:{speaker_id} ex:aPrononce ?discours .
?discours schema:title ?titre ;
schema:date ?date .
}}
"""
# ... exécution et retour
return jsonify(results)
if __name__ == '__main__':
app.run(port=5000)