Ontopy : programmation orientée ontologie en PythonCe travail a

publicité
Ontopy : programmation orientée ontologie en Python
Ontopy : programmation orientée ontologie
en Python?
Jean-Baptiste Lamy1 , Hélène Berthelot1
LIMICS, Université Paris 13, Sorbonne Paris Cité, 93017 Bobigny, France, INSERM UMRS 1142, UPMC
Université Paris 6, Sorbonne Universités, Paris
[email protected], [email protected]
Résumé : Les ontologies et les modèles objets partagent un vocabulaire commun mais diffèrent
dans leurs utilisations : l’ontologie permet d’effectuer des inférences et les modèles objets sont
utilisés pour la programmation. Il est souvent nécessaire d’interfacer ontologie et programme objet.
Plusieurs approches ont été proposées, de OWL API à la programmation orientée ontologie. Dans
cet article, nous présentons Ontopy, un module de programmation orientée ontologie dynamique en
Python, et nous prendrons pour exemple la comparaison des contre-indications des médicaments.
Mots-clés : Ontologies, Programmation orientée ontologie, Programmation dynamique
1
Introduction
Les ontologies formelles, par exemple au format OWL (Ontology Web Language),
structurent un domaine de connaissance pour réaliser des inférences logiques et relier
les connaissances entre elles. Des éditeurs comme Protégé rendent facile la construction
d’ontologies, mais leur intégration à des logiciels existants est plus compliquée (Goldman
NM, 2003). Il existe des similitudes entre ontologie et modèle objet (Koide et al., 2005) :
les classes, propriétés et individus des ontologies correspondent aux classes, attributs et
instances des modèles objets (Knublauch et al., 2006). Cependant, les principaux outils
comme OWL API (Horridge & Bechhofer, 2011) n’en tirent pas parti : avec ces outils
une classe de l’ontologie ne correspond pas à une classe du langage de programmation.
Ces outils sont par conséquents complexes à mettre en oeuvre et difficilement compatibles
avec les méthodes de développement agile. Une approche différente consisterait à aller
vers le rapprochement, voire l’unification, des ontologies et des modèles objets : c’est la
programmation orientée ontologie (Goldman NM, 2003). Sur un exemple du W3C, cette
approche a permis de réduire de moitié le volume de code source (Knublauch et al., 2006).
Cet article présente Ontopy, un module Python pour la programmation orientée ontologie dynamique. Ontopy permet de créer et manipuler les classes et les instances OWL
comme des objets Python, et de classifier automatiquement des classes et des instances via
un raisonneur externe. Nous présentons ensuite le problème de la comparaison des contreindications des médicaments, que nous réalisons avec une ontologie et un programme
objet. Nous montrerons un exemple d’utilisation d’Ontopy dans ce contexte. OWL API
n’a pas été utilisé car peu adapté à nos méthodes de développement agile, de plus nous
souhaitions réutiliser des outils terminologiques mis au point précédemment en Python
(Lamy et al., 2015). Nous terminerons en comparant notre approche à la littérature.
?. Ce travail a été financé par l’ANSM au travers du projet de recherche VIIIP (AAP-2012-013).
IC 2015
Figure 1 – Architecture générale d’Ontopy.
2
Ontopy : un module Python pour la programmation orientée ontologie
Ontopy est un module Python en logiciel libre (licence GNU LGPL v3, https:
//bitbucket.org/jibalamy/ontopy) pour la programmation orientée ontologie et le développement agile d’application à base d’ontologie. Le langage Python 3.4 a été choisi car
il s’agit d’un langage objet dynamique avec héritage multiple. En particulier, il permet
de changer la classe d’un objet ou les superclasses d’une classe en cours d’exécution, par
exemple suite à la classification, ce que ne permet pas un langage statique comme Java.
Ontopy permet (a) de charger des ontologies au format OWL 2 XML, (b) d’accéder au
contenu de l’ontologie comme s’il s’agissait d’objets Python, (c) de créer des classes OWL
en Python, (d) d’ajouter des méthodes Python aux classes OWL, et (e) d’effectuer la classification automatique des instances, classes et propriétés. Les types de données suivants
sont gérés : booléen, entier, flottant, date, chaı̂ne de caractères.
Ontopy (Figure 1) n’effectue aucune inférence (hors mise à jour des propriétés inverses)
tant que le raisonneur n’est pas appelé explicitement. Ce comportement est similaire à
celui de Protégé. Nous avons utilisé le raisonneur HermiT 1.3.8 (Motik et al., 2009) auquel
nous avons ajouté une option en ligne de commande pour obtenir en sortie la classification
des instances. La classification se fait en 3 étapes : (1) exporter l’ontologie dans un fichier
OWL temporaire, (2) exécuter HermiT sur ce fichier, (3) récupérer la sortie d’HermiT et
appliquer les résultats en changeant les classes des instances et les superclasses des classes.
Python permet de modifier son modèle objet via un système de métaclasses (classe de
classe). La Table 1 montre les méthodes spéciales que nous avons redéfinies pour adapter
le modèle objet Python à OWL. Deux autres différences ont demandé un traitement
particulier : (1) dans une ontologie, une instance peut appartenir à plusieurs classes, ce
que ne permettent pas les langages objets ; dans ce cas, une classe intersection héritant des
différentes classes est créée automatiquement et associée à l’objet, (2) les annotations ne
sont pas héritées dans les ontologies, alors que tous les attributs le sont dans les langages
objets ; c’est pourquoi nous avons placé les annotations dans un dictionnaire à part qui fait
correspondre une entité (ou un triplet) à un second dictionnaire, lequel fait correspondre
les propriétés d’annotation à leurs valeurs.
3
Le problème de la comparaison des contre-indications des médicaments
Le processus complexe de rédaction, structuration et codage des propriétés des médicaments conduit à une grande hétérogénéité dans les bases de données, qui com-
Ontopy : programmation orientée ontologie en Python
Méthode
Effet
Raison de la redéfinition
C. new
Crée un nouvel objet
C. instancecheck
Teste si un objet est une
instance de la classe
Teste si une classe est une
sous-classe de la classe
Calcule l’ordre de résolution des
méthodes (method resolution
order , MRO) notamment en cas
d’héritage multiple
Modifie un attribut de l’objet
Obtient un attribut de l’objet
(appelé uniquement pour les
attributs inexistants)
Combiner la nouvelle classe à la classe
OWL de même nom, si elle existe
Prendre en compte les classes équivalentes
OWL
Prendre en compte les classes équivalentes
OWL
Ne pas déclencher d’erreur en cas de
MRO temporairement incorrect lors du
chargement de l’ontologie (les classes
parentes étant ajoutées une à une)
Mettre à jour les propriétés inverses
Retourner une liste vide si la propriété n’a
pas été renseignée, ou None pour une
propriété fonctionnelle
C. subclasscheck
C.mro
i. setattr
i. getattr
Table 1 – Méthodes spéciales du modèle objet de Python qui ont été redéfinies pour
le rendre compatible avec OWL. Pour chaque méthode est indiqué si elle s’applique aux
classes (C.) ou aux instances (i.), son effet et la raison de sa redéfinition.
Condition clinique
maladie hémorragique
maladie hémorragique acquise
maladie hémorragique constitutionnelle
ticagrélor
CI
Condition clinique
maladie hémorragique
maladie hémorragique acquise
maladie hémorragique constitutionnelle
ticagrélor
CI
CI
CI
aspirine
héparine
CI
CI
CI
aspirine
CI
CI
CI
héparine
CI/ok
ok
CI
Table 2 – Trois contre-indications pour trois médicaments, issues de la base médicament
Thériaque en haut, et telles qu’interprétées par un expert en bas (CI : contre-indiqué, ok :
absence de contre-indication, CI/ok : contre-indiqué dans certaines situations seulement).
plique la comparaison entre médicaments. La Table 2 (haut) montre trois exemples de
situations de contre-indication pour trois médicaments, extraits de la base Thériaque
(http://theriaque.org). Cependant, bien que cela n’apparaisse pas dans ce tableau, le ticagrélor est contre-indiqué avec les maladies hémorragiques acquises et constitutionnelles,
car contre-indiqué dans l’ensemble des maladies hémorragiques (héritage). Et l’aspirine
est contre-indiquée dans les maladies hémorragiques car contre-indiquée à la fois dans
celles acquises et constitutionnelles (partition). Enfin, il est possible de déduire les situations dans lesquelles un médicament n’est pas contre-indiqué, par exemple les maladies
hémorragiques acquises pour l’héparine (à ne pas confondre avec l’absence de mention
de contre-indication dans la base). La Table 2 (bas) montre l’interprétation que ferait un
expert ; nous souhaitons automatiser ce raisonnement.
IC 2015
Nous avons structuré les contre-indications à l’aide d’une ontologie formelle, dans laquelle les conditions cliniques associées aux contre-indications sont décrites par un code
dans une terminologie et un ou plusieurs qualifieurs tels que “acquise”, “constitutionnelle”, “antécédent”,... Ces conditions cliniques sont représentées par des classes et non des
instances, afin de pouvoir prendre en compte les relations est-un existant entre conditions
cliniques (par exemple maladie hémorragique acquise est une maladie hémorragique).
4
Exemple d’utilisation d’Ontopy
Nous donnons ici un exemple d’application d’Ontopy au problème de la comparaison des
contre-indications. Ontopy charge les ontologies à partir des répertoires locaux définis dans
la variable globale onto_path, ou à défaut à partir de leur URL. onto_path se comporte
comme le classpath de Java ou le pythonpath de Python, mais pour les fichiers OWL.
from ontopy import *
onto_path.append("/chemin/local/des/ontos")
onto_ci = get_ontology("http://test.org/onto_ci.owl").load()
#charge /chemin/local/des/ontos/onto_ci.owl ou http://test.org/onto_ci.owl
L’ontologie peut ensuite être utilisée comme un module Python, et la notation pointée usuelle permet d’accéder aux éléments de l’ontologie. Des attributs
(imported_ontologies, classes, properties, etc) permettent de récupérer la liste des
éléments d’un type donné.
onto_ci.Médicament # La classe http://test.org/onto_ci.owl#Médicament
Les classes de l’ontologie peuvent être instanciées en Python. La notation pointée permet
d’accéder aux relations des instances. Les relations fonctionnelles ont une valeur unique,
les autres sont des listes.
aspirine = onto_ci.Médicament("aspirine") # onto_ci.owl#aspirine
aspirine.noms_de_marque = ["Aspirine du Rh^
one", "Aspirine UPSA"]
Il est possible de créer des classes OWL en Python, en héritant de Thing ou d’une classe
fille. Les attributs is_a et equivalent_to sont des listes correspondant aux superclasses
et aux classes équivalentes OWL. Ces listes peuvent contenir des classes, mais aussi des
restrictions portant sur une propriété (définies de manière similaire à Protégé), des énumérations d’instances (one of ), ou plusieurs de ces éléments reliés par des opérateurs logiques
ET (&), OU (|) ou NON (NOT). Les classes présentes dans is_a sont ajoutées aux superclasses Python, en revanche les autres éléments ne sont pas traités comme des classes
par Ontopy. L’exemple ci-dessous crée la classe des maladies hémorragiques acquises, fille
de Condition_clinique, et définie comme équivalente à une condition clinique associée
au terme “maladie hémorragique” et ayant pour qualifieur Acquis.
class Maladie_hémorragique_acquise(onto_ci.Condition_clinique):
equivalent_to = [ onto_ci.Condition_clinique
& onto_ci.a_pour_terme
(SOME, onto_ci.Terme_maladie_hémorragique)
& onto_ci.a_pour_qualifieur(SOME, onto_ci.Acquis) ]
Nous pouvons ensuite créer la première contre-indication et la relier à l’aspirine.
ci1 = onto_ci.Contre_indication()
aspirine.a_pour_contre_indication.append(ci1)
Ontopy : programmation orientée ontologie en Python
Relier cette contre-indication aux maladies hémorragiques acquises est un peu plus compliqué, car il s’agit d’une classe et non d’une instance. Pour cela nous modifions les attributs is_a de la classe Maladie_hémorragique_acquise et de l’instance ci1. L’attribut
is_a d’une instance fonctionne de manière similaire à celui d’une classe, mais contient les
classes auxquels appartient l’instance. Ci-dessous, nous spécifions que la contre-indication
est reliée seulement à des maladies hémorragiques acquises, et que la classe des maladies
hémorragiques acquises est reliée à notre contre-indication.
ci1.is_a.append(
onto_ci.a_pour_condition_clinique(ONLY, Maladie_hémorragique_acquise) )
Maladie_hémorragique_acquise.is_a.append(
onto_ci.est_condition_clinique_de(VALUE, ci1) )
Créons ensuite la classe définie des conditions cliniques contre-indiquées avec l’aspirine.
class Condition_CI_avec_aspirine(onto_ci.Condition_clinique):
equivalent_to = [ onto_ci.Condition_clinique
& onto_ci.est_condition_clinique_de(SOME, onto_ci.Contre_indication
& onto_ci.est_contre_indication_de(VALUE, aspirine) ) ]
Ontopy permet aussi l’ajout de méthodes Python aux classes OWL, en redéfinissant les
classes dans un module Python. Ce module peut être lié à l’ontologie via une annotation, de sorte à être chargé automatiquement avec l’ontologie. L’exemple suivant ajoute
une méthode teste_ci à la classe Médicament. Elle prend en paramètre une classe de
condition clinique et retourne une chaı̂ne de caractères. La méthode récupère la classe des
conditions cliniques contre-indiquées avec le médicament, en se basant sur son nom, et
teste si la condition clinique est une classe fille avec l’opérateur issubclass de Python.
Puis nous lançons le raisonneur et nous affichons les résultats.
class Médicament(Thing):
def teste_ci(self, Condition):
Condition_CI = onto_ci["Condition_CI_avec_" + self.name]
if issubclass(Condition, Condition_CI): return "CI"
[...] # XXX tester si le médicament est OK
onto_ci.sync_reasoner() # Lance HermiT et effectue la classification
print(aspirine.teste_ci(Maladie_hémorragique)) # => "CI"
5
Discussion et conclusion
La programmation orientée ontologie n’est pas une idée nouvelle et le W3C a déjà
suggéré l’intégration de méthodes dans des classes OWL (Knublauch et al., 2006). Des
approches statiques ont été proposées (Kalyanpur et al., 2004; Goldman NM, 2003), qui
génèrent le code source de classes Java ou C# correspondant à une ontologie en OWL. Ces
approches permettent d’accéder à l’ontologie et de vérifier le typage à la compilation, mais
leur nature statique n’est pas adaptée à la classification automatique. Plus récemment,
une approche semi-dynamique en Java (Stevenson & Dobson, 2011) a permis la classification des instances mais pas celle des classes. Une approche dynamique a été proposée en
Common Lisp (Koide et al., 2005), en utilisant un algorithme de subsomption spécifique
pour l’inférence et non un raisonneur externe. Un prototype en Python a aussi été réalisé
(Babik & Hluchy, 2006), mais ne va pas jusqu’à une syntaxe “entièrement Python” pour
IC 2015
définir les restrictions ou les relations. Une troisième approche consiste à concevoir de
nouveaux langages, tel que Go ! (Clark & McCabe, 2006).
Au final, peu d’approches sont allées aussi loin dans l’unification entre modèle objet
et ontologie que la nôtre. Ontopy n’a pas été optimisé en terme de performance car nous
n’en avons pas ressenti le besoin : le temps consommé par la manipulation de l’ontologie
en Python reste négligeable comparé au temps de raisonnement. La totalité de l’ontologie
est chargée en mémoire, ce qui peut poser problème sur des ontologies volumineuses.
Nous avons cependant réussi à charger IDOSCHISTO, une ontologie complexe sur la
schistosomiase (Camara et al., 2014). Une autre limite d’Ontopy est la prise en compte
d’espaces de nom multiples et d’assertions présentes dans une ontologie mais portant sur
des éléments d’une autre ontologie, qui enfreignent le principe d’encapsulation des langages
objets (l’ensemble des informations d’un objet sont placées dans une seule “capsule”).
Les perspectives de développement d’Ontopy incluent (a) la liaison à un triple store,
afin de ne pas charger la totalité des ontologies en mémoire, (b) la traçabilité de l’ontologie
d’origine de chaque assertion, afin de faciliter l’emploi d’ontologies modulaires, ainsi que
(c) la génération automatique de boı̂tes de dialogue pour éditer les instances.
Références
Babik M. & Hluchy L. (2006). Deep Integration of Python with Web Ontology Language.
In Proceedings of the 2nd workshop on scripting for the semantic web, Budva, Montenegro.
Camara G., Despres S. & Lo M. (2014). IDOSCHISTO : une extension de l’ontologie noyau
des maladies infectieuses (IDO-Core) pour la schistosomiase. In Actes du congrès d’Ingénierie
des Connaissances (IC2014), p. 39–50, Clermont-Ferrand, France.
Clark K. L. & McCabe F. G. (2006). Ontology oriented programming in Go. Applied
Intelligence, 24, 3–37.
Goldman NM (2003). Ontology-oriented programming : static typing for the inconsistent
programmer. In Lecture notes in computer science : the SemanticWeb, ISWC, volume 2870,
p. 850–865.
Horridge M. & Bechhofer S. (2011). The OWL API : A Java API for OWL ontologies.
Semantic Web 2, p. 11–21.
Kalyanpur A., Pastor D., Battle S. & Padget J. (2004). Automatic mapping of OWL
ontologies into Java. In Proceedings of the Sixteenth International Conference on Software
Engineering & Knowledge Engineering (SEKE’2004), p. 98–103.
Knublauch H., Oberle D., Tetlow P. & Wallace E. (2006). A Semantic Web Primer
for Object-Oriented Software Developers. W3C Working Group Note.
Koide S., Aasman J. & Haflich S. (2005). OWL vs. Object Oriented Programming. In
the 4th International Semantic Web Conference (ISWC 2005), Workshop on Semantic Web
Enabled Software Engineering (SWESE).
Lamy J. B., Venot A. & Duclos C. (2015). PyMedTermino : an open-source generic API
for advanced terminology services. Stud Health Technol Inform.
Motik B., Shearer R. & Horrocks I. (2009). Hypertableau reasoning for description logics.
Journal of Artificial Intelligence Research, 36, 165–228.
Stevenson G. & Dobson S. (2011). Sapphire : Generating Java Runtime Artefacts from
OWL Ontologies. In Lecture Notes in Business Information Processing, Advanced Information
Systems Engineering Workshops , volume 83, p. 425–436.
Téléchargement