Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici

Raisonnement automatique

Introduction

Dans ce chapitre, nous verrons comment utiliser les raisonneurs HermiT et Pellet en Python, afin de vérifier la cohérence d’une ontologie et d’effectuer des déductions et de la classification automatiques à partir des constructeurs.

Disjonctions

Owlready permet de créer des disjonctions entre classes avec la classe AllDisjoint. Par exemple, nous pouvons déclarer les classes Isolée, ParDeux, EnAmas et EnChaîne disjointes deux à deux de la manière suivante :

>>> from owlready2 import *  
>>> onto = get_ontology("bacterie.owl").load()  
 
>>> AllDisjoint([onto.Isolé, onto.ParDeux, onto.EnAmas, onto.EnChaîne]) 

Notez que Olwready, tout comme Protégé, ne distingue pas les disjonctions entre deux entités des disjonctions deux à deux entre plusieurs entités, contrairement à OWL. Olwready choisira automatiquement la bonne méthode OWL selon le nombre d’entités déclarées disjointes. De plus, AllDisjoint fonctionne aussi avec une liste de propriétés (propriétés disjointes) ou une liste d’individus (individus différents).

Il est possible de retrouver les disjonctions d’une classe avec la méthode disjoints(), qui retourne un générateur pour lister les AllDisjoint concernant une entité donnée. Ensuite, l’attribut entities permet d’obtenir la liste des entités déclarées disjointes.

Raisonnement en monde ouvert

La fonction sync_reasoner() permet d’exécuter le raisonneur et d’appliquer automatiquement les faits déduits dans le quadstore. Par défaut, le raisonneur HermiT est utilisé. Les fonctions sync_reasoner_pellet() et sync_reasoner_hermit() permettent de spécifier le raisonneur sachant que Pellet et Hermit fonctionnent de la même manière dans Owlready.

Par exemple, dans l’ontologie des bactéries commençons par vérifier la classe à laquelle appartient l’individu bactérie_inconnue :

>>> onto.bactérie_inconnue.__class__  
bacterie.Bactérie 

Nous exécutons ensuite le raisonneur :

>>> sync_reasoner()  
* Owlready2 * Running HermiT...  
   java [...]  
* Owlready2 * HermiT took 0.5354642868041992 seconds  
* Owlready * Reparenting bacterie.bactérie_inconnue [...] 

Par défaut, Owlready affiche la ligne de commande du raisonneur et les reclassements effectués (le paramètre debug = 0 permet d’éviter cet affichage).

Nous constatons que l’individu bactérie_inconnue a bien été reclassé dans la classe Staphylocoque, comme il l’avait été dans Protégé :

>>> onto.bactérie_inconnue.__class__  
bacterie.Staphylocoque 

Les faits déduits par le raisonneur...

Raisonnement en monde fermé ou en monde fermé local

Les raisonneurs OWL fonctionnent selon l’hypothèse du monde ouvert : tout ce qui n’est pas interdit est considéré comme possible (voir chapitre Les ontologies OWL, section Raisonnement automatique). Cependant, il est souvent souhaitable de limiter le raisonnement aux seuls faits connus, pour l’ensemble de l’ontologie ou pour certaines entités. Il s’agit alors d’un raisonnement en monde fermé, parfois aussi appelé négation par défaut (negation as failure), c’est-à-dire que tout ce qui n’est pas explicitement connu est considéré comme faux.

La fonction close_world() permet de « fermer le monde » et de limiter le raisonnement aux faits présents dans l’ontologie, pour l’individu, la classe ou l’ontologie passé en argument. Cette fonction ajoute automatiquement les contraintes nécessaires, sous forme de constructeurs. On parle de raisonnement en monde fermé lorsque toute l’ontologie est fermée, et de raisonnement en monde fermé local lorsque seules certaines entités le sont.

Nous avions déjà rencontré un problème de monde ouvert ou fermé avec la classe Streptocoque au chapitre Les ontologies OWL (voir le point 2 à la fin de la section Raisonnement automatique) :...

Classes et ontologies inconsistantes

Lors du raisonnement, il se peut que le raisonneur détecte des classes inconsistantes. Ces classes sont illogiques et, par conséquent, il ne doit pas y avoir d’individu appartenant à celles-ci. Par exemple dans notre ontologie des bactéries, la classe suivante, héritant de la classe Streptocoque et associé à une forme Allongée (via une restriction), serait inconsistante :

>>> with onto:  
...     class StreptocoqueAllongé(onto.Streptocoque):  
...         is_a = [onto.a_pour_forme.some(onto.Allongée)] 

Tant qu’il n’y a pas d’individu appartenant à cette classe, cela ne pose pas de problème. Les classes inconsistantes sont reclassées comme équivalente à Nothing par le raisonneur. Il est donc possible de les détecter en Python en cherchant Nothing dans les classes équivalentes, par exemple pour vérifier si la classe StreptocoqueAllongé est consistante :

>>> sync_reasoner()  
>>> if Nothing in StreptocoqueAllongé.equivalent_to:  
...         print("La classe est inconsistante !")  
... else:  
...         print("La classe est consistante.")  
La classe est inconsistante ! 

De plus, la méthode default_world.inconsistent_classes()...

Restriction et raisonnement sur les nombres et les chaînes de caractères

La classe ConstrainedDatatype permet de créer un type de données contraint selon une ou plusieurs facettes, par exemple un entier positif ou une chaîne de caractères d’au moins 3 caractères. La syntaxe est la suivante :

ConstrainedDatatype(type_de_donnée_de_base, facette1 = valeur1, 
                                           facette2 = valeur2,...) 

type_de_donnée_de_base est le type de donnée initial, par exemple int, float, bool, str, norm_str,... (voir tableau du chapitre Accéder aux ontologies en Python - section Relations).

Les facettes suivantes sont proposées par XMLSchema :

  • Pour les types numériques :

  • max_inclusive : valeur maximum, incluse (la valeur doit être ≥ à la valeur indiquée).

  • max_exclusive : valeur maximum, non incluse (la valeur doit être > à la valeur indiquée).

  • min_inclusive : valeur minimum, incluse (la valeur doit être ≤ à la valeur indiquée).

  • min_exclusive : valeur minimum, non incluse (la valeur doit être < à la valeur indiquée).

  • total_digits : nombre de chiffres présents en tout.

  • fraction_digits : nombre de chiffres présents après la virgule.

  • Pour les chaînes de caractères :

  • length : nombre exact de caractères.

  • min_length : nombre de caractères minimum.

  • max_length : nombre de caractères maximum.

  • pattern : une expression...

Règles SWRL

SWRL (Semantic Web Rule Language) est un langage qui permet d’intégrer des règles d’inférences dans les ontologies. Les règles peuvent être écrites dans l’éditeur Protégé ou bien en Python, à l’aide d’Owlready, et ensuite exécutées via les raisonneurs HermiT ou Pellet intégrés.

Dans l’ontologie des bactéries, l’exemple de règle suivant permet de classer comme étant des Staphylocoques toutes les bactéries Gram positives de forme ronde et regroupées en amas :

Bactérie(?b),  
gram_positif(?b, true),  
a_pour_forme(?b,?f), Ronde(?f)  
a_pour_regroupement(?b,?r), EnAmas(?r)  
-> Staphylocoque(?b) 

1. La syntaxe SWRL

Une règle SWRL comprend une ou plusieurs conditions et une ou plusieurs conséquences, séparées par une flèche « -> » (caractères moins et plus grand que). Si la règle possède plusieurs conditions ou conséquences, elles sont séparées entre elles par une virgule « , » qui a le sens d’un « et » logique. Les éléments qui composent les conditions et les conséquences sont appelés atomes.

De plus, les règles SWRL utilisent des variables, dont les noms commencent par « ? », par exemple « ?x ». Ces variables peuvent représenter des individus ou des valeurs (nombres entiers, nombres réels, chaînes de caractères, booléens...), mais jamais des classes ou des propriétés.

Les atomes disponibles sont les suivants :

  • Appartenance à une classe : « Classe(?x) », ce qui signifie :

  • «si l’individu ?x appartient à la classe Classe»(lorsque cet atome est utilisé en tant que condition)

  • « l’individu ?x appartient désormais à la classe Classe » (en plus de sa/ses classes actuelles) (lorsqu’il est utilisé en tant que conséquence)

  • Valeur de propriété objet : « propriété_objet (?x,?y) », qui signifie :

  • « si l’individu ?x a pour propriété_objet ?y »(condition)

  • « ajouter la relation...

Exemple : système d’aide à la décision à base d’ontologie

Un système d’aide à la décision permet d’aider un expert à prendre une décision, par exemple en lui faisant des propositions. Ici, nous nous intéressons à l’identification des bactéries. À partir des caractéristiques observées sur la bactérie (coloration Gram, forme, regroupement...) et des connaissances contenues dans l’ontologie des bactéries, le système essaie d’en déterminer le type. Le système peut également s’abstenir : lorsque les données sont insuffisantes, aucune détermination n’est effectuée.

Ce système d’aide à la décision est réalisé sous la forme d’un site web dynamique avec Flask (que nous avons déjà utilisé au chapitre Accéder aux ontologies en Python, section Exemple : créer un site web dynamique à partir d’une ontologie). Il comprend deux pages : une page de saisie qui contient un formulaire pour décrire la bactérie observée, et une page résultat qui effectue le raisonnement et affiche le résultat.

Le programme suivant crée le site web d’aide à la décision :

# Fichier aide_decision.py  
from owlready2 import *  
onto = get_ontology("bacterie.owl").load()  
 
from flask import Flask, request 
app = Flask(__name__)  
 
@app.route('/')  
def page_saisie():  
   html = """<html><body>  
<h3>Entrez les caractéristiques de la bactérie :</h3>  
<form action="/resultat">  
   Gram :<br/>  
   <input type="radio" name="gram" value="True"/> Positif<br/>  
   <input type="radio" name="gram" value="False"/> Négatif<br/>  
   <br/>  
   Forme :<br/>  ...