Traitement de l'information incertaine
======================================

Dans cette série, nous organiserons les exercices en deux parties. La première partie traitera des réseaux bayésiens, mais sans programmation. Le but est de passer en revue les principes du raisonnement probabiliste, la modélisation des liens de causalité et l'inférence dans un réseau bayésien.

Dans la deuxième partie, vous modifierez le moteur d'inférence à chaînage avant avec variables que nous avons développé dans les exercices des chapitres précédents afin de prendre en compte la possibilité d'incertitudes dans les faits et les règles. 

Réseaux Bayésiens
=================

Exercice 1: Raisonnement probabiliste
-------------------------------------

Après votre bilan de santé annuel, vous recevez par courrier les résultats de vos examens : vous avez été testé positif à une maladie grave. Les taux de faux positifs et négatifs sont de 1\%, c'est-à-dire que la probabilité que le test classifie un patient sain comme étant malade ou un malade comme étant sain est de 0.01. Par bonheur, cette maladie est extrêmement rare : elle ne frappe qu'une personne sur dix mille.

À l'aide d'un raisonnement probabiliste, expliquez pourquoi la rareté de la maladie est une chance pour vous. Indication: Utilisez la règle d'inférence bayésienne, *T* = Test et *M* = Maladie constituant les événements concernés.

Exercice 2: Causalité
---------------------

Les liens de causalité expriment des relations du type :math:`si~A=1, alors~B=1`, c'est-à-dire, selon le formalisme de la logique propositionnelle, des règles du type :math:`A\Rightarrow B`, :math:`A` et :math:`B` étant deux prédicats booléens. Lorsque le raisonnement est incertain, la causalité peut être établie via un raisonnement probabiliste:

:math:`(A \Rightarrow B)` devient (:math:`A \Rightarrow B` avec probabilité :math:`P(B|A)`)

L'inférence `bayésienne <http://www.ai.mit.edu/courses/6.825/fall02/pdf/6.825-lecture-15.pdf>`__  consiste alors généralement à partir d'une observation sur :math:`B` afin d'établir la probabilité selon laquelle :math:`A` est vrai, c'est-à-dire que l'on remonte la chaîne de causalité. Cet exercice a pour but de vous donner une idée de la raison pour laquelle cela est généralement le cas.

Considérons une petite histoire. L'inspecteur Smith se trouve à Priory `School <https://en.wikipedia.org/wiki/The_Adventure_of_the_Priory_School>`__ . Il attend le détective Holmes et son ami, le docteur Watson, pour enquêter sur la disparition d'un professeur. Mais ils sont en retard. Tous deux ont la réputation d'être mauvais conducteurs et l'inspecteur Smith se demande si la route est gelée, ce qui leur causerait certainement de gros ennuis. Il téléphone à sa secrétaire, Miss Lovelace, qui l'informe que le Dr Watson a effectivement eu un accident. Voici leur conversation:

*Smith* --- Un accident ? Bien, la route étant probablement gelée, il est probable que Holmes ait aussi eu un accident!

*Lovelace* --- La route, gelée ? Non, certainement pas: il ne fait pas si froid et les routes ont été sablées.

*Smith* --- Pas de chance pour Watson alors. Attendons Holmes encore dix minutes...

Le lendemain, l'épouse de Watson, Mary, lit le journal. Elle découvre l'accident de son mari et apprend que le professeur de Priory School a été sauvé et les ravisseurs arrêtés. Voici ses pensées :

*Mary* --- Ils ont arrêté les ravisseurs? Alors, Holmes n'a probablement pas eu d'accident, donc la route n'était probablement pas gelée.

Elle lit ensuite qu'il ne faisait pas si froid et que les routes avaient été sablées. Elle se dit :

*Mary* --- Décidément, Holmes a évité l'accident.

Modélisation du problème
~~~~~~~~~~~~~~~~~~~~~~~~

Question 2.1 :
______________
 
Formalisez les relations entre les événements de cette petite histoire à l'aide d'un réseau bayésien à quatre noeuds :

* :math:`I` = Route-Gelée
* :math:`H` = Accident-Holmes
* :math:`W` = Accident-Watson
* :math:`S` = Professeur-Sauvé

Question 2.2 (Inférence déductive) :
____________________________________
 
Si l'on connaît la probabilité de l’événement :math:`I`, :math:`P(I)`:

* Comment peut-on calculer les probabilités :math:`P(H)` et :math:`P(W)` des événements :math:`H` et :math:`W` ? De quelles informations faut-il disposer quant aux relations entre :math:`I`, :math:`H` et :math:`W`?
* Comment peut-on calculer la probabilité :math:`P(S)` de l'événement :math:`S` ? Quelles informations nous faut-il sur les relations entre :math:`I`, :math:`H`, :math:`W` et :math:`S` ?

Question 2.3 (Inférence abductive) :
____________________________________
 
Comment peut-on exprimer la probabilité de l’événement :math:`I` lorsqu'on sait si l’événement :math:`W` s'est produit ou pas (:math:`W = 1` ou :math:`W = 0`)? Si l'on connaît :math:`P(I)`, quelles informations nous faut-il sur la relation entre :math:`I` et :math:`W` pour calculer cette probabilité?

Question 2.4 (Déduction et abduction) :
_______________________________________

Si on connaît :math:`P(I)` :

* Comment peut-on exprimer la probabilité de l'événement :math:`H` lorsqu'on sait si l'événement :math:`W` s'est produit ou pas ? Quelles informations nous faut-il sur les relations entre :math:`I`, :math:`W` et :math:`H` pour calculer cette probabilité ?
* Comment peut-on exprimer la probabilité de :math:`H` lorsqu'on sait si les événements :math:`W` et :math:`S` se sont produits ou pas ? Quelles informations nous faut-il sur les relations entre :math:`I`, :math:`H`, :math:`W` et :math:`S` pour calculer cette probabilité ?
* Comment peut-on exprimer la probabilité de :math:`H` lorsqu'on sait si les événements :math:`I`, :math:`W` et :math:`S` se sont produits ou pas ? Quelles informations nous faut-il sur les relations entre :math:`I`, :math:`H`, :math:`W` et :math:`S` pour calculer cette probabilité ?

Calcul probabiliste
~~~~~~~~~~~~~~~~~~~

Ajoutons maintenant les informations sur les relations de ce réseau. Sachant qu'il est probable que la route soit gelée, et que Holmes et Watson sont de mauvais conducteurs, nous considérerons que :

:math:`P(I=1)=0.7`\\
:math:`P(H=1|I=1) = 0.9, P(W=1|I=1) = 0.7`\\
:math:`P(H=1|I=0) = 0.1, P(W=1|I=0)=0.5`

Ensuite, sachant que la présence de Holmes est essentielle pour la résolution d'une affaire, nous considérerons que :

:math:`P(S=1|H=1, W = 1) = 0.1`\\
:math:`P(S=1|H=1, W = 0) = 0.2`\\
:math:`P(S=1|H=0, W = 1) = 0.8`\\
:math:`P(S=1|H = 0, W = 0) = 1`

Question 2.5:
_____________
 
Complétez les tableaux des probabilités conditionnelles :math:`P(H|I)` et :math:`P(W|I)`.

+--------------+-----------+-----------+--------------+-----------+-----------+
+:math:`P(H|I)`|:math:`I=1`|:math:`I=0`|:math:`P(W|I)`|:math:`I=1`|:math:`I=0`+
+--------------+-----------+-----------+--------------+-----------+-----------+
+:math:`H=1`   |...        |...        |:math:`W=1`   |...        |...        +
+--------------+-----------+-----------+--------------+-----------+-----------+
+:math:`H=0`   |...        |...        |:math:`W=0`   |...        |...        +
+--------------+-----------+-----------+--------------+-----------+-----------+

Question 2.6:
_____________

À partir de ces tableaux, calculez les probabilités que Holmes et Watson aient eu un accident, :math:`P(H = 1)` et :math:`P(W = 1)`.

Question 2.7:
_____________
 
Dès que Miss Lovelace l'a informé que Watson a eu un accident, Smith déduit que les routes sont probablement gelées. Calculez la probabilité :math:`P(I = 1|W = 1)`.

Question 2.8 (Dépendance) :
___________________________
 
Ensuite, il en déduit que Holmes a probablement eu un accident aussi. Quelle est la probabilité :math:`P(H = 1|W = 1)`? Comparez-la avec :math:`P(H = 1)` : :math:`H` et :math:`W` sont deux événements dépendants.

Question 2.9 (Indépendance conditionnelle) :
____________________________________________
 
Dès que Miss Lovelace l'informe que les routes ne sont pas gelées, Smith revient sur ses conclusions. Quelle est la probabilité :math:`P(H = 1|W = 1, I = 0)` ? Comparez-la avec :math:`P(H = 1 | I = 0)` : :math:`H` et :math:`W` sont deux événements indépendants, étant donné :math:`I`.

Question 2.10 :
_______________
 
Complétez le tableau de la probabilité conditionnelle :math:`P(S|W, H)` :

+-----------------+----------------+----------------+----------------+---------------+
+:math:`P(S|W,H)` |:math:`H=1,W=1` |:math:`H=1,W=0` |:math:`H=0,W=1` |:math:`H=0,W=0`+
+-----------------+----------------+----------------+----------------+---------------+
+:math:`S=1`      |...             |...             |...             |...            +
+-----------------+----------------+----------------+----------------+---------------+
+:math:`S=0`      |...             |...             |...             |...            +
+-----------------+----------------+----------------+----------------+---------------+

Question 2.11 (Causes multiples) :
__________________________________
 
À partir de ce tableau, calculez la probabilité que le professeur ait été sauvé, :math:`P(S = 1)`.

Question 2.12 :
_______________
 
En découvrant l'accident de son mari et le dénouement de l'affaire de la Priory School, Mary conclut que Holmes a évité l'accident. Quelle est la probabilité :math:`P(H = 1|W = 1,S = 1)` ?

Question 2.13 (Abduction avec plusieurs conséquences) :
_______________________________________________________

Ensuite, elle déduit que les routes n'étaient pas gelées. Quelle est la probabilité :math:`P(I = 1|W = 1,S = 1)` ?

Question 2.14 :
_______________
 
Finalement, elle découvre l'état des routes et déduit que Holmes n'a certainement pas eu un accident. Quelle est la probabilité :math:`P(H = 1|I = 0,W = 1,S = 1)` ?

Question 2.15 (Dépendance conditionnelle) :
___________________________________________
 
Et si Mary n'était pas au courant de l'accident de Watson ? Quelle est la probabilité :math:`P(H = 1|I = 0,S = 1)` ? Comparez-la avec :math:`P(H = 1|I = 0, W = 1, S = 1)` : :math:`H` et :math:`W` sont deux événements dépendants, étant donnés :math:`I` et :math:`S`.

Question 2.16 :
_______________
 
Finalement, quelle cause pourrait expliquer l'accident de Watson ? Peut-être ses pneus sont-ils trop vieux ? Modifiez le réseau en ajoutant le noeud :math:`V` = Vieux-Pneus-Watson.

Question 2.17 :
_______________
 
Comment peut-on exprimer la probabilité de l'événement :math:`V` dès lors qu'on sait si les événements :math:`I` et :math:`W` se sont produits ou pas ? Si l'on connaît :math:`P(V)`, de quelles informations faut-il disposer sur les relations entre :math:`I`, :math:`W` et :math:`V` pour calculer cette probabilité ?

Inférence à chaînage avant avec facteurs de certitude
=====================================================

Dans cet exercice, nous allons modifier notre moteur d'inférence à chaînage avant de façon à intégrer la notion d'incertitude dans les faits et les règles. Les modules ci-dessous représentent le squelette du programme que nous allons développer. 

Notez que le code de cette série s'appuie sur des modules développés dans les chapitres précédents. Nous ne les reproduisons pas ici, mais il est nécessaire de pouvoir les importer. Veillez à organiser vos dossiers en conséquence.

:download:`inference_facteurs_certitude.zip <../../squelettes/inference_facteurs_certitude.zip>`

Module ``.../moteur_avec_variables_fc/facteurs_certitude.py`` :

.. include::  ../../squelettes/inference_facteurs_certitude/moteur_avec_variables_fc/facteurs_certitude.py
    :code:

Module ``.../moteur_avec_variables_fc/regle_avec_variables_fc.py`` :

.. include::  ../../squelettes/inference_facteurs_certitude/moteur_avec_variables_fc/regle_avec_variables_fc.py
    :code:

Module ``.../moteur_avec_variables_fc/connaissance_fc.py`` :

.. include::  ../../squelettes/inference_facteurs_certitude/moteur_avec_variables_fc/connaissance_fc.py
    :code:

Module ``.../moteur_avec_variables_fc/chainage_avant_avec_variables_fc.py`` :

.. include::  ../../squelettes/inference_facteurs_certitude/moteur_avec_variables_fc/chainage_avant_avec_variables_fc.py
    :code:

Module ``.../exemple_animaux.py`` :

.. include::  ../../squelettes/inference_facteurs_certitude/exemple_animaux.py
    :code:

Exercice 1 : Manipulation des facteurs de certitude
---------------------------------------------------

Dans cet exercice, nous aurons besoin de pouvoir combiner les facteurs de certitude de faits entrant dans une relation logique les uns avec les autres. Commencez donc par compléter la fonction ``fc_et`` de ``facteurs_certitude.py``, qui prend comme paramètres deux facteurs de certitude devant exister conjointement et qui retourne le minimum des deux. Cette fonction sera utilisée par la méthode ``RegleAvecVariables_FC.satisfaite_par``, que nous verrons ci-dessous, pour calculer le facteur de certitude associé à un ensemble de conditions.

Dans le même module, codez aussi une fonction ``fc_ou`` qui prend comme paramètres les facteurs de certitude de deux faits identiques par leur contenu (même proposition) et qui retourne le facteur de certitude résultant. Cette fonction sera utilisée par la méthode ``BaseConnaissances_FC.ajoute_un_fait`` pour réaliser la mise à jour des facteurs de certitude.

Exercice 2 : Le faits et les règles
-----------------------------------

Des facteurs de certitude doivent être associés aux faits et aux règles. Le langage Python ne permettant pas d'utiliser des structures au sens des ``struct`` du C, nous ferons usage de ``tuples`` de la forme ``(fait, fc)``, où ``fait`` est le fait que l'on veut définir et ``fc`` un facteur de certitude. Nous aurons ainsi par exemple :

::

    fait = (('père', 'Jean', 'Paul'), 9/10)

D'une façon analogue, la classe ``RegleAvecVariables_FC`` implémentera une règle avec trois attributs :

* ``conditions`` : la liste des conditions de la règle;
* ``conséquence`` : la conséquence de la règle;
* ``fc`` : le facteur de certitude associé à la règle.

Nous aurons ainsi par exemple : 

::

    regle = RegleAvecVariables_FC([('père', '?x', '?z'), ('père', '?z', '?y')], 
                                   ('grand-père', '?x', '?y'), 
                                   8/10)

``RegleAvecVariables_FC`` s'inspire donc de la classe ``RegleAvecVariables`` du moteur de chaînage avant en lui ajoutant un attribut ``self.fc``, qui contiendra la valeur du facteur de certitude associé. Le constructeur de la règle, que vous devez compléter, devra prendre trois arguments, en accord avec la définition ci-dessus. Pour simplifier la création de nouvelles règles, vous pouvez stipuler que l'argument correspondant au facteur de certitude soit optionnel, avec une valeur par défaut de 1.0.

La méthode ``RegleAvecVariables.satisfaite_par``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

La méthode ``depend_de`` de la classe ``RegleAvecVariables`` peut être reprise telle quelle. Il faut en revanche modifier la méthode ``satisfaite_par`` de sorte qu'elle prenne comme arguments :

* La liste des faits déjà connus;
* La condition testée avec succès par ``depend_de``;
* L'environnement résultant de l'appel à ``depend_de``;
* Le facteur de certitude associé au fait qui a déclenché la règle;
* La classe de pattern matching (filtre ou unificateur) utilisée.

La valeur de retour doit être une liste de pairs d'environnements, accompagnés chacun par le facteur de certitude associé. Le facteur de certitude d'un nouvel environnement est donné par le minimum des facteurs de cet environnement et du fait passé en argument à l'appel de la fonction ``pattern_match`` qui a conduit à la découverte du nouvel environnement. 

Prenons par exemple (par simplification, les faits et règles ne sont pas écrits selon le format interne) :

::

    règle = ([('vole', '?x', '?y'), ('est-découvert', '?x')],
             ('en-prison', '?x', 'pour-vol-de', '?y'), 8/10)

    faits = [
             (('vole', 'Jean', 'bijoux'), 9/10),
             (('vole', 'Paul', 'voiture'), 8/10),
             (('est-découvert', 'Paul'), 6/10),
             (('est-découvert', 'Jean'), 8/10))
             ]
        
    # Fait déclencheur :
    fait_declencheur = (('est-découvert', 'Jean'), 8/10)

    # Paires de conditions et environnements retournées par règle.depend_de :
    envs = {('est-découvert', '?x') : {'?x': 'Jean'}}

    # L'essai de la règle par le moteur à chaînage avant doit provoquer l'appel
    # suivant de règle.satisfaite_par :
    satisfaite_par(faits, ('est-découvert', `?x'), {'?x': 'Jean'}, 8/10))
        -> [({'?x': 'Jean', '?y': 'bijoux'}, 8/10)]

La base de connaissances
~~~~~~~~~~~~~~~~~~~~~~~~

La base de connaissances ``BaseConnaissances_FC`` doit aussi être modifiée en adaptant les méthodes ``ajoute_un_fait`` et ``ajoute_une_regle`` aux nouvelles définitions des faits et des règles :

* ``ajoute_un_fait((('père', 'Jean', 'Paul'), 9/10))`` ajoute le fait ``('père', 'Jean', 'Paul')`` à la base de connaissances avec un facteur de certitude de 9/10;
* ``ajoute_un_fait(('père', `Paul', 'Marc'))`` ajoute le fait ``('père', 'Paul', 'Marc')`` à la base de connaissances avec une facteur de certitude de 1 (valeur par défaut);
* ``ajoute_une_regle(([('père', '?x', '?z'), ('père', '?z', '?y')], ('grand-père', '?x', '?y'), 8/10))`` ajoute une règle à la base de connaissances avec un facteur de certitude de 8/10;
* ``ajoute_une_regle(([('père', '?x', '?z'), ('père', '?z', '?y')], ('grand-père', '?x', '?y')))`` ajoute une règle à la base de connaissances avec un facteur de certitude de 1.

La fonction ``ajoute_un_fait`` continue de jouer le même rôle que par le passé : elle ajoute un fait à la base des faits si celui-ci n'est pas déjà connu. Cependant, elle doit être modifiée pour tenir compte des facteurs de certitude. En effet, si le fait à ajouter n'est pas nouveau (il a donc été démontré auparavant par un chemin différent), il faut mettre à jour le facteur de certitude associé pour refléter la nouvelle valeur de certitude. Utilisez la méthode ``fc_ou`` dans ce cas.

Exercice 3 : Le moteur d'inférence à chaînage avant avec variables
------------------------------------------------------------------

La méthode ``ChainageAvantAvecVariables.instancie_conclusion``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Comme auparavant, la méthode ``instancie_conclusion`` de la classe ``ChainageAvantAvecVariables_FC`` doit instancier la conséquence d'une règle au moyen d'une liste d'environnements. Cependant, les nouveaux faits doivent maintenant se voir affecter un facteur de certitude, qui dépendra du facteur de certitude de la règle et de celui qui résulte des conditions. Elle doit donc prendre en paramètre une liste de paire d'environnements associés à leurs facteurs de certitude, et retourner les instanciations de la conclusion, munies chacune de leur facteur de certitude.

Prenez bien en compte la formule de calcul du facteur de certitude résultant de l'application d'une règle. Exemple :

::

    règle = ([('vole', '?x', '?y'), ('est-découvert', '?x')], 
             ('en-prison', '?x', 'pour-vol-de', '?y'), 8/10)

    instancie_conclusion(règle,
                    [({'?x' : 'Jean', '?y' : 'bijoux'}, 8/10),
                     ({'?x' : 'Paul', '?y' : 'voiture'}, 6/10)])
        -> [(('en-prison', 'Jean', 'pour-vol-de', 'bijoux'), 16/25),
           (('en-prison', 'Paul', 'pour-vol-de', 'voiture'), 12/25)]

La méthode ``ChainageAvantAvecVariables_FC.chaine``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Les faits dont le facteur de certitude est négatif ne doivent pas être utilisés comme déclencheurs, même s'ils sont consignés dans la base des faits. Implémentez donc la méthode de chaînage avant ``chaine`` de ``ChainageAvantAvecVariables_FC`` en tenant compte des modifications apportées aux autres fonctions et en vous inspirant de l'algorithme suivant :

::

    ChainageAvantAvecVariables_FC(faits_depart, regles)
    1.  solutions <- liste vide
    2.  Q <- faits_depart
    3.  WHILE Q n'est pas vide DO
    4.      q <- premier(Q)
    5.      Q <- reste(Q)
    6.      IF q n'est pas dans solutions THEN
    7.          ajouter q à solutions
    8.          IF le facteur de certitude fc de q est plus grand que 0 THEN
    9.              FOR EACH règle r de regles DO
    10.                 envs <- toute les paires de conditions et environnements issues du pattern matching réussi entre q et les conditions de r
    11.                 FOR chaque condition cond et environnement env de envs DO
    12.                     envs1 <-  toutes les paires environnements et fcs établis par le pattern matching des conditions restantes de r étant donné env
    13.                     FOR chaque environnement env1 et facteur fc1 de envs1 DO
    14.                         instances <- instanciation de la conclusion de r selon env1 et fc1
    15.                         ajouter instances en queue de Q
    16.                     END FOR
    17.                 END FOR
    18.             END FOR
    19.         END IF
    20.     END IF
    21. END WHILE
    22. RETURN solutions	
    END ChainageAvantAvecVariables_FC

Test du programe
----------------

Le module ``exemple_animaux.py`` contient des règles et des faits modélisant une petite partie d'un arbre de classification des mammifères. Après avoir écrit votre programme, testez-le sur le premier exemple en ajoutant l'option ``A``. Vous pouvez afficher la trace en utilisant l'option ``trace``.

::

    python3 exemple_animaux.py A
    python3 exemple_animaux.py A trace

Le module contient un deuxième exemple que vous pouvez exécuter avec la commande :

::

    python3 exemple_animaux.py B
    python3 exemple_animaux.py B trace
