InferenceBot/CheckerBot
Le code pour InferenceBot est disponible sur https://github.com/mj2905/InferenceBot. Il suffit de lancer le fichier python main.py, puis de taper autorun lorsqu'une entrée utilisateur est demandée.
La déduction est un principe en intelligence artificielle qui permet d’obtenir de nouvelles informations en se basant sur des faits, et des règles déjà établies. Pour la réalisation de ce projet, nous avons divisé le travail à accomplir en trois parties, chacune pouvant être modulée, afin de pouvoir être étendue par la suite :
- Le “wiki scraping” (récupération des informations), qui nous permet de lire directement récupérer des informations en provenance du wiki. En l'occurrence, pour notre bot, ces informations correspondent aux différents faits qui seront utilisés pour déduire d’autres faits.
- Le moteur d’inférence, qui va inférer des informations à partir de ce qui lui est fourni en entrée.
- Le “wiki writing” (écriture des informations sur le wiki) qui va écrire les faits déduits, considérés comme utiles, dans le wiki.
Le wiki scraping
Les objectifs fixé pour la récupération des données sur le wiki étaient les suivants:
- Modularité: étendre le code du programme pour récupérer davantage de faits doit être facile.
- Flexibilité: de petits écarts dans le format d'une ligne ne doivent pas faire échouer l'extraction des faits.
- Abstraction: la récupération des données ne doit pas présenter de couplage avec le reste du programme.
Pour réaliser les objectifs ci-dessus, nous avons implémenté le bot de la façon suivante. Tout d’abord, le bot récupère la liste des urls récemment modifiées par des utilisateurs agréés. Il regroupe ensuite les adresses en paquet de dix et ouvre un nombre équivalent de connexions HTTP pour récupérer les données relatives aux pages. Pour ouvrir des connexions en parallèle nous créons plusieurs "threads" qui s'exécutent de manière concurrente. La raison qui nous a poussé à choisir cette approche parallélisée est que l'obtention des données demande un temps beaucoup plus élevé que leur traitement. Il est donc judicieux de chercher à en minimiser l'impact.
Une fois les données HTML acquises, le bot les analyse séquentiellement dans le but d'y trouver de potentiels concepts intéressants. Comme mentionné précédemment, le temps requis par ce traitement séquentiel des données est négligeable par rapport au temps nécessaire pour les obtenir. L'analyse des données consiste trouver des correspondance entre des "regular expressions" (abrégées en 'regex') python et des sous parties du texte. L'utilisation des regex nous permet d'obtenir une certaine marge de tolérance vis-à-vis des entrées mal formatées par les utilisateurs. En effet, il est possible d'obtenir des correspondances même lorsque des caractères de formatages tels que ceux utilisés pour les dates: (.-/) sont manquants.
Il est important de noter qu'à ce stade il n'existe pas de lien trivial entre les chaines de caractères extraites du wiki et les objets utilisés par le moteur d'inférence. Afin de découpler le résultat du scraping du fonctionnement du reste du programme, le scraper instancie des objets (au sens de la programmation orientée-objet) correspondant au concepts recherchés. Ces objets servent d'abstraction pour le moteur d'inférence qui peut alors traiter les données sans se soucier de leur format sous-jacent.
Lorsque l'algorithme de scraping prend fin, nous obtenons un objet regroupant l'ensemble des concepts extraits pour une page donnée. Ces objets sont ensuite regroupés dans une nouvelle structure qui représente l'ensemble de tous les concepts extraits pour toutes les pages visitées.
Finalement, puisque le moteur d'inférence n'est pas directement dépendant du processus d'obtention des données, il est facile d'ajouter des concepts à extraire. Nous entendons par là que le code du moteur n'a pas a être modifié, il suffit d'ajouter de nouveaux types d'objets. Nous remplissons ainsi les objectifs fixés pour la récupération des données.
Le moteur d’inférence
Ici, il s’agit d’obtenir des faits à partir d’autres faits et règles. Nous avons utilisé un algorithme d’intelligence artificielle appelé “chaînage avant”, qui nous permet d’effectuer ces déductions. L’algorithme a la forme suivante :
Q = faits existants Solutions = liste vide Règles = règles existantes tant que Q n’est pas vide: fait = premier élément de Q si fait n’est pas dans Solutions: ajouter fait à Solutions # Vérifie si des règles sont déclenchées par le nouveau fait. pour chaque règle dans Règles: si le fait satisfait une des conditions de règle, et que l’ensemble des faits déduits (Solutions) peut satisfaire l’ensemble des autres conditions de cette même règle: ajouter la conclusion de la règle à Q renvoyer Solutions
Cet algorithme utilise plusieurs éléments de base : Les faits, qui sont des éléments basique dans notre algorithme, et qui peuvent contenir des variables. Des exemple de faits seraient : père(Paul, Pierre) ou oncle(?A, Jean) avec ?A une variable. Les règles, qui suivent la logique des prédicats, et doivent appartenir à l’ensemble des clauses de Horn. Cet ensemble correspond à tout prédicat sous la forme d’une disjonction de littéraux qui sont tous négatifs, sauf au plus un des littéraux qui peut être positifs. Cela nous permet d’avoir des règles de la forme not(A) or not(B) or … or Z, ce qui peut être réécrit sous la forme (A et B et …) => Z. Nous commençons donc à comprendre comment et pourquoi nous pouvons ajouter la conclusion de la règle (notre Z) à l’ensemble des faits déduits, si l’ensemble des conditions de cette règle (A, B, …) sont satisfaites.
De plus, comme des variables sont impliquées dans nos règles (en effet, nous ne savons pas à l’avance pour qui/quoi ces règles s’appliqueront), nous devons utiliser un algorithme d’unification, qui va pouvoir mettre ensemble deux faits qui peuvent être unifiés, à la manière d’une reconnaissance de motif (pattern matching), et renvoyer l’association des variables identifiées comme définies.
Le wiki writer
Cette étape nous permet de directement écrire sur le wiki les informations utiles obtenues. Etant donné qu’un grand nombre de faits sont déduits, même des faits intermédiaires sans doutes inutiles, il faut pouvoir trier ce qui est obtenu. Ainsi, nous disons que par défaut, aucun fait n’est écrit, sauf certains spécifiés. C’est pour cette raison que nous ne retrouvons en sortie que les faits Erreur de date, Erreur de recontre, Erreur d’élection… Par la suite, étant donné que nous obtenons des informations utiles, mais pas très lisibles, il faut passer par une étape de transcription de l’erreur brute vers une erreur plus compréhensible pour la personne/le bot qui se chargera de modifier la page par la suite.
Nous avions choisi d’écrire directement dans la page InferenceBot - Output. Cependant, après la discussion du mardi 2 mai, utiliser la page discussions nous a semblé être une bonne idée, étant donné que notre bot serait plus utile si il avertissait directement les personnes concernées, plutôt que de les obliger à venir voir la page contenant toutes les erreurs du wiki, afin de voir s'ils ont des problèmes dans leurs pages. C'est pour cette raison que nous écrivons dans cette page, en suivant le format suivant :
== InferenceBot == * fait 1 * fait 2 ... * fait N ----
Tout ce qui précède et tout ce qui suit cette section est conservé. De plus, si la section n'existe pas dans la page de discussion, le bot va la créer (en conservant encore le reste du contenu) à la page discussion.
De plus, étant donné que nous créons les arbres généalogiques de personnes du wiki, il nous faut écrire ces images sur le wiki. De même que pour les faits inférés, nous écrivons dans une section spéciale de la page discussion de la personne. Cette section s'appelle InferenceBotPicture, et contient toutes les images d'arbres généalogiques contenus dans la page (par exemple, pour la page mariage, il peut y en avoir plusieurs).
Afin de faire cette écriture, il nous faut importer premièrement l'image dans le wiki sous le nom Fichier:*nom_de_l'image*, puis mettre un lien de la forme
[[Fichier:*nom_de_l'image*]]
dans chacune des pages l'utilisant. Pour générer le nom de l'image, nous utilisons une méthode de hash des noms des personnes contenues dans cet arbre généalogique. Ceci fonctionne bien, mais a la fâcheuse tendance d'écrire beaucoup d'images différentes sur le site (ralentissant le bot), et de réécrire plusieurs fois les mêmes images entre les différents lancements du bot. De plus, comme nous n'avons pas de pouvoir de suppression d'images, il ne nous est pas possible de supprimer les nombreuses pages créées quand les images ne sont plus utilisées.
Exemple d’Output
Nous avons créé plusieurs pages de test afin de vérifier le comportement de notre bot. Les pages sont Secundinus_Aurelianus, Pompilius_Iuvenalis et Laurentinus_Porcius.
Nous avons directement pu obtenir les informations suivantes sur les pages Discussion:Secundinus_Aurelianus, Discussion:Pompilius_Iuvenalis et Discussion:Laurentinus_Porcius.
Ainsi, comme nous pouvons le voir sur la page Discussion:Secundinus_Aurelianus, le bot d’inférence a pu en déduire plusieurs erreurs, comme une erreur de naissance (qui devraient se dérouler avant la mort, pas comme ici); une erreur de rencontre, où deux personnes se sont rencontrées en même temps à deux endroits différents; ainsi que plusieurs dates de mort.
Evaluation des performances
Actuellement, les règles du bot d’inférence se focalisent principalement sur des erreurs de contenu (incohérences dans les dates ou des lieux p.ex.). Sachant que les pages pertinentes du wikipast (donc pas celles crées par des attaques de bots) ont été créées méticuleusement par un étudiant, il est rare qu’elle contiennent de telles erreurs. C’est pourquoi le bot n’output pas beaucoup d’erreurs autres que celles provoquées par des pages de test. Cependant, nous pensons qu’il est utile d’avoir un tel bot lorsque le nombre de pages augmente et lorsque des utilisateurs et bots commencent à modifier les pages des autres utilisateurs. En effet, il est beaucoup plus facile de faire des erreurs en modifiant des pages d’autres utilisateurs.
Le nombres d’erreurs détectées et d'inférences dépend aussi du nombre de règles implémentées. Comme indiqué dans la description du bot, il est facile d’ajouter des règles grâce à la modularité de son implémentation. La principale limite de performance sur un grand wiki est donc le nombre de règles que le responsable du bot peut trouver. Cette structure basée sur les règles est aussi un point faible. En effet, le bot construit des prédicats seulement lorsque l'entrée du wiki correspond à un format particulier (ordre des mots par exemple). Le manque de structure de certains types d'entrées rend parfois impossible la création de règle d'inférence.
Concernant le temps nécessaire à l’inférence des faits/erreurs par le bot, il faut savoir que la puissance et le nombre de connexions de l’ordinateur/serveur utilisé sont des facteurs importants. Le bot, lorsqu’il est exécuté sur un ordinateur portable, prend actuellement quelques dizaines de secondes pour scraper tout le Wikipast et inférer les faits/erreurs. Nous avons implémenté l’utilisation de multiples connexions (réglable simplement) pour scraper plus rapidement, car il s’agit d'une partie prenant une grosse partie du temps total d'execution (autour de 15 secondes en conditions réelles). L’inférence des faits/erreurs était peu coûteuse lorsque nous avions peu de règles et de faits, cependant à force d'ajouter un grand nombre de faits, par exemple pour détecter que deux lieux ou deux dates sont différentes, comme le temps d’exécution est polynomial selon le nombre d'entrées, nous avons remarqué un certain ralentissement lors des derniers tests.
Nous avons trouvé que notre bot avait des conflits, surtout avec le bot TangoBot. Les pages de test de notre bot ont des entrées qui se ressemblent à la date ou au lieu près, car nous testons différentes règles qui se concentrent sur les dates, lieux et personnes. TangoBot supprime ce qu'il considère être trop ressemblant et réduit considérablement nos pages.