SW-7b-Python-OWL

Navigation : Index | << SW-7 C# | SW-8 SHACL >>

Ontologies OWL en Python avec OWLReady2

Ce notebook est un sidetrack optionnel qui presente l’equivalent Python des concepts OWL du notebook SW-7 (dotNetRDF). Vous y decouvrirez OWLReady2, une bibliotheque Python pour manipuler des ontologies OWL et effectuer du raisonnement.

Objectifs d’apprentissage

A la fin de ce notebook, vous saurez : 1. Créer et manipuler des ontologies OWL avec OWLReady2 2. Définir des classes, proprietes et restrictions 3. Utiliser un raisonneur (HermiT) pour deduire de nouvelles connaissances 4. Faire la correspondance entre dotNetRDF et OWLReady2

Prerequis

  • SW-7-CSharp-OWL recommande (pour la comprehension conceptuelle)
  • Python 3.10+
  • Java requis pour le raisonneur HermiT (inclus dans OWLReady2)

Duree estimee : 30 minutes

Note : OWLReady2 inclut le raisonneur HermiT (Java) pour le raisonnement OWL 2 DL complet.


1. Installation et Imports

OWLReady2 est la bibliotheque de reference pour manipuler des ontologies OWL en Python.

# Dependances pre-provisionnees (owlready2) : voir SemanticWeb/requirements.txt ; imports dans les cellules suivantes.

Importation de la bibliotheque OWLReady2 pour la manipulation d’ontologies OWL.

try:
    from owlready2 import *
    import owlready2

    # Limiter la memoire JVM du raisonneur pour compatibilite JVM 32-bit (defaut owlready2 = 2000 Mo)
    owlready2.reasoning.JAVA_MEMORY = 1000

    OWLREADY2_AVAILABLE = True
    print("OWLReady2 importe.")
except ImportError:
    OWLREADY2_AVAILABLE = False
    print("OWLReady2 non disponible. Installez avec : pip install owlready2")
OWLReady2 importe.

2. Créer une Ontologie Simple

Commencons par créer une ontologie pour une famille de personnes, avec des classes et des proprietes.

if OWLREADY2_AVAILABLE:
    # Create a new ontology
    onto = get_ontology("http://example.org/family.owl#")

    # Define classes
    with onto:
        class Person(Thing):
            pass

        class Man(Person):
            pass

        class Woman(Person):
            pass

        # Properties - has_child is NOT Functional (can have multiple children)
        class has_child(ObjectProperty):
            domain = [Person]
            range = [Person]

        class has_parent(ObjectProperty):
            domain = [Person]
            range = [Person]

        # Set the inverse relationship
        has_child.inverse_property = has_parent

        class has_name(DataProperty, FunctionalProperty):
            domain = [Person]
            range = [str]

        class age(DataProperty, FunctionalProperty):
            domain = [Person]
            range = [int, float]

    print("Ontologie creee avec classes :")
    print(f"  Person (Thing subclass)")
    print(f"  Man (Person subclass)")
    print(f"  Woman (Person subclass)")
    print()
    print("Proprietes :")
    print(f"  has_child (ObjectProperty, inverse: has_parent)")
    print(f"  has_name (DataProperty, string)")
    print(f"  age (DataProperty, integer)")
else:
    print("OWLReady2 non disponible : creation d'ontologie ignoree.")
Ontologie creee avec classes :
  Person (Thing subclass)
  Man (Person subclass)
  Woman (Person subclass)

Proprietes :
  has_child (ObjectProperty, inverse: has_parent)
  has_name (DataProperty, string)
  age (DataProperty, integer)

Interpretation : Creation d’ontologie

Syntaxe OWLReady2 : - Thing est la classe racine OWL (equivalent de owl:Thing) - ObjectProperty relie deux individus (comme owl:ObjectProperty) - DataProperty relie un individu a une valeur litterale (comme owl:DatatypeProperty) - FunctionalProperty signifie qu’une entite ne peut avoir qu’une seule valeur (max 1)

OWLReady2 OWL (Turtle) dotNetRDF
class Person(Thing) :Person a owl:Class ; rdfs:subClassOf owl:Thing . new OntologyClass("Person")
ObjectProperty a owl:ObjectProperty new OntologyObjectProperty("has_child")
DataProperty a owl:DatatypeProperty new OntologyDataProperty("age")
domain = [Person] rdfs:domain :Person .Domain = personClass
range = [Person] rdfs:range :Person .Range = personClass

3. Créer des Individus et Assertions

Ajoutons des individus (instances) a notre ontologie.

if OWLREADY2_AVAILABLE:
    # Create individuals
    john = Man("John", has_name="John Smith", age=45)
    mary = Woman("Mary", has_name="Mary Smith", age=42)
    alice = Woman("Alice", has_name="Alice Smith", age=15)

    # Define relationships
    john.has_child.append(alice)
    mary.has_child.append(alice)

    print("Individus crees :")
    print(f"  {john.name} : {john.has_name}, {john.age} ans")
    print(f"  {mary.name} : {mary.has_name}, {mary.age} ans")
    print(f"  {alice.name} : {alice.has_name}, {alice.age} ans")
    print()
    print("Relations parent-enfant :")
    print(f"  {john.name} -> has_child -> {alice.name}")
    print(f"  {mary.name} -> has_child -> {alice.name}")
    print()
    print("Propriete inverse (auto-generee) :")
    print(f"  {alice.name} -> has_parent -> {[p.name for p in alice.has_parent]}")
else:
    print("OWLReady2 non disponible : creation d'individus ignoree.")
Individus crees :
  John : John Smith, 45 ans
  Mary : Mary Smith, 42 ans
  Alice : Alice Smith, 15 ans

Relations parent-enfant :
  John -> has_child -> Alice
  Mary -> has_child -> Alice

Propriete inverse (auto-generee) :
  Alice -> has_parent -> ['John', 'Mary']

Interpretation : Individus

OWLReady2 utilise une syntaxe Python intuitive : - Man("John", ...) créé un individu de la classe Man avec l’IRI #John - Les proprietes sont accessibles comme des attributs Python - Les proprietes inverse sont automatiquement maintenues

Opération OWLReady2 OWL (Turtle)
Créer individu john = Man("John") :John a :Man .
Assert propriete john.has_child.append(alice) :John :has_child :Alice .
Propriete data john.has_name = "John" :John :has_name "John" .

4. Restrictions OWL

OWL permet d’exprimer des restrictions sur les classes. Creons une nouvelle ontologie avec des restrictions.

if OWLREADY2_AVAILABLE:
    # Create a new ontology with restrictions
    onto2 = get_ontology("http://example.org/company.owl#")

    with onto2:
        class Person(Thing):
            pass

        class Department(Thing):
            pass

        class works_in(ObjectProperty):
            domain = [Person]
            range = [Department]

        class manages(ObjectProperty):
            domain = [Person]
            range = [Department]

        # Restriction: Manager is someone who manages at least one department
        class Manager(Person):
            equivalent_to = [Person & manages.some(Department)]

        # Restriction: Employee is someone who works in some department
        class Employee(Person):
            equivalent_to = [Person & works_in.some(Department)]

    print("Classes avec restrictions :")
    print(f"  Manager = Person AND manages SOME Department")
    print(f"  Employee = Person AND works_in SOME Department")
    print()
    print("Restriction OWLReady2 :")
    print(f"  manages.some(Department) = exists manages . Department")
    print(f"  Person & manages.some(Department) = Person AND (exists manages . Department)")
else:
    print("OWLReady2 non disponible : restrictions ignorees.")
Classes avec restrictions :
  Manager = Person AND manages SOME Department
  Employee = Person AND works_in SOME Department

Restriction OWLReady2 :
  manages.some(Department) = exists manages . Department
  Person & manages.some(Department) = Person AND (exists manages . Department)

Interpretation : Restrictions OWL

Restriction OWLReady2 OWL (Manchester) Turtle Signification
manages.some(Department) manages some Department owl:someValuesFrom ∃ : au moins 1
manages.only(Department) manages only Department owl:allValuesFrom ∀ : seulement
manages.max(1) manages max 1 owl:maxCardinality ≤ 1
manages.min(1) manages min 1 owl:minCardinality ≥ 1
manages.exactly(1) manages exactly 1 owl:cardinality = 1

Equivalent dotNetRDF : Restrictions avec OntologyClass et expressions OWL.


5. Raisonnement avec HermiT

OWLReady2 inclut le raisonneur HermiT qui peut deduire de nouvelles connaissances a partir de l’ontologie.

if OWLREADY2_AVAILABLE:
    # Create individuals and run reasoning
    import subprocess

    # Check if Java is available for HermiT reasoner
    java_available = False
    try:
        result = subprocess.run(["java", "-version"], capture_output=True, text=True)
        java_available = result.returncode == 0
    except FileNotFoundError:
        pass

    with onto2:
        it_dept = Department("IT")
        hr_dept = Department("HR")

        bob = Person("Bob")
        alice = Person("Alice")
        charlie = Person("Charlie")

        bob.works_in = [it_dept]
        alice.works_in = [hr_dept]
        charlie.manages = [it_dept]
        charlie.works_in = [it_dept]  # Charlie also works in IT

    print("=== Avant raisonnement ===")
    print(f"Bob types : {bob.is_a}")
    print(f"Alice types : {alice.is_a}")
    print(f"Charlie types : {charlie.is_a}")
    print()

    if java_available:
        # Run HermiT reasoner
        with onto2:
            sync_reasoner()

        print("=== Apres raisonnement (HermiT) ===")
        print(f"Bob types : {bob.is_a}")
        print(f"Alice types : {alice.is_a}")
        print(f"Charlie types : {charlie.is_a}")
        print()
        print("Deductions :")
        print(f"  Bob est Employee (car works_in SOME Department)")
        print(f"  Alice est Employee (car works_in SOME Department)")
        print(f"  Charlie est Employee ET Manager (car works_in ET manages)")
    else:
        print("=== Java non disponible - Raisonnement ignore ===")
        print("Le raisonneur HermiT necessite Java pour fonctionner.")
        print("Installez Java pour activer le raisonnement OWL 2 DL complet.")
        print()
        print("Resultats attendus apres raisonnement :")
        print("  Bob -> Employee (car works_in SOME Department)")
        print("  Alice -> Employee (car works_in SOME Department)")
        print("  Charlie -> Employee + Manager (car works_in ET manages)")
else:
    print("OWLReady2 non disponible : raisonnement ignore.")
=== Avant raisonnement ===
Bob types : [company.Person]
Alice types : [company.Person]
Charlie types : [company.Person]
=== Apres raisonnement (HermiT) ===
Bob types : [company.Employee]
Alice types : [company.Employee]
Charlie types : [company.Employee, company.Manager]

Deductions :
  Bob est Employee (car works_in SOME Department)
  Alice est Employee (car works_in SOME Department)
  Charlie est Employee ET Manager (car works_in ET manages)

Interpretation : Raisonnement

Le raisonneur HermiT a deduit : - Bob est Employee : il travaille dans un departement (IT) - Alice est Employee : elle travaille dans un departement (HR) - Charlie est Employee ET Manager : il travaille dans ET dirige un departement

Opération OWLReady2 dotNetRDF
Raisonnement sync_reasoner() (HermiT) OntologyGraph avec RdfsReasoner
Types inférés .is_a (mis a jour) GetTypes() (inclus inférés)
Raisonneur HermiT (OWL 2 DL complet) RDFS + OWL 2 RL limite

Note : HermiT est un raisonneur OWL 2 DL complet, beaucoup plus puissant que le raisonneur RDFS de base de dotNetRDF.


6. Charger une Ontologie Existante

OWLReady2 peut charger des ontologies depuis des fichiers ou des URLs.

if OWLREADY2_AVAILABLE:
    # Load the university ontology (if available)
    import os

    ontology_file = "data/university.owl"

    if os.path.exists(ontology_file):
        # Load existing ontology
        onto_univ = get_ontology("file://" + os.path.abspath(ontology_file)).load()

        print(f"Ontologie chargee : {onto_univ.base_iri}")
        print(f"Classes : {len(list(onto_univ.classes()))}")
        print(f"Individuals : {len(list(onto_univ.individuals()))}")
        print()

        # Show some classes
        print("Classes dans l'ontologie :")
        for cls in list(onto_univ.classes())[:10]:
            print(f"  {cls.name}")
    else:
        print(f"Fichier {ontology_file} non trouve.")
        print("Utilisation de l'ontologie de demonstration interne.")

        # Use our demo ontology
        onto_univ = onto
        print(f"\nOntologie de demonstration : {onto_univ.base_iri}")
        for cls in onto_univ.classes():
            print(f"  {cls.name}")
else:
    print("OWLReady2 non disponible : chargement d'ontologie ignore.")
Ontologie chargee : http://example.org/university#
Classes : 6
Individuals : 6

Classes dans l'ontologie :
  Student
  Course
  Professor
  Person
  Department
  GraduateStudent

7. Exporter une Ontologie

OWLReady2 peut exporter les ontologies dans différents formats.

if OWLREADY2_AVAILABLE:
    # Export ontology to RDF/XML
    onto2.save(file="data/temp_company.owl", format="rdfxml")
    print("Ontologie exportee : data/temp_company.owl")
    print()

    # Also export to Turtle
    onto2.save(file="data/temp_company.ttl", format="ntriples")
    print("Ontologie exportee : data/temp_company.ttl")
    print()

    # Show first 20 lines of the Turtle export
    print("=== Apercu Turtle (20 premieres lignes) ===")
    with open("data/temp_company.ttl", "r") as f:
        for i, line in enumerate(f):
            if i >= 20:
                break
            print(line.rstrip())
else:
    print("OWLReady2 non disponible : export ignore.")
Ontologie exportee : data/temp_company.owl

Ontologie exportee : data/temp_company.ttl

=== Apercu Turtle (20 premieres lignes) ===
<http://example.org/company.owl> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Ontology> .
<http://example.org/company.owl#Person> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
<http://example.org/company.owl#Person> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2002/07/owl#Thing> .
<http://example.org/company.owl#Department> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
<http://example.org/company.owl#Department> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://www.w3.org/2002/07/owl#Thing> .
<http://example.org/company.owl#works_in> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#ObjectProperty> .
<http://example.org/company.owl#works_in> <http://www.w3.org/2000/01/rdf-schema#domain> <http://example.org/company.owl#Person> .
<http://example.org/company.owl#works_in> <http://www.w3.org/2000/01/rdf-schema#range> <http://example.org/company.owl#Department> .
<http://example.org/company.owl#manages> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#ObjectProperty> .
<http://example.org/company.owl#manages> <http://www.w3.org/2000/01/rdf-schema#domain> <http://example.org/company.owl#Person> .
<http://example.org/company.owl#manages> <http://www.w3.org/2000/01/rdf-schema#range> <http://example.org/company.owl#Department> .
<http://example.org/company.owl#Manager> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
<http://example.org/company.owl#Manager> <http://www.w3.org/2000/01/rdf-schema#subClassOf> <http://example.org/company.owl#Person> .
_:2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction> .
_:2 <http://www.w3.org/2002/07/owl#onProperty> <http://example.org/company.owl#manages> .
_:2 <http://www.w3.org/2002/07/owl#someValuesFrom> <http://example.org/company.owl#Department> .
_:3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
_:3 <http://www.w3.org/2002/07/owl#intersectionOf> _:1 .
_:1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> <http://example.org/company.owl#Person> .
_:1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:4 .

8. Tableau de Correspondance dotNetRDF / OWLReady2

Ce tableau recapitule les equivalences pour la manipulation d’ontologies.

Opération dotNetRDF (C#) OWLReady2 (Python)
Créer ontologie new OntologyGraph() get_ontology("uri#")
Créer classe new OntologyClass("Person") class Person(Thing): pass
Sous-classe subClassOf class Person(Thing): pass
ObjectProperty new OntologyObjectProperty("knows") class knows(ObjectProperty): pass
DataProperty new OntologyDataProperty("age") class age(DataProperty): pass
Créer individu g.CreateUriNode("ex:John") + assertions john = Person("John")
Assert propriete g.Assert(new Triple(...)) john.knows.append(alice)
Restriction SOME new SomeValuesRestriction(...) knows.some(Person)
Restriction ONLY new AllValuesRestriction(...) knows.only(Person)
Raisonnement new RdfsReasoner().Apply(graph) sync_reasoner() (HermiT)
Charger fichier FileLoader.Load(graph, file) get_ontology("file://...").load()
Exporter new CompressingTurtleWriter().Save() onto.save(file="...")

Différences philosophiques

Aspect dotNetRDF OWLReady2
Style Triple-based (bas niveau) Class-based (haut niveau)
Modificate Modifications directes de triples Python classes + instances
Raisonneur RDFS + OWL RL HermiT (OWL 2 DL complet)
Performance Rapide pour gros graphes Optimise pour ontologies

Choix : Utilisez dotNetRDF pour des graphes RDF generiques, OWLReady2 pour des ontologies OWL avec raisonnement complexe.


Exemples guides (issus de contributions etudiantes)

Les exercices ci-dessous ont ete resolus et valides a partir de contributions etudiantes ; ils sont conserves comme exemples guides d’utilisation d’OWL et du raisonneur HermiT. De nouveaux exercices a completer sont proposes a la fin du notebook.

Exemple guide 1 : Famille avec restrictions

Ontologie de famille : - Classes : Person, Parent, Child - Propriete : has_child (inverse : has_parent) - Restrictions : Parent = Person qui has_child SOME Person ; Child = Person qui has_parent SOME Person

Résultat : après sync_reasoner, HermiT reclasse Pierre et Marie en Parent, et Lucie en Child.

if OWLREADY2_AVAILABLE:
    # Exemple guide 1 : famille avec restrictions OWL
    # Parent et Child sont definis par equivalent_to ; HermiT reclasse les individus
    from owlready2 import World

    world_family = World()
    onto_family = world_family.get_ontology("http://example.org/family2.owl#")

    with onto_family:
        # Define classes
        class Person(Thing):
            pass

        class has_child(ObjectProperty):
            domain = [Person]
            range = [Person]

        class has_parent(ObjectProperty):
            domain = [Person]
            range = [Person]

        has_child.inverse_property = has_parent

        class Parent(Person):
            equivalent_to = [Person & has_child.some(Person)]

        class Child(Person):
            equivalent_to = [Person & has_parent.some(Person)]

        # Create individuals and test reasoning
        pierre = Person("Pierre")
        marie = Person("Marie")
        lucie = Person("Lucie")

        pierre.has_child.append(lucie)
        marie.has_child.append(lucie)

    print("=== Avant raisonnement ===")
    print(f"Pierre : {[c.name for c in pierre.is_a]}")
    print(f"Marie  : {[c.name for c in marie.is_a]}")
    print(f"Lucie  : {[c.name for c in lucie.is_a]}")

    with onto_family:
        sync_reasoner(world_family, infer_property_values=True)

    print()
    print("=== Apres raisonnement ===")
    print(f"Pierre : {[c.name for c in pierre.is_a]}")
    print(f"Marie  : {[c.name for c in marie.is_a]}")
    print(f"Lucie  : {[c.name for c in lucie.is_a]}")
else:
    print("OWLReady2 non disponible : exemple guide 1 ignore.")
=== Avant raisonnement ===
Pierre : ['Person']
Marie  : ['Person']
Lucie  : ['Person']

=== Apres raisonnement ===
Pierre : ['Parent']
Marie  : ['Parent']
Lucie  : ['Child']

Exemple guide 2 : Graphe de connaissances de films

Ontologie de films : - Classes : Movie, Person (acteur/realisateur), Genre - Proprietes : has_actor, has_director, has_genre - Restriction : ActionMovie = Movie qui has_genre SOME Action

Résultat : après raisonnement, DarkKnight est classe ActionMovie.

if OWLREADY2_AVAILABLE:
    # Exemple guide 2 : ontologie de films
    # ActionMovie = Movie & has_genre.some(Action) ; HermiT classe DarkKnight
    world_movies = World()
    onto_movies = world_movies.get_ontology("http://example.org/movies.owl#")

    with onto_movies:
        class Movie(Thing):
            pass

        class Person(Thing):
            pass

        class Genre(Thing):
            pass

        class has_actor(ObjectProperty):
            domain = [Movie]
            range = [Person]

        class has_director(ObjectProperty):
            domain = [Movie]
            range = [Person]

        class has_genre(ObjectProperty):
            domain = [Movie]
            range = [Genre]

        class Action(Genre):
            pass

        class Drama(Genre):
            pass

        class ActionMovie(Movie):
            equivalent_to = [Movie & has_genre.some(Action)]

        action = Action("ActionGenre")
        drama = Drama("DramaGenre")

        nolan = Person("Nolan")
        bale = Person("Bale")
        hardy = Person("Hardy")

        dark_knight = Movie("DarkKnight")
        dark_knight.has_director.append(nolan)
        dark_knight.has_actor.append(bale)
        dark_knight.has_actor.append(hardy)
        dark_knight.has_genre.append(action)

        interstellar = Movie("Interstellar")
        interstellar.has_director.append(nolan)
        interstellar.has_genre.append(drama)

    print("=== Avant raisonnement ===")
    print(f"DarkKnight   : {[c.name for c in dark_knight.is_a]}")
    print(f"Interstellar : {[c.name for c in interstellar.is_a]}")

    with onto_movies:
        sync_reasoner(world_movies)

    print()
    print("=== Apres raisonnement ===")
    print(f"DarkKnight   : {[c.name for c in dark_knight.is_a]}")
    print(f"Interstellar : {[c.name for c in interstellar.is_a]}")
else:
    print("OWLReady2 non disponible : exemple guide 2 ignore.")
=== Avant raisonnement ===
DarkKnight   : ['Movie']
Interstellar : ['Movie']

=== Apres raisonnement ===
DarkKnight   : ['ActionMovie']
Interstellar : ['Movie']

Resume et perspectives

Ce sidetrack a presente la manipulation d’ontologies OWL en Python avec OWLReady2, en mettant en lumiere la différence de philosophie avec dotNetRDF. Alors que dotNetRDF travaille au niveau des triples (bas niveau), OWLReady2 offre une API orientee classes (haut niveau) ou les ontologies sont définies comme des classes Python heritant de Thing, les proprietes comme des sous-classes d’ObjectProperty ou DataProperty, et les restrictions OWL s’expriment naturellement (manages.some(Department) pour someValuesFrom).

Le point fort d’OWLReady2 est l’integration du raisonneur HermiT (OWL 2 DL complet), qui permet de deduire automatiquement de nouvelles connaissances. Les exemples guides ont illustre cette puissance : classification automatique d’individus en Employee ou Manager selon leurs proprietes, classification en Parent/Child via les restrictions equivalent_to, et même la categorisation de films en ActionMovie selon leur genre. L’exemple de synthese sur les ecosystemes a combine hiérarchie de classes, restrictions et raisonnement pour classifier le renard et le loup comme carnivores.

Dans le notebook suivant (SW-8-Python-SHACL), nous aborderons la validation de données avec pySHACL, puis SW-9-Python-JSONLD presentera le format JSON-LD pour l’integration web, avant le notebook SW-11-Python-KnowledgeGraphs qui combine ces technologies pour construire des graphes de connaissances.

Concepts cles

Concept Ce que vous avez appris
OWLReady2 Manipuler des ontologies OWL en Python
Classes/Proprietes Définir la structure d’une ontologie
Restrictions some (someValuesFrom), all (allValuesFrom), cardinalites
HermiT Raisonneur OWL 2 DL complet
Correspondance Equivalences dotNetRDF / OWLReady2

Prochaines étapes

  • SW-8-Python-SHACL : Validation de données avec pySHACL
  • SW-9-Python-JSONLD : Données structurees pour le web
  • SW-11-Python-KnowledgeGraphs : Graphes de connaissances avec kglab

Retour au sommaire : Index SemanticWeb


Exemple guide de synthese : Ontologie avec raisonnement

Ontologie d’ecosysteme construite avec owlready2, raisonnee avec HermiT.

  • 3 classes avec hiérarchie : Animal, Mammifere, Carnivore
  • 1 restriction : Carnivore = Animal qui has_prey SOME Animal
  • Instances dont le type est derive par inference (Renard, Loup -> Carnivore)
if OWLREADY2_AVAILABLE:
    # Exemple guide de synthese : ontologie d'ecosystemes avec raisonnement
    # Carnivore = Animal & has_prey.some(Animal) ; HermiT classe Renard et Loup
    from owlready2 import get_ontology, Thing, ObjectProperty, sync_reasoner, World

    # Etape 1 : creer l'ontologie avec get_ontology
    world_eco = World()
    onto_eco = world_eco.get_ontology("http://example.org/ecosystem.owl#")

    # Etape 2 : definir les classes et proprietes (Animal, Mammifere, Carnivore, has_prey)
    with onto_eco:
        class Animal(Thing):
            pass

        class Mammifere(Animal):
            pass

        class has_prey(ObjectProperty):
            domain = [Animal]
            range = [Animal]

        class Carnivore(Animal):
            equivalent_to = [Animal & has_prey.some(Animal)]

        # Etape 3 : ajouter des instances
        lapin = Mammifere("Lapin")
        souris = Mammifere("Souris")
        renard = Mammifere("Renard")
        loup = Mammifere("Loup")

        renard.has_prey.append(lapin)
        loup.has_prey.append(souris)
        loup.has_prey.append(lapin)

    print("=== Avant raisonnement ===")
    for ind in [lapin, souris, renard, loup]:
        print(f"  {ind.name} : {[c.name for c in ind.is_a]}")

    # Etape 4 : lancer le raisonneur sync_reasoner_pellet et afficher les types inferes
    with onto_eco:
        sync_reasoner(world_eco)

    print()
    print("=== Apres raisonnement ===")
    for ind in [lapin, souris, renard, loup]:
        print(f"  {ind.name} : {[c.name for c in ind.is_a]}")

    # Etape 5 (bonus) : exporter l'ontologie
    onto_eco.save(file="data/temp_ecosystem.owl", format="rdfxml")
    print()
    print("Ontologie exportee : data/temp_ecosystem.owl")
else:
    print("OWLReady2 non disponible : exemple de synthese ignore.")
=== Avant raisonnement ===
  Lapin : ['Mammifere']
  Souris : ['Mammifere']
  Renard : ['Mammifere']
  Loup : ['Mammifere']

=== Apres raisonnement ===
  Lapin : ['Mammifere']
  Souris : ['Mammifere']
  Renard : ['Mammifere', 'Carnivore']
  Loup : ['Mammifere', 'Carnivore']

Ontologie exportee : data/temp_ecosystem.owl

Exemple guide 3 : Ontologie de LLM

Ontologie de modèles d’intelligence artificielle : - Classes : AIModel, LLM, Organization, Benchmark, Capability (avec sous-classes ImageUnderstanding, CodeGeneration, Reasoning) - Proprietes : developed_by, has_capability, evaluated_on, outperforms - Restrictions : MultimodalLLM = LLM qui has_capability SOME ImageUnderstanding ; CodingLLM = LLM qui has_capability SOME CodeGeneration ; ReasoningLLM = LLM qui has_capability SOME Reasoning

Résultat : après raisonnement, HermiT classe Claude comme ReasoningLLM + CodingLLM + MultimodalLLM, GPT4 comme CodingLLM + MultimodalLLM, Gemini comme ReasoningLLM + MultimodalLLM, et Llama comme CodingLLM.

Contribue par louisparmentiermichelet-hue (PR #1932, cohorte 2026)

if OWLREADY2_AVAILABLE:
    # Exemple guide 3 : ontologie de LLM avec raisonnement
    # Contribue par louisparmentiermichelet-hue (PR #1932, cohorte 2026)
    # MultimodalLLM / CodingLLM / ReasoningLLM definis par equivalent_to ; HermiT classe les modeles
    from owlready2 import World

    llm_world = World()
    onto_llm = llm_world.get_ontology("http://example.org/llm.owl#")

    with onto_llm:
        # Classes de base
        class AIModel(Thing):
            pass

        class LLM(AIModel):
            pass

        class Organization(Thing):
            pass

        class Benchmark(Thing):
            pass

        class Capability(Thing):
            pass

        # Proprietes
        class developed_by(ObjectProperty):
            pass

        class has_capability(ObjectProperty):
            pass

        class evaluated_on(ObjectProperty):
            pass

        class outperforms(ObjectProperty):
            pass

        # Sous-classes de Capability
        class ImageUnderstanding(Capability):
            pass

        class CodeGeneration(Capability):
            pass

        class Reasoning(Capability):
            pass

        # Classes definies par restrictions equivalent_to
        class MultimodalLLM(LLM):
            equivalent_to = [LLM & has_capability.some(ImageUnderstanding)]

        class CodingLLM(LLM):
            equivalent_to = [LLM & has_capability.some(CodeGeneration)]

        class ReasoningLLM(LLM):
            equivalent_to = [LLM & has_capability.some(Reasoning)]

    # Individus
    with onto_llm:
        org_anthropic = Organization("Anthropic")
        org_openai = Organization("OpenAI")
        org_google = Organization("Google")
        org_meta = Organization("Meta")

        cap_image = ImageUnderstanding("cap_image")
        cap_code = CodeGeneration("cap_code")
        cap_reason = Reasoning("cap_reason")

        bench_mmlu = Benchmark("MMLU")
        bench_humaneval = Benchmark("HumanEval")

        claude = LLM("Claude")
        claude.developed_by = [org_anthropic]
        claude.has_capability = [cap_image, cap_code, cap_reason]
        claude.evaluated_on = [bench_mmlu]

        gpt4 = LLM("GPT4")
        gpt4.developed_by = [org_openai]
        gpt4.has_capability = [cap_image, cap_code]
        gpt4.evaluated_on = [bench_mmlu, bench_humaneval]

        gemini = LLM("Gemini")
        gemini.developed_by = [org_google]
        gemini.has_capability = [cap_image, cap_reason]

        llama = LLM("Llama")
        llama.developed_by = [org_meta]
        llama.has_capability = [cap_code]

        AllDifferent([claude, gpt4, gemini, llama,
                      org_anthropic, org_openai, org_google, org_meta,
                      cap_image, cap_code, cap_reason,
                      bench_mmlu, bench_humaneval])

    print("=== Avant raisonnement ===")
    for m in [claude, gpt4, gemini, llama]:
        print(f"  {m.name:10} types : {[c.name for c in m.is_a]}")
    print()

    with onto_llm:
        sync_reasoner(llm_world, infer_property_values=True)

    print("=== Apres raisonnement (HermiT) ===")
    for m in [claude, gpt4, gemini, llama]:
        print(f"  {m.name:10} types : {[c.name for c in m.is_a]}")
        print(f"  Organisation : {[o.name for o in m.developed_by]}")

    onto_llm.save(file="data/temp_llm_ontology.owl", format="rdfxml")
    print()
    print("Ontologie exportee : data/temp_llm_ontology.owl")
else:
    print("OWLReady2 non disponible : exemple guide 3 ignore.")
=== Avant raisonnement ===
  Claude     types : ['LLM']
  GPT4       types : ['LLM']
  Gemini     types : ['LLM']
  Llama      types : ['LLM']

=== Apres raisonnement (HermiT) ===
  Claude     types : ['ReasoningLLM', 'CodingLLM', 'MultimodalLLM']
  Organisation : ['Anthropic']
  GPT4       types : ['CodingLLM', 'MultimodalLLM']
  Organisation : ['OpenAI']
  Gemini     types : ['ReasoningLLM', 'MultimodalLLM']
  Organisation : ['Google']
  Llama      types : ['CodingLLM']
  Organisation : ['Meta']

Ontologie exportee : data/temp_llm_ontology.owl

Exercice 1 : Vehicules electriques (equivalent_to + raisonnement)

Construisez une ontologie de vehicules : - Classes : Vehicle, Battery, ElectricVehicle - Propriete : has_battery - Restriction : ElectricVehicle = Vehicle qui has_battery SOME Battery

Créez un vehicule muni d’une batterie, lancez le raisonneur et verifiez qu’il est classe ElectricVehicle.

Indice : sur le modèle de l’exemple guide 1, utilisez equivalent_to = [Vehicle & has_battery.some(Battery)] puis sync_reasoner.

# Exercice 1 : vehicules electriques (equivalent_to + raisonnement)
# TODO etudiant : definir Vehicle, Battery, ElectricVehicle = Vehicle & has_battery.some(Battery)
# Indice : creer un World(), une ontologie, puis sync_reasoner et verifier is_a

print("Exercice a completer")
Exercice a completer

Exercice 2 : Classes disjointes

Dans une ontologie d’animaux, declarez que Herbivore et Carnivore sont disjointes (AllDisjoint). Créez un individu, puis observez le comportement du raisonneur (il ne doit pas inferer qu’un même individu est a la fois Herbivore et Carnivore).

Indice : utilisez AllDisjoint([Herbivore, Carnivore]) a l’interieur du bloc with onto:.

# Exercice 2 : classes disjointes (AllDisjoint)
# TODO etudiant : declarer Herbivore et Carnivore disjointes via AllDisjoint([...])
# Indice : a placer dans le bloc with onto: , puis creer un individu et raisonner

print("Exercice a completer")
Exercice a completer

Exercice 3 : Restrictions de cardinalite (min, exactly)

Modelisez la classe BigFamily = Person qui has_child au moins 3 Person. Créez ensuite la classe Couple = Person qui has_partner exactement 1 Person.

Créez un individu avec 3+ enfants, lancez le raisonneur et verifiez qu’il est classe BigFamily.

Indice : equivalent_to = [Person & has_child.min(3, Person)] pour le minimum. Utilisez .exactly(N, Class) pour la cardinalite exacte.

# Exercice 3 : restrictions de cardinalite (min, exactly)
# TODO etudiant : definir BigFamily = Person & has_child.min(3, Person)
# Indice : creer un parent avec 3 enfants ; sync_reasoner ; verifier is_a BigFamily

print("Exercice a completer")
Exercice a completer

Exercice 4 : Restriction universelle only (AllValuesFrom)

Modelisez la classe Vegan = Person qui eats uniquement des Plant. Attention : some (existentiel) signifie ‘au moins un’, tandis que only (universel) signifie ‘tous’. Un Vegan n’est PAS forcement défini par ‘mange au moins une plante’ mais par ‘tout ce qu’il mange est une plante’.

Créez deux individus : Alice qui mange uniquement des plantes (Carrot, Apple), et Bob qui mange une plante et un animal (Carrot, Cow). Après raisonnement, seul Alice doit etre classe Vegan.

Indice : equivalent_to = [Person & eats.only(Plant)]. Definissez Plant et Animal comme classes disjointes (AllDisjoint).

# Exercice 4 : restriction universelle only (AllValuesFrom)
# TODO etudiant : Vegan = Person & eats.only(Plant)
# Indice : 2 individus (Alice tout-plantes, Bob plante+animal) ; seul Alice doit etre Vegan

print("Exercice a completer")
Exercice a completer

Exercice 5 : Propriete transitive (TransitiveProperty)

Modelisez la propriete is_part_of comme transitive : si A is_part_of B et B is_part_of C, alors le raisonneur doit inferer A is_part_of C.

Créez les individus Finger, Hand, Arm, Body avec la chaîne : - Finger is_part_of Hand - Hand is_part_of Arm - Arm is_part_of Body

Après sync_reasoner, verifiez que Finger.is_part_of contient bien Body (via transitivite).

Indice : declarer la classe is_part_of(ObjectProperty, TransitiveProperty).

# Exercice 5 : propriete transitive
# TODO etudiant : declarer is_part_of(ObjectProperty, TransitiveProperty)
# Indice : chaine Finger -> Hand -> Arm -> Body ; sync_reasoner ; verifier Finger.is_part_of inclut Body

print("Exercice a completer")
Exercice a completer

Exercices

A votre tour. Completez les exercices ci-dessous (les solutions ne sont pas fournies). Le notebook doit pouvoir s’executer de bout en bout même si les exercices ne sont pas encore completes.

Exercice 6 : Ontologie de domaine libre

Choisissez un domaine de votre choix (musique, sport, cuisine, etc.) et créez une ontologie OWL complete :

  • Au moins 5 classes avec une hiérarchie coherente
  • Au moins 3 proprietes (ObjectProperty ou DataProperty)
  • Des restrictions equivalent_to pour définir des classes par leur comportement
  • Des individus pour tester le raisonnement
  • Utilisez sync_reasoner() pour verifier les inferences

Indice : inspirez-vous des exemples guides 1-3. Definissez vos classes avec equivalent_to = [ClasseParent & propriete.some(ClasseCible)], créez des individus, puis lancez sync_reasoner pour observer les classifications automatiques.

# Exercice 6 : Ontologie de domaine libre
# TODO etudiant : Choisissez un domaine (musique, sport, cuisine, etc.)
# et creez une ontologie OWL avec au moins 5 classes, 3 proprietes,
# des restrictions equivalent_to, et des individus.
# Utilisez sync_reasoner() pour verifier les inferences.

result = None  # TODO etudiant : remplacer par votre ontologie
print("Exercice a completer : ontologie de domaine libre")
Exercice a completer : ontologie de domaine libre

Conclusion

Ce sidetrack a presente la manipulation d’ontologies OWL en Python avec OWLReady2, en mettant en lumiere son approche orientee classes (haut niveau) par rapport a l’approche triple-based de dotNetRDF. Vous avez appris a définir des classes, proprietes et restrictions OWL directement en syntaxe Python, a créer des individus et assertions, et a exploiter le raisonneur HermiT pour deduire automatiquement de nouvelles connaissances (classification en Employee/Manager, Parent/Child, Carnivore).

Les points cles a retenir sont la puissance du raisonnement OWL 2 DL via sync_reasoner() qui reclassifie les individus selon leurs proprietes, les restrictions equivalent_to avec .some() et .only() pour définir des classes par leur comportement, et l’export multi-format (RDF/XML, Turtle) pour l’integration dans d’autres outils. Ces competences preparent a la validation de données avec SHACL et a la construction de graphes de connaissances.

Retour au sommet