lundi 12 mars 2018

Chapitre 21 : IA Raspberry : petit système expert



Après avoir vu différents aspects de développement en assembleur sur le raspberry, nous allons écrire un programme plus complet  comme divertissement !!!!
Ce programme est fortement inspiré du livre de MME  Virginie MATHIVET « L’intelligence artificielle pour les développeurs »  publié aux éditions ENI.
Dans ce livre les exemples de programmes sont écrits en Java et j’ai donc dû les adapter au langage assembleur. Quoi !! Réécrire un programme en langage objet de haut niveau en langage assembleur !! hé bien pourquoi pas !! et je vais peut-être en décevoir certains mais un objet ce n’est rien d’autre qu’un pointeur associé à une structure de données et une méthode, ce n’est rien qu’une routine à laquelle on passe un pointeur de la structure à traiter.
Pour la théorie des systèmes experts, je vous renvoie au livre en question.
Donc j’ai déjà adapté les règles pour les mettre en français et j’ai adapté le programme pour lire ces règles à partir d’un fichier texte facile à saisir. La syntaxe d’une règle doit être impérativement respectée et est de la forme :
Nomdelaregle :  SI  (nompremisse1=valeur (question)) ET nompremisse2)  ALORS  conclusion
Les prémisses et la conclusion sont considérées comme des faits. La prémisse 1 est de type entier (une valeur entière doit être saisie), la prémisse2 est de type booléen. On peut mettre le caractère ! devant le nom pour indiquer que la prémisse de type booléen doit être fausse. Voici un exemple de règle :
R2 :  SI (Triangle ET Angle_Droit (La figure a-t-elle au moins un angle droit ?)) ALORS Triangle_rectangle
Voici le fichier des règles que j’ai utilisé pour ce développement (et toujours inspiré du livre cité plus haut).
Dans le programme, nous commençons par décrire les structures qui seront utilisées : une pour les faits et une pour les règles ainsi que toutes les constantes.
Dans la section data, nous décrivons les libellés à utiliser et dans la section bss, nous réservons la place pour la base des régles et sa copie, la base des faits, ainsi que les compteurs et pointeurs associés. Dans ce programme, nous n’allons pas utiliser des listes chainées pour stocker les données mais des tableaux. C’est pourquoi nous reservons la place en multipliant la taille de chaque structure (donnée par fait_fin par exemple) par le nombre d’élements estimés.
Dans la section du code, nous commençons par récupérer le nom du fichier des règles passé dans la ligne de commande et nous appelons une routine pour lire ces règles et les stocker dans un buffer. Puis nous initialisons les tableaux, pointeurs et compteurs de chaque entité.
Ensuite nous devons analyser le buffer pour éclater les règles en nom, prémisses et conclusion en fonction des mots clés SI ET ALORS. Nous avons 2 solutions : soit écrire une véritable analyse syntaxique de chaque règles cad découper chaque règles en phrases et mots et définir leur type ou alors analyser chaque caractère du buffer et effectuer un découpage en fonction des caractères rencontrés ? C’est cette dernière solution que j’ai développé ici car plus simple à mettre en œuvre.
Donc dans la routine analyseRegles, nous balayons un à un les caractères du buffer pour eliminer les blancs, trouver le nom de la règle puis le délimiteur : puis le SI pour chercher le nom de la première prémisse, déterminer si c’est une variable entière ou booleenne, convertir sa valeur et préparer l’affichage de la question éventuelle. Dans ce programme, nous ne recopions pas les noms dans un tas ou dans d’autres zones de mémoire, nous nous contentons de conserver le pointeur de début du nom et nous forçons un zéro de fin de chaine lorsque nous arrivons à un délimiteur de fin. Ici cela est possible car le buffer de lecture n’est pas réutilisé pour un autre usage dans ce programme. Les pointeurs sont stockés dans les tableaux définis précédemment en fonction de leur type : règles, prémisses, faits et conclusion. Remarque, l’analyse est succincte et donc de nombreuses erreurs possibles de saisies des règles ne sont pas détectées, il faudrait être plus strict !!  Mais pour assurer un contrôle, nous affichons les règles après leur prise en compte ce qui permet de vérifier si cette partie a bien fonctionné :
Ensuite nous trouvons la partie résolution, qui commence par effectuer une copie de la base des règles, une remise à zéro de la base des faits puis une boucle qui va examiner chaque règle, et en fonction des prémisses, poser les questions nécessaires à la résolution du problème.
Les résultats doivent être affichés triés ce qui est effectué par une routine de tri par insertion.
Voici un exemple de fonctionnement :

Il est très facile d’ajouter de nouvelles règles sans avoir à modifier le programme. Et il est possible de créer plusieurs fichiers de règles sur des domaines différents.

Exercices : réecrire le programme en utilisant des listes chainées pour le stockage des règles et des faits.
                écrire un programme pour implanter un moteur d'inférence en chainage arrière.
                écrire un fichier des règles sur un autre sujet (et pourquoi pas sur la configuration du raspberry !!!).

Aucun commentaire:

Enregistrer un commentaire