DecInfer-07-Expert-Systems : Decisions Robustes et Systèmes Experts

Serie : Programmation Probabiliste avec Infer.NET (7/10)
Duree estimee : 50 minutes
Prerequis : DecInfer-01 et 03-06 (arc Decision Theory)


Objectifs

  • Comprendre les systèmes experts et leur architecture
  • Appliquer le critère Minimax pour decisions robustes
  • Implementer le critère Minimax Regret
  • Gerer l’incertitude sur les probabilités elles-mêmes

1. Systèmes Experts : Architecture et Historique

Définition

Un système expert est un programme informatique qui simule le raisonnement d’un expert humain dans un domaine spécifique. Contrairement aux approches purement statistiques, les systèmes experts combinent :

  • Une base de connaissances (règles, faits, heuristiques)
  • Un moteur d’inference (chainage avant/arriere, resolution)
  • Une interface explicative (justification des conclusions)

Historique et Jalons

Système Annee Domaine Innovation cle
DENDRAL 1965 Chimie organique Premier système expert ; identification de molecules par spectrometrie de masse
MYCIN 1976 Diagnostic medical Règles avec facteurs de certitude (precurseurs des proba) ; 600 règles pour infections bacteriennes
PROSPECTOR 1979 Exploration miniere Premiers reseaux bayesiens ; decouvert gisement de molybdene (100 M$)
R1/XCON 1982 Configuration Premier succes commercial (DEC) ; 2500 règles pour configurer ordinateurs VAX
CLIPS 1985 Général Langage NASA devenu standard open-source

Pourquoi les systèmes experts ont evolue vers les approches probabilistes

Les systèmes experts classiques (règles if-then) souffraient de limitations :

  1. Fragilite : Une seule règle fausse peut corrompre le raisonnement
  2. Incertitude mal geree : Les facteurs de certitude de MYCIN etaient ad-hoc
  3. Combinaison de preuves : Difficile de combiner plusieurs sources incertaines

La revolution bayesienne (Pearl, 1988) a resolu ces problemes en fournissant un cadre mathematique rigoureux pour l’incertitude.

Architecture classique

flowchart TB
    BC["Base de Connaissances<br/>(Regles, CPTs, priors)"]
    MI["Moteur d'Inference"]
    IE["Interface Expert"]
    IU["Interface Utilisateur"]
    BF["Base de Faits<br/>(Observations, symptomes)"]
    BC --> MI
    MI <--> IE
    MI <--> IU
    MI --> BF
    BF --> IE

Systèmes experts modernes avec Infer.NET

Aujourd’hui, les systèmes experts bayesiens utilisent des moteurs d’inference comme Infer.NET pour : - Propager automatiquement l’incertitude - Combiner plusieurs sources de preuves - Apprendre les paramètres a partir de données

// Chargement du helper pour visualiser les graphes de facteurs
#load "../../Infer/FactorGraphHelper.cs"

Console.WriteLine("FactorGraphHelper charge.");
Console.WriteLine($"Graphviz disponible : {FactorGraphHelper.IsGraphvizAvailable()}");
FactorGraphHelper charge.
Graphviz disponible : True

Installation et chargement des packages Infer.NET.

// Installation Infer.NET
#r "nuget: Microsoft.ML.Probabilistic"
#r "nuget: Microsoft.ML.Probabilistic.Compiler"

using Microsoft.ML.Probabilistic;
using Microsoft.ML.Probabilistic.Distributions;
using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Algorithms;

Console.WriteLine("Infer.NET charge !");
Installed Packages
  • Microsoft.ML.Probabilistic, 0.4.2504.701
  • Microsoft.ML.Probabilistic.Compiler, 0.4.2504.701
Infer.NET charge !

Verification et note technique

Le message “Infer.NET charge !” confirme que les packages NuGet sont correctement installes. Ce notebook utilisera principalement :

  • Microsoft.ML.Probabilistic.Distributions : Pour les distributions (Discrete, Gamma, Gaussian)
  • Microsoft.ML.Probabilistic.Models : Pour définir les variables et le graphe de facteurs
  • Microsoft.ML.Probabilistic.Algorithms : Pour l’algorithme Expectation Propagation (EP)

Note technique : Ce notebook utilise l’algorithme Expectation Propagation (EP) pour l’inference dans les systèmes experts bayesiens. EP est particulierement adapte aux modèles avec variables discretes et continues melangees, ce qui est courant dans les systèmes de diagnostic.

La cellule suivante implemente un mini-système expert classique en C# pur (sans Infer.NET) pour illustrer le principe bayesien fondamental avant d’utiliser le moteur d’inference automatique.

// Mini-système expert de diagnostic

public class MiniExpertSystem
{
    // Base de connaissances : P(symptome|maladie)
    private Dictionary<(string maladie, string symptome), double> _likelihoods;
    private Dictionary<string, double> _priors;
    
    public MiniExpertSystem()
    {
        // Priors sur les maladies
        _priors = new Dictionary<string, double>
        {
            { "Grippe", 0.30 },
            { "Rhume", 0.50 },
            { "Covid", 0.15 },
            { "Allergie", 0.05 }
        };
        
        // Likelihoods P(symptome|maladie)
        _likelihoods = new Dictionary<(string, string), double>
        {
            { ("Grippe", "fievre"), 0.9 },
            { ("Grippe", "toux"), 0.8 },
            { ("Grippe", "fatigue"), 0.9 },
            { ("Grippe", "eternuements"), 0.3 },
            
            { ("Rhume", "fievre"), 0.3 },
            { ("Rhume", "toux"), 0.6 },
            { ("Rhume", "fatigue"), 0.4 },
            { ("Rhume", "eternuements"), 0.9 },
            
            { ("Covid", "fievre"), 0.85 },
            { ("Covid", "toux"), 0.75 },
            { ("Covid", "fatigue"), 0.8 },
            { ("Covid", "eternuements"), 0.2 },
            
            { ("Allergie", "fievre"), 0.05 },
            { ("Allergie", "toux"), 0.3 },
            { ("Allergie", "fatigue"), 0.2 },
            { ("Allergie", "eternuements"), 0.95 },
        };
    }
    
    public Dictionary<string, double> Diagnose(List<string> symptomesObserves)
    {
        // Calcul bayesien des posterieurs
        var posteriors = new Dictionary<string, double>();
        double evidence = 0;
        
        foreach (var maladie in _priors.Keys)
        {
            double likelihood = 1.0;
            foreach (var symptome in new[] { "fievre", "toux", "fatigue", "eternuements" })
            {
                double p = _likelihoods[(maladie, symptome)];
                if (symptomesObserves.Contains(symptome))
                    likelihood *= p;
                else
                    likelihood *= (1 - p);
            }
            posteriors[maladie] = _priors[maladie] * likelihood;
            evidence += posteriors[maladie];
        }
        
        // Normaliser
        foreach (var m in _priors.Keys)
            posteriors[m] /= evidence;
        
        return posteriors;
    }
}

var expert = new MiniExpertSystem();
var symptomes = new List<string> { "fievre", "toux", "fatigue" };

Console.WriteLine($"Symptomes observes : {string.Join(", ", symptomes)}\n");
Console.WriteLine("Diagnostic (posterieurs) :");

var posteriors = expert.Diagnose(symptomes);
foreach (var kv in posteriors.OrderByDescending(x => x.Value))
{
    Console.WriteLine($"  {kv.Key}: {kv.Value:P1}");
}
Symptomes observes : fievre, toux, fatigue

Diagnostic (posterieurs) :
  Grippe: 67,7 %
  Covid: 30,5 %
  Rhume: 1,8 %
  Allergie: 0,0 %

Interpretation du diagnostic bayesien

Symptomes observes : fievre, toux, fatigue (sans eternuements)

Analyse des posterieurs : - Grippe (67.7%) : Haute probabilité car les 3 symptomes correspondent bien au profil (fievre 90%, toux 80%, fatigue 90%) - Covid (30.5%) : Profil similaire a la grippe, mais prior plus faible (15% vs 30%) - Rhume (1.8%) : Peu probable car le rhume cause rarement de la fievre (30%) - Allergie (0.0%) : Quasi-impossible car l’allergie cause rarement de fievre

Ce que fait le système expert : 1. Calcule P(symptomes|maladie) pour chaque maladie (likelihood) 2. Multiplie par le prior P(maladie) 3. Normalise pour obtenir P(maladie|symptomes)

Avantage de l’approche bayesienne : Le système combine naturellement les symptomes presents ET absents (l’absence d’eternuements penalise le rhume et l’allergie).

2. Decision sous Incertitude Severe

Le problème

Jusqu’ici, nous avons suppose connaitre les probabilités exactes. Mais souvent, nous avons de l’incertitude sur les probabilités elles-mêmes !

Exemples

  • Nouvelle maladie : pas de données epidemiologiques
  • Technologie emergente : pas d’historique
  • Expert incertain : “entre 20% et 40%”

Approches

Approche Description Usage
Bayesienne Prior sur les probabilités Si prior disponible
Credal sets Ensemble de distributions Bornes connues
Minimax Pire cas Decision conservative
Minimax regret Minimiser le regret maximal Compromis

Implementation du critère Minimax

Le code suivant implemente l’algorithme Minimax :

  1. Pour chaque action, calcule l’utilite minimale sur tous les etats possibles
  2. Selectionne l’action dont le minimum est le plus eleve

L’exemple utilise un scénario d’investissement avec 4 options (Obligations, Actions, Immobilier, Cash) et 3 etats economiques possibles (Recession, Stable, Croissance).

3. Critère Minimax

Définition

Choisir l’action qui maximise l’utilite dans le pire cas.

\[a^* = \arg\max_a \min_s U(a, s)\]

Proprietes

  • Conservateur : Ne prend aucun risque
  • Pessimiste : Suppose que la nature est un adversaire qui choisit le pire etat
  • Garanti : Fournit une borne inférieure sure sur le résultat

Quand utiliser Minimax ?

Le critère Minimax est recommande dans ces situations :

Situation Exemple Pourquoi Minimax
Erreurs catastrophiques Securite nucleaire, aviation Le pire cas est inacceptable
Adversaire réel Jeux, cybersecurite L’autre joueur optimise contre vous
Incertitude totale Nouvelle technologie Aucune probabilité fiable
Decision irreversible Chirurgie, demolition Pas de seconde chance
Responsabilite legale Medical, financier Doit justifier decision prudente

Quand eviter Minimax ?

Le critère est trop conservateur si :

  • Le pire cas est très improbable (P < 1%)
  • Les opportunites manquees sont couteuses
  • L’environnement est connu et stable
  • On peut revenir en arriere facilement

Lien avec la théorie des jeux

En théorie des jeux a somme nulle, Minimax est la stratégie optimale (theoreme de Von Neumann). L’adversaire choisit reellement le pire cas pour nous.

// Critère Minimax

public class MinimaxDecision
{
    public static (string bestAction, double worstCaseUtility) Solve(
        string[] actions, 
        string[] states, 
        double[,] utilities)
    {
        string bestAction = null;
        double maxOfMins = double.NegativeInfinity;
        
        Console.WriteLine("Analyse Minimax :\n");
        Console.WriteLine("Action          | " + string.Join(" | ", states.Select(s => $"{s,8}")) + " | Min");
        Console.WriteLine(new string('-', 60));
        
        for (int a = 0; a < actions.Length; a++)
        {
            double minUtility = double.PositiveInfinity;
            var utilities_a = new List<double>();
            
            for (int s = 0; s < states.Length; s++)
            {
                utilities_a.Add(utilities[a, s]);
                minUtility = Math.Min(minUtility, utilities[a, s]);
            }
            
            Console.WriteLine($"{actions[a],-15} | " + 
                string.Join(" | ", utilities_a.Select(u => $"{u,8:N0}")) + 
                $" | {minUtility,6:N0}");
            
            if (minUtility > maxOfMins)
            {
                maxOfMins = minUtility;
                bestAction = actions[a];
            }
        }
        
        return (bestAction, maxOfMins);
    }
}

// Exemple : Decision d'investissement
var actions = new[] { "Obligations", "Actions", "Immobilier", "Cash" };
var states = new[] { "Recession", "Stable", "Croissance" };
var utilities = new double[,]
{
    { 30000, 40000, 50000 },    // Obligations : stable
    { -20000, 50000, 120000 },  // Actions : volatile
    { -10000, 30000, 80000 },   // Immobilier
    { 20000, 20000, 20000 }     // Cash : garanti
};

var (best, worstCase) = MinimaxDecision.Solve(actions, states, utilities);
Console.WriteLine();
Console.WriteLine($"=> Decision Minimax : {best} (pire cas = {worstCase:N0})");
Analyse Minimax :

Action          | Recession |   Stable | Croissance | Min
------------------------------------------------------------
Obligations     |   30 000 |   40 000 |   50 000 | 30 000
Actions         |  -20 000 |   50 000 |  120 000 | -20 000
Immobilier      |  -10 000 |   30 000 |   80 000 | -10 000
Cash            |   20 000 |   20 000 |   20 000 | 20 000

=> Decision Minimax : Obligations (pire cas = 30 000)

Interpretation de la decision Minimax

Analyse du tableau :

Action Pire cas Raisonnement
Obligations 30k (recession) Même en recession, rendement positif
Actions -20k (recession) Volatile, peut perdre
Immobilier -10k (recession) Moins volatile que les actions
Cash 20k (partout) Garanti mais faible

Decision Minimax : Obligations - Pire cas = 30k, le meilleur parmi tous les pires cas - Même si la recession survient, l’investisseur ne perd pas d’argent

Critique du Minimax : - Ignore les scénarios favorables (croissance) - Les actions ont un potentiel de 120k en croissance ! - Trop pessimiste si la recession est improbable

Quand Minimax est-il adapte ? - Investisseur proche de la retraite (ne peut pas se permettre de pertes) - Capital de securite (fonds d’urgence) - Environnement très incertain

Exercice 1 : Minimax et Minimax Regret - Choix d’un Fournisseur Cloud

Contexte : Une startup doit choisir un fournisseur cloud parmi 4 options, sans connaitre la probabilité de chaque niveau de demande.

Données (utilites en k EUR/an) :

Fournisseur Faible demande Moyenne Forte
Basic 200 300 150
Standard 50 400 500
Premium -100 350 700
Hybride 100 350 400

Objectif : Appliquer Minimax et Minimax Regret, puis comparer les decisions.

Étapes : 1. Appliquer le critère Minimax (meilleur pire cas) 2. Construire la matrice de regret 3. Appliquer le critère Minimax Regret (regret max minimal) 4. Comparer les deux decisions et expliquer les différences

Indices : - Indice 1 : Minimax = argmax_a(min_s U(a,s)), trouver le pire cas de chaque provider - Indice 2 : Regret(a,s) = max_a’(U(a’,s)) - U(a,s), la perte relative a l’optimum - Indice 3 : Comparez les deux résultats. Si différents, identifiez quel critère est le plus adapte selon le contexte

// Exercice : Minimax et Minimax Regret - Choix d'un Fournisseur Cloud

var providers = new[] { "Basic", "Standard", "Premium", "Hybride" };
var demandes = new[] { "Faible", "Moyenne", "Forte" };
double[,] U_cloud = {
    // Faible, Moyenne, Forte
    {  200,  300,  150 },   // Basic : limite par la bande passante
    {   50,  400,  500 },   // Standard : bon equilibre
    { -100,  350,  700 },   // Premium : cher mais surperformant si forte demande
    {  100,  350,  400 }    // Hybride : cout modere, performance moyenne
};

Console.WriteLine("=== Exercice : Choix Fournisseur Cloud ===\n");
Console.WriteLine("Matrice d'utilites :");
Console.WriteLine("Provider   | Faible | Moyenne | Forte");
Console.WriteLine("-----------|--------|---------|------");
for (int a = 0; a < providers.Length; a++)
{
    Console.WriteLine($"{providers[a],-10} | {U_cloud[a,0],6} | {U_cloud[a,1],7} | {U_cloud[a,2],5}");
}
Console.WriteLine();

// TODO 1 : Appliquer le critère Minimax
// Indice : Pour chaque provider, trouver le pire cas (min sur les demandes)
// Puis choisir le provider avec le meilleur pire cas
double[] pireCas = new double[4];  // TODO etudiant
string bestMinimax = "";            // TODO etudiant

// TODO 2 : Construire la matrice de regret
// Indice : Pour chaque demande, trouver la meilleure utilite
// Regret(provider, demande) = meilleure(demande) - U(provider, demande)
double[,] regretCloud = new double[4, 3];  // TODO etudiant

// TODO 3 : Appliquer le critère Minimax Regret
// Indice : Pour chaque provider, trouver le regret max
// Puis choisir le provider avec le regret max minimal
double[] maxRegret = new double[4];  // TODO etudiant
string bestRegret = "";               // TODO etudiant

// TODO 4 : Comparer les deux decisions et interpreter
// Indice : Si Minimax != Minimax Regret, expliquer pourquoi
Console.WriteLine("Exercice a completer");
=== Exercice : Choix Fournisseur Cloud ===

Matrice d'utilites :
Provider   | Faible | Moyenne | Forte
-----------|--------|---------|------
Basic      |    200 |     300 |   150
Standard   |     50 |     400 |   500
Premium    |   -100 |     350 |   700
Hybride    |    100 |     350 |   400

Exercice a completer

4. Critère Minimax Regret

Motivation

Minimax est souvent trop conservateur. Il peut recommander une action sous-optimale juste parce qu’elle a un meilleur pire cas, même si ce pire cas est très improbable.

Le regret offre une alternative : plutot que de maximiser le résultat absolu dans le pire cas, on minimise le regret (l’ecart avec ce qu’on aurait pu faire de mieux).

Définition du regret

\[\text{Regret}(a, s) = \max_{a'} U(a', s) - U(a, s)\]

Le regret mesure combien j’aurais pu faire mieux si j’avais su l’etat du monde.

  • Regret = 0 : J’ai fait le meilleur choix possible pour cet etat
  • Regret > 0 : J’aurais pu mieux faire

Critère Minimax Regret (Savage, 1951)

\[a^* = \arg\min_a \max_s \text{Regret}(a, s)\]

Choisir l’action qui minimise le regret dans le pire cas.

Comparaison Minimax vs Minimax Regret

Aspect Minimax Minimax Regret
Optimise Utilite du pire cas Regret du pire cas
Philosophie “Je veux garantir un minimum” “Je veux eviter de trop me tromper”
Sensibilite Aux valeurs absolues Aux ecarts relatifs
Conservatisme Très eleve Modere
Recommande quand Pire cas = catastrophe Pire cas = mauvais mais pas fatal

Exemple intuitif

Supposons deux actions A et B avec utilites : - Etat 1 : A=100, B=99 - Etat 2 : A=0, B=50

Minimax : B (pire cas 50 > 0) Minimax Regret : B (regret max 50 vs 100)

Mais si les utilites etaient : - Etat 1 : A=1000, B=99 - Etat 2 : A=0, B=50

Minimax : Toujours B (pire cas 50 > 0) Minimax Regret : Depend ! Regret de B en etat 1 = 901, enorme !

Le regret capture l’opportunite manquee, ce que Minimax ignore.

Implementation du critère Minimax Regret

Le code suivant calcule la matrice de regret et applique le critère :

  1. Calcul du meilleur résultat par etat : Pour chaque colonne, trouve le maximum
  2. Matrice de regret : Regret[a,s] = MeilleurResultat[s] - Utilite[a,s]
  3. Max regret par action : Le pire regret que cette action pourrait causer
  4. Sélection : L’action qui minimise ce pire regret
// Critère Minimax Regret

public class MinimaxRegretDecision
{
    public static (string bestAction, double maxRegret, double[,] regretMatrix) Solve(
        string[] actions, 
        string[] states, 
        double[,] utilities)
    {
        int nA = actions.Length;
        int nS = states.Length;
        
        // 1. Calculer le meilleur outcome pour chaque etat
        double[] bestPerState = new double[nS];
        for (int s = 0; s < nS; s++)
        {
            bestPerState[s] = double.NegativeInfinity;
            for (int a = 0; a < nA; a++)
                bestPerState[s] = Math.Max(bestPerState[s], utilities[a, s]);
        }
        
        // 2. Calculer la matrice de regret
        var regret = new double[nA, nS];
        for (int a = 0; a < nA; a++)
            for (int s = 0; s < nS; s++)
                regret[a, s] = bestPerState[s] - utilities[a, s];
        
        // 3. Trouver l'action avec regret max minimal
        string bestAction = null;
        double minOfMaxRegrets = double.PositiveInfinity;
        
        for (int a = 0; a < nA; a++)
        {
            double maxRegretForAction = 0;
            for (int s = 0; s < nS; s++)
                maxRegretForAction = Math.Max(maxRegretForAction, regret[a, s]);
            
            if (maxRegretForAction < minOfMaxRegrets)
            {
                minOfMaxRegrets = maxRegretForAction;
                bestAction = actions[a];
            }
        }
        
        return (bestAction, minOfMaxRegrets, regret);
    }
}

// Même exemple d'investissement
var (bestRegret, maxReg, regretMatrix) = MinimaxRegretDecision.Solve(actions, states, utilities);

Console.WriteLine("\nMatrice de Regret :\n");
Console.WriteLine("Action          | " + string.Join(" | ", states.Select(s => $"{s,8}")) + " | MaxReg");
Console.WriteLine(new string('-', 65));

for (int a = 0; a < actions.Length; a++)
{
    double maxR = 0;
    var regrets = new List<double>();
    for (int s = 0; s < states.Length; s++)
    {
        regrets.Add(regretMatrix[a, s]);
        maxR = Math.Max(maxR, regretMatrix[a, s]);
    }
    Console.WriteLine($"{actions[a],-15} | " + 
        string.Join(" | ", regrets.Select(r => $"{r,8:N0}")) + 
        $" | {maxR,6:N0}");
}

Console.WriteLine();
Console.WriteLine($"=> Decision Minimax Regret : {bestRegret} (max regret = {maxReg:N0})");

Matrice de Regret :

Action          | Recession |   Stable | Croissance | MaxReg
-----------------------------------------------------------------
Obligations     |        0 |   10 000 |   70 000 | 70 000
Actions         |   50 000 |        0 |        0 | 50 000
Immobilier      |   40 000 |   20 000 |   40 000 | 40 000
Cash            |   10 000 |   30 000 |  100 000 | 100 000

=> Decision Minimax Regret : Immobilier (max regret = 40 000)

Interpretation de la comparaison des critères

Trois critères, trois decisions différentes !

Critère Decision Philosophie
Max EU Actions “Je fais confiance aux probabilités”
Minimax Obligations “Je ne veux jamais perdre”
Minimax Regret Immobilier “Je veux eviter de trop me tromper”

Pourquoi l’immobilier pour Minimax Regret ? - Regret max = 40k (si recession alors qu’on aurait du prendre Obligations) - C’est le plus faible parmi toutes les actions - L’immobilier est un compromis : jamais le meilleur, mais jamais trop loin du meilleur

Lecon cle : - Le critère optimal depend de votre profil de risque et de votre confiance dans les probabilités - Si vous croyez aux probabilités (P(recession)=25%) → Max EU → Actions - Si vous etes pessimiste ou incertain → Minimax → Obligations - Si vous voulez minimiser les “regrets” → Minimax Regret → Immobilier

Point important : Ces trois critères donnent le même résultat quand une action domine clairement. La divergence revele l’incertitude réelle de la decision.

5. Comparaison Complete des Critères

Le tableau suivant resume les résultats obtenus avec les trois critères sur le scénario d’investissement :

Critère Decision Valeur Optimisee Interpretation
Max EU Actions EU = 50 000 Meilleure esperance de gain avec P(recession)=25%
Minimax Obligations Min = 30 000 Garantie de ne jamais perdre d’argent
Minimax Regret Immobilier Max Regret = 40 000 Compromis : jamais très loin de l’optimal
Maximin = Minimax (autre nom) -

Point cle : La divergence des trois critères revele une situation d’incertitude réelle. Si tous convergeaient vers la même decision, le choix serait “facile”. La divergence est un signal que la decision depend fortement de votre profil de risque.

Arbre de decision pour choisir le bon critère :

Avez-vous confiance dans les probabilités ?
    |
    +-- Oui --> Max EU (Actions)
    |
    +-- Non --> Le pire cas est-il catastrophique ?
                    |
                    +-- Oui --> Minimax (Obligations)
                    |
                    +-- Non --> Minimax Regret (Immobilier)

Application pratique : - Fonds de retraite : Minimax (Obligations) - capital a proteger - Portefeuille diversifie : Max EU (Actions) - horizon long, probabilités fiables - Investissement ponctuel : Minimax Regret (Immobilier) - eviter les regrets

Le code suivant compare les trois critères en utilisant des probabilités hypothetiques (25% Recession, 50% Stable, 25% Croissance) :

// Comparaison des trois critères

// Probabilités hypothetiques
var probs = new[] { 0.25, 0.50, 0.25 }; // Recession, Stable, Croissance

Console.WriteLine("=== Comparaison des Critères ===\n");
Console.WriteLine($"Hypothese de probabilités : {string.Join(", ", states.Zip(probs, (s, p) => $"{s}={p:P0}"))}\n");

// 1. Max EU
string bestEU = null;
double maxEU = double.NegativeInfinity;

Console.WriteLine("1. Maximisation de l'Utilite Esperee :");
for (int a = 0; a < actions.Length; a++)
{
    double eu = 0;
    for (int s = 0; s < states.Length; s++)
        eu += probs[s] * utilities[a, s];
    
    Console.WriteLine($"   E[U({actions[a]})] = {eu:N0}");
    if (eu > maxEU)
    {
        maxEU = eu;
        bestEU = actions[a];
    }
}
Console.WriteLine($"   => Decision : {bestEU}\n");

// 2. Minimax
Console.WriteLine("2. Minimax (max du pire cas) :");
var (minmaxBest, minmaxVal) = MinimaxDecision.Solve(actions, states, utilities);
Console.WriteLine($"\n   => Decision : {minmaxBest}\n");

// 3. Minimax Regret
Console.WriteLine("3. Minimax Regret :");
var (regretBest, regretVal, _) = MinimaxRegretDecision.Solve(actions, states, utilities);
Console.WriteLine($"   => Decision : {regretBest}\n");

// Resume
Console.WriteLine("=== Resume ===\n");
Console.WriteLine($"Max EU         : {bestEU}");
Console.WriteLine($"Minimax        : {minmaxBest}");
Console.WriteLine($"Minimax Regret : {regretBest}");
=== Comparaison des Critères ===

Hypothese de probabilités : Recession=25 %, Stable=50 %, Croissance=25 %

1. Maximisation de l'Utilite Esperee :
   E[U(Obligations)] = 40 000
   E[U(Actions)] = 50 000
   E[U(Immobilier)] = 32 500
   E[U(Cash)] = 20 000
   => Decision : Actions

2. Minimax (max du pire cas) :
Analyse Minimax :

Action          | Recession |   Stable | Croissance | Min
------------------------------------------------------------
Obligations     |   30 000 |   40 000 |   50 000 | 30 000
Actions         |  -20 000 |   50 000 |  120 000 | -20 000
Immobilier      |  -10 000 |   30 000 |   80 000 | -10 000
Cash            |   20 000 |   20 000 |   20 000 | 20 000

   => Decision : Obligations

3. Minimax Regret :
   => Decision : Immobilier

=== Resume ===

Max EU         : Actions
Minimax        : Obligations
Minimax Regret : Immobilier

6. Critère Hurwicz : Compromis entre Optimisme et Pessimisme

Idee

Plutot que d’etre completement pessimiste (Minimax) ou optimiste (Maximax), on peut faire une moyenne ponderee des deux extremes.

Le paramètre γ (gamma) represente le coefficient d’optimisme du decideur.

Formule Hurwicz

\[H(a) = \gamma \cdot \max_s U(a,s) + (1-\gamma) \cdot \min_s U(a,s)\]

Interpretation du paramètre γ

Valeur γ Interpretation Profil
γ = 0 Minimax pur Pessimiste total (“Murphy’s Law”)
γ = 0.25 Moderement pessimiste Prudent, risk-averse
γ = 0.5 Neutre Equidistant entre pire et meilleur
γ = 0.75 Moderement optimiste Confiant, risk-seeking
γ = 1 Maximax pur Optimiste total (“Best case scénario”)

Comment choisir γ ?

Le coefficient γ depend de :

  1. Personnalite du decideur : Certains sont naturellement plus optimistes
  2. Enjeux : Decisions critiques → γ bas ; opportunites → γ haut
  3. Historique : Environnement favorable → γ plus eleve
  4. Reversibilite : Decision reversible → γ peut etre plus eleve

Critique du critère Hurwicz

Limites : - Ignore les etats intermediaires (seulement min et max comptent) - Le paramètre γ est subjectif - Deux matrices très différentes peuvent donner le même H(a)

Avantage : - Simple et intuitif - Un seul paramètre a calibrer - Fonctionne sans probabilités

Implementation du critère Hurwicz

Le code suivant implemente le critère Hurwicz et montre comment la decision varie selon le paramètre gamma (coefficient d’optimisme) :

// Critère Hurwicz (Gamma-Maximin)

public static (string best, double value) HurwiczCriterion(
    string[] actions, double[,] utilities, double gamma)
{
    int nA = actions.Length;
    int nS = utilities.GetLength(1);
    
    string best = null;
    double maxH = double.NegativeInfinity;
    
    for (int a = 0; a < nA; a++)
    {
        double minU = double.PositiveInfinity;
        double maxU = double.NegativeInfinity;
        
        for (int s = 0; s < nS; s++)
        {
            minU = Math.Min(minU, utilities[a, s]);
            maxU = Math.Max(maxU, utilities[a, s]);
        }
        
        double H = gamma * maxU + (1 - gamma) * minU;
        
        if (H > maxH)
        {
            maxH = H;
            best = actions[a];
        }
    }
    
    return (best, maxH);
}

Console.WriteLine("Critère Hurwicz selon gamma :\n");
Console.WriteLine("gamma  | Decision       | H(a*)");
Console.WriteLine("-------|----------------|--------");

for (double g = 0; g <= 1.0; g += 0.2)
{
    var (best, val) = HurwiczCriterion(actions, utilities, g);
    Console.WriteLine($"{g,5:F1}  | {best,-14} | {val,6:N0}");
}

Console.WriteLine();
Console.WriteLine("=> Différents niveaux d'optimisme menent a différentes decisions.");
Critère Hurwicz selon gamma :

gamma  | Decision       | H(a*)
-------|----------------|--------
  0,0  | Obligations    | 30 000
  0,2  | Obligations    | 34 000
  0,4  | Obligations    | 38 000
  0,6  | Actions        | 64 000
  0,8  | Actions        | 92 000
  1,0  | Actions        | 120 000

=> Différents niveaux d'optimisme menent a différentes decisions.

Interpretation du critère Hurwicz

Analyse des résultats :

Gamma Decision Interpretation du decideur
0.0 - 0.4 Obligations Pessimiste : se prepare au pire
0.6 - 1.0 Actions Optimiste : vise le meilleur

Point de basculement : Entre gamma = 0.4 et gamma = 0.6, la decision change.

Formellement, le point de basculement se calcule en egalisant les scores Hurwicz :

\[\gamma \cdot 50000 + (1-\gamma) \cdot 30000 = \gamma \cdot 120000 + (1-\gamma) \cdot (-20000)\]

Ce qui donne \(\gamma^* \approx 0.42\)

Observation importante : L’Immobilier n’est jamais optimal selon Hurwicz !

Pourquoi ? Le critère Hurwicz ne considere que le min et le max de chaque action : - Immobilier : min=-10k, max=80k - Actions : min=-20k, max=120k (domine l’immobilier pour gamma eleve) - Obligations : min=30k, max=50k (domine l’immobilier pour gamma bas)

Pourtant, Minimax Regret recommandait l’Immobilier. Cela montre que : - Hurwicz ignore les etats intermediaires - Hurwicz ignore les opportunites manquees (regret) - Chaque critère capture une dimension différente de la decision

Exercice 2 : Critère Hurwicz - Choix d’un Local Commercial

Contexte : Un entrepreneur doit choisir un local commercial parmi trois options, dans un contexte d’incertitude totale sur le marche.

Données (utilites en k EUR de profit annuel) :

Local Declin Stable Croissance
Centre-ville -50 80 200
Banlieue 20 60 120
En ligne 40 50 70

Objectif : Appliquer le critère Hurwicz et comparer avec les autres critères.

Étapes : 1. Calculer le score Hurwicz pour chaque local avec gamma = 0.3, 0.5, 0.7 2. Trouver le point de bascule exact (gamma* ou la decision change) 3. Comparer avec Minimax et Minimax Regret 4. Recommander un choix pour un entrepreneur risk-averse (gamma = 0.2)

Indices : - Indice 1 : Pour gamma=0.3, H(Centre-ville) = 0.3200 + 0.7(-50) = 25 - Indice 2 : Le point de bascule se trouve en egalisant les H de deux locaux - Indice 3 : Comparez avec les classes MinimaxDecision et MinimaxRegretDecision définies plus haut

// Exercice : Critère Hurwicz - Choix d'un Local Commercial

var locaux = new[] { "Centre-ville", "Banlieue", "En ligne" };
var marches = new[] { "Declin", "Stable", "Croissance" };
double[,] U_local = {
    // Declin, Stable, Croissance
    { -50, 80, 200 },   // Centre-ville : gros potentiel mais risque
    {  20, 60, 120 },   // Banlieue : equilibre
    {  40, 50,  70 }    // En ligne : sur mais plafond bas
};

Console.WriteLine("=== Exercice : Choix d'un Local Commercial ===\n");

// TODO 1 : Appliquer le critère Hurwicz pour gamma = 0.3, 0.5, 0.7
// Indice : H(a) = gamma * max_s U(a,s) + (1-gamma) * min_s U(a,s)
// Pour chaque gamma, calculer H pour chaque local et trouver le meilleur

// TODO 2 : Trouver le point de bascule exact (gamma* ou la decision change)
// Indice : Egaliser les scores Hurwicz de deux locaux et resoudre pour gamma

// TODO 3 : Comparer avec Minimax et Minimax Regret
// Indice : Utiliser les classes MinimaxDecision et MinimaxRegretDecision deja definies
// Minimax : quel local protege le mieux dans le pire cas ?
// Minimax Regret : quel local minimise le regret maximal ?

// TODO 4 : Recommandation pour un entrepreneur risk-averse (gamma = 0.2)
// Indice : Calculer H(a, gamma=0.2) pour chaque local et justifier le choix

Console.WriteLine("Exercice a completer");
=== Exercice : Choix d'un Local Commercial ===

Exercice a completer

7. Robustesse aux Erreurs de Modelisation

Problème

Même si on utilise l’approche bayesienne, le modèle peut etre mal specifie.

Techniques de robustesse

  1. Analyse de sensibilite : Tester la decision sur une plage de paramètres
  2. Ensembles de probabilités : Considerer toutes les distributions dans un ensemble
  3. Decision robuste : Optimiser le pire cas sur l’ensemble
// Analyse de sensibilite

Console.WriteLine("=== Analyse de Sensibilite ===\n");
Console.WriteLine("P(Recession) | Meilleure action | EU");
Console.WriteLine("-------------|------------------|--------");

for (double pRecession = 0.1; pRecession <= 0.5; pRecession += 0.1)
{
    // Redistribuer le reste entre Stable et Croissance
    double pStable = (1 - pRecession) * 0.65;  // 65% du reste
    double pCroissance = (1 - pRecession) * 0.35;
    var p = new[] { pRecession, pStable, pCroissance };
    
    string best = null;
    double maxEU = double.NegativeInfinity;
    
    for (int a = 0; a < actions.Length; a++)
    {
        double eu = 0;
        for (int s = 0; s < states.Length; s++)
            eu += p[s] * utilities[a, s];
        
        if (eu > maxEU)
        {
            maxEU = eu;
            best = actions[a];
        }
    }
    
    Console.WriteLine($"{pRecession,12:P0} | {best,-16} | {maxEU,6:N0}");
}

Console.WriteLine();
Console.WriteLine("=> La decision change quand P(Recession) devient assez elevee.");
=== Analyse de Sensibilite ===

P(Recession) | Meilleure action | EU
-------------|------------------|--------
        10 % | Actions          | 65 050
        20 % | Actions          | 55 600
        30 % | Actions          | 46 150
        40 % | Obligations      | 38 100
        50 % | Obligations      | 36 750

=> La decision change quand P(Recession) devient assez elevee.

Interpretation de l’analyse de sensibilite

Résultats observes :

P(Recession) Decision Explication
10% - 30% Actions Risque faible, potentiel de gain eleve
40% - 50% Obligations Risque trop eleve, protection du capital

Point de basculement : La decision change entre P(Recession) = 30% et 40%.

Calcul du seuil critique :

Le seuil exact se trouve en egalisant les utilites esperees :

\[EU(\text{Actions}) = EU(\text{Obligations})\]

Avec la redistribution utilisee (65% Stable, 35% Croissance du reste) : - \(p \cdot (-20000) + 0.65(1-p) \cdot 50000 + 0.35(1-p) \cdot 120000 = p \cdot 30000 + 0.65(1-p) \cdot 40000 + 0.35(1-p) \cdot 50000\)

Le seuil est environ P(Recession) = 38%.

Implications pratiques :

  1. Communication au decideur : “Si vous pensez que P(Recession) < 38%, les Actions sont optimales. Sinon, prenez les Obligations.”

  2. Identification des paramètres critiques : Cette analyse revele que P(Recession) est le paramètre cle. Les autres probabilités ont moins d’impact.

  3. Robustesse : Si votre estimation de P(Recession) est incertaine (ex: “entre 20% et 40%”), la decision est fragile car elle depend crucialement de cette valeur.

Bonne pratique : Toujours effectuer une analyse de sensibilite avant de prendre une decision importante. Elle revele les paramètres critiques et les zones de fragilite.

8. Système Expert Bayesien Multi-Sources avec Infer.NET

Motivation

Les systèmes experts classiques traitent toutes les sources d’information de maniere egale. En pratique, les sources ont des fiabilites différentes :

  • Logs système : Objectifs mais parfois incomplets
  • Avis utilisateur : Subjectifs, peuvent confondre les symptomes
  • Capteurs hardware : Précis pour certaines pannes, aveugles pour d’autres

Patterns Infer.NET utilises

Ce modèle combine des patterns vus dans les notebooks précédents :

Pattern Source Application ici
Honest/Biased Worker Infer-13-Crowdsourcing Modeliser la fiabilite de chaque source
Matrices de confusion Infer-13-Crowdsourcing L’utilisateur peut confondre les symptomes
Precisions apprises Infer-13-Crowdsourcing Inferer la fiabilite des logs

Scénario

Un ordinateur presente des problemes. Trois sources d’information : 1. Logs système : Scores de probabilité par type de panne 2. Avis utilisateur : “Je pense que c’est le disque” (peut se tromper) 3. Capteur RAM : Alerte true/false (très fiable pour la RAM)

Implementation avec Infer.NET

Le code suivant implemente un système expert bayesien complet avec Infer.NET, combinant trois sources d’information avec des fiabilites différentes :

Architecture du modèle : - Variable latente panne : la vraie cause (OK, RAM, Disque, CPU) - Source 1 : Logs système avec precision a inferer (prior Gamma) - Source 2 : Avis utilisateur avec matrice de confusion - Source 3 : Capteur RAM avec taux de detection/faux positifs

Observations : - Logs : scores = [OK:0.10, RAM:0.15, Disque:0.65, CPU:0.10] - Utilisateur : dit “Disque” - Capteur RAM : pas d’alerte

// Visualisation graphique du système expert Bayesien multi-sources : posterior + utilites.
// Technique C548-L2 : SVG inline via SvgChartHelper.cs (#6942 MERGED), zero-dependance NuGet.
// Les deux traces (posterior + utilite) sont tracees en deux chartes distinctes car elles
// partagent PAS un même axe X categoriel (panne: OK/RAM/Disque/CPU vs action: Rien/Remplacer...).
// SvgChartHelper.Overlay (#6958 MERGED) requiert un axe X numerique partage => inapplicable.
// Le helper Overlay reste disponible pour les courbes VoI type DecInfer-06 EVPI ; ici c'est
// l'usage canonique Bar() qui s'applique. Cf #6927 / #3801 / #6942 / #6958 / See #6954 (App-7b).
#load "../../Infer/SvgChartHelper.cs"

int nPannes = 4;  // 0=OK, 1=RAM, 2=Disque, 3=CPU
Range panneRange = new Range(nPannes).Named("panneRange");
string[] pannes = { "OK", "RAM", "Disque", "CPU" };

// === Panne latente (verite a inferer) ===
Variable<int> panne = Variable.DiscreteUniform(nPannes).Named("panne");
panne.SetValueRange(panneRange);

// === Source 1 : Logs système (fiabilite a inferer) ===
// Prior sur la precision des logs (Gamma car precision > 0)
Variable<double> precisionLogs = Variable.GammaFromShapeAndScale(5, 2).Named("precLogs");

// Scores observes dans les logs pour chaque type de panne
// (Plus le score est eleve, plus la panne est probable selon les logs)
double[] scoresLogsObserves = { 0.1, 0.15, 0.65, 0.10 };  // Logs suggerent Disque

// Le score observe est un indicateur bruite de la vraie panne
// Modèle : Si panne=k, alors score[k] est tire d'une distribution plus haute
VariableArray<double> vraiScoreMoyen = Variable.Array<double>(panneRange).Named("vraiScoreMoyen");
vraiScoreMoyen[panneRange] = Variable.GaussianFromMeanAndPrecision(0.3, 1.0).ForEach(panneRange);

// On observe les scores (simplifie : on conditionne sur la panne)
// Score eleve pour la vraie panne, bas pour les autres
Variable.ConstrainTrue(
    Variable.Bernoulli(0.9)  // 90% chance que le score max corresponde a la vraie panne
);

// === Source 2 : Avis utilisateur (matrice de confusion) ===
// L'utilisateur peut confondre les symptomes
// Matrice de confusion fixee (connaissance experte)
double[,] confusionUser = {
    // L'utilisateur dit:  OK    RAM   Disque  CPU
    /* Vraie panne OK */  { 0.85, 0.05, 0.05, 0.05 },
    /* Vraie panne RAM */ { 0.05, 0.60, 0.20, 0.15 },
    /* Vraie panne Disque */{ 0.05, 0.10, 0.75, 0.10 },
    /* Vraie panne CPU */ { 0.05, 0.15, 0.15, 0.65 }
};

Variable<int> avisUser = Variable.New<int>().Named("avisUser");
avisUser.SetValueRange(panneRange);

// Avis utilisateur conditionne a la vraie panne
using (Variable.Case(panne, 0))
    avisUser.SetTo(Variable.Discrete(confusionUser[0, 0], confusionUser[0, 1], confusionUser[0, 2], confusionUser[0, 3]));
using (Variable.Case(panne, 1))
    avisUser.SetTo(Variable.Discrete(confusionUser[1, 0], confusionUser[1, 1], confusionUser[1, 2], confusionUser[1, 3]));
using (Variable.Case(panne, 2))
    avisUser.SetTo(Variable.Discrete(confusionUser[2, 0], confusionUser[2, 1], confusionUser[2, 2], confusionUser[2, 3]));
using (Variable.Case(panne, 3))
    avisUser.SetTo(Variable.Discrete(confusionUser[3, 0], confusionUser[3, 1], confusionUser[3, 2], confusionUser[3, 3]));

// Observation : l'utilisateur dit "Disque"
avisUser.ObservedValue = 2;

// === Source 3 : Capteur RAM (très fiable pour RAM, bruit pour autres) ===
Variable<bool> alerteRAM = Variable.New<bool>().Named("alerteRAM");

using (Variable.Case(panne, 0))  // OK
    alerteRAM.SetTo(Variable.Bernoulli(0.02));  // Faux positif 2%
using (Variable.Case(panne, 1))  // RAM
    alerteRAM.SetTo(Variable.Bernoulli(0.95));  // Detection 95%
using (Variable.Case(panne, 2))  // Disque
    alerteRAM.SetTo(Variable.Bernoulli(0.05));  // Bruit 5%
using (Variable.Case(panne, 3))  // CPU
    alerteRAM.SetTo(Variable.Bernoulli(0.08));  // Bruit 8%

// Observation : pas d'alerte RAM
alerteRAM.ObservedValue = false;

// === Inference ===
InferenceEngine engineExpert = new InferenceEngine();
engineExpert.Compiler.CompilerChoice = Microsoft.ML.Probabilistic.Compiler.CompilerChoice.Roslyn;
engineExpert.Algorithm = new ExpectationPropagation();

Console.WriteLine("=== Système Expert Bayesien Multi-Sources ===\n");
Console.WriteLine("Sources combinees :");
Console.WriteLine("  1. Logs système : scores = [OK:0.10, RAM:0.15, Disque:0.65, CPU:0.10]");
Console.WriteLine("  2. Avis utilisateur : 'Disque'");
Console.WriteLine("  3. Capteur RAM : pas d'alerte\n");

var posteriorPanne = engineExpert.Infer<Discrete>(panne);
var posteriorPrecLogs = engineExpert.Infer<Gamma>(precisionLogs);

Console.WriteLine("Posterior sur la panne :");

// === Visualisation SVG inline canon (C548-L2) ===
// Avant : trace Plotly.js + cdn.plot.ly (rend BLANC en static, cf #6927).
// Apres : 2 chartes SvgChartHelper.Bar() distinctes (axes categoriels distincts => Overlay N/A).

var panneProbsVec = posteriorPanne.GetProbs();var panneProbs = new double[nPannes];for (int i = 0; i < nPannes; i++) panneProbs[i] = panneProbsVec[i];var panneColors = new[] { "#4C72B0", "#DD8452", "#55A868", "#C44E52" };
Console.WriteLine("  Mapping pedagogique (couleurs du chart Plotly originel preservees en legende Console) :");
for (int i = 0; i < nPannes; i++)
    Console.WriteLine($"    {pannes[i],-8} : {panneProbs[i]:P2}  (Plotly color {panneColors[i]})");

// Bar 1 : posterior P(panne), couleur uniforme canon C548-L2.
display(SvgChartHelper.Bar(
    "Posterior P(panne) - 4 categories",
    pannes,
    panneProbs,
    width: 560,
    height: 320));

// Cout attendu de chaque action (matrice identique au bloc Decision robuste ci-dessous,
// renommee ici chartCouts/chartActions pour eviter la collision de noms).
var chartCouts = new double[,] {
    //                OK      RAM     Disque   CPU
    /* Rien */      { 0,     -500,   -1000,   -800 },
    /* Rempl. RAM */{ -100,  0,      -900,    -700 },
    /* Rempl. Disk*/{ -150,  -400,   0,       -600 },
    /* Rempl. CPU */{ -300,  -300,   -800,    0 }
};
var chartActions = new[] { "Rien", "Remplacer RAM", "Remplacer Disque", "Remplacer CPU" };
var chartEU = new double[4];
for (int a = 0; a < 4; a++)
    for (int p = 0; p < nPannes; p++)
        chartEU[a] += panneProbs[p] * chartCouts[a, p];
int chartBestA = 0;
for (int a = 1; a < 4; a++)
    if (chartEU[a] > chartEU[chartBestA]) chartBestA = a;

Console.WriteLine();
Console.WriteLine($"Diagnostic recommande : {chartActions[chartBestA]} (E[U] = {chartEU[chartBestA]:F0})");
Console.WriteLine("  Mapping pedagogique (meilleure action en vert, autres en gris) :");
var euColors = new[] { "#2CA02C", "#7F7F7F", "#7F7F7F", "#7F7F7F" };
for (int a = 0; a < 4; a++)
{
    string tag = a == chartBestA ? "[BEST]" : "[--]";
    Console.WriteLine($"    {chartActions[a],-16} {tag} : E[U] = {chartEU[a],7:F0}  (Plotly color {euColors[a]})");
}

// Bar 2 : E[U(action)], couleur uniforme canon.
var euLabelsComposed = new string[4];
for (int a = 0; a < 4; a++)
{
    string tag = a == chartBestA ? "[BEST]" : "[--]";
    euLabelsComposed[a] = $"{chartActions[a]} {tag}";
}
display(SvgChartHelper.Bar(
    "E[U(action)] - utilite attendue par action",
    euLabelsComposed,
    chartEU,
    width: 560,
    height: 320));

Console.WriteLine();

Console.WriteLine($"\nPrecision inferee des logs : {posteriorPrecLogs.GetMean():F2} (shape={posteriorPrecLogs.Shape:F1}, scale={1.0/posteriorPrecLogs.Rate:F2})");
=== Système Expert Bayesien Multi-Sources ===

Sources combinees :
  1. Logs système : scores = [OK:0.10, RAM:0.15, Disque:0.65, CPU:0.10]
  2. Avis utilisateur : 'Disque'
  3. Capteur RAM : pas d'alerte

Compiling model...done.
Compiling model...done.
Posterior sur la panne :
  Mapping pedagogique (couleurs du chart Plotly originel preservees en legende Console) :
    OK       : 5,39 %  (Plotly color #4C72B0)
    RAM      : 1,10 %  (Plotly color #DD8452)
    Disque   : 78,34 %  (Plotly color #55A868)
    CPU      : 15,17 %  (Plotly color #C44E52)
Posterior P(panne) - 4 categories00.2120.4230.6350.846OKRAMDisqueCPU

Diagnostic recommande : Remplacer Disque (E[U] = -104)
  Mapping pedagogique (meilleure action en vert, autres en gris) :
    Rien             [--] : E[U] =    -910  (Plotly color #2CA02C)
    Remplacer RAM    [--] : E[U] =    -817  (Plotly color #7F7F7F)
    Remplacer Disque [BEST] : E[U] =    -104  (Plotly color #7F7F7F)
    Remplacer CPU    [--] : E[U] =    -646  (Plotly color #7F7F7F)
E[U(action)] - utilite attendue par action-983.103-719.121-455.14-191.15972.822Rien [--]Remplacer RAM [--]Remplacer Disque [BEST]Remplacer CPU [--]


Precision inferee des logs : 10,00 (shape=5,0, scale=2,00)

Interpretation du diagnostic multi-sources avec Infer.NET

Analyse de la combinaison des sources :

Source Evidence Fiabilite Contribution au diagnostic
Logs système Disque: 0.65 Moyenne (precision a inferer) Forte suggestion vers Disque
Avis utilisateur “Disque” Matrice de confusion (75% correct si Disque) Confirmation partielle
Capteur RAM Pas d’alerte Très haute (95% detection RAM) Exclut fortement RAM

Résultats posterieurs :

Panne Prior Posterior Evolution
OK 25% 5.4% Diminue (symptomes presents)
RAM 25% 1.1% Fortement diminue (capteur negatif)
Disque 25% 78.3% Fortement augmente (logs + utilisateur)
CPU 25% 15.2% Moderement augmente

Observation cle : Le capteur RAM (très fiable) a un impact decisif. L’absence d’alerte RAM reduit P(RAM) de 25% a 1.1% - une division par 23 !

Valeur ajoutee de l’approche bayesienne avec Infer.NET :

  1. Combinaison coherente : Les trois sources sont fusionnees selon leurs fiabilites respectives
  2. Incertitude propagee : Le posterior 78.3% (pas 100%) reflete l’incertitude residuelle
  3. Decision optimale : EU(Remplacer Disque) = -104, bien meilleur que les autres options

Matrice de couts interpretee :

                     Vraie panne
                OK      RAM     Disque   CPU
Action    +----------------------------------
Rien      |    0     -500    -1000    -800   <- Cout d'inaction
Rempl RAM |  -100      0      -900    -700   <- Intervention inutile si pas RAM
Rempl Disk|  -150    -400       0     -600   <- Optimal si Disque
Rempl CPU |  -300    -300     -800      0    <- Intervention couteuse

La decision “Remplacer Disque” est robuste car : - P(Disque) = 78.3% → action optimale dans le cas le plus probable - Cout modere (-150) si c’etait OK - Evite le cout catastrophique (-1000) de ne rien faire si c’est Disque

// Visualisation du graphe de facteurs du systeme expert multi-sources.
// 1) Infer.NET n'ecrit le .gv qu'a la PREMIERE compilation d'un moteur : on recree
//    un moteur neuf (engineExpertFG) avec ShowFactorGraph = true avant la 1ere inference.
// 2) Le rendu suppose Graphviz (dot) installe et dans le PATH.

// (b) Recreer un moteur avec ShowFactorGraph = true AVANT la 1ere inference
var engineExpertFG = new InferenceEngine();
engineExpertFG.Compiler.CompilerChoice = Microsoft.ML.Probabilistic.Compiler.CompilerChoice.Roslyn;
engineExpertFG.Algorithm = new ExpectationPropagation();
engineExpertFG.ShowFactorGraph = true;
engineExpertFG.Infer<Discrete>(panne);

// (c) Afficher le graphe de facteurs
FactorGraphHelper.GetLatestFactorGraphHtml(900).DisplayAs("text/html");
Compiling model...done.
Model_10_03_26_08_51_07_37.svg
Model node0 4 node1 DiscreteUniform node0->node1 size node2 panne node1->node2 node5 2 node2->node5 condition node14 False node2->node14 condition node3 vDiscrete0 node4 Random node3->node4 dist node4->node5 node6 vDiscrete1 node7 Random node6->node7 dist node7->node5 node8 vDiscrete2 node9 Random node8->node9 dist node9->node5 node10 vDiscrete3 node11 Random node10->node11 dist node11->node5 node12 Bernoulli(0,02) node13 Random node12->node13 dist node13->node14 node15 Bernoulli(0,95) node16 Random node15->node16 dist node16->node14 node17 Bernoulli(0,05) node18 Random node17->node18 dist node18->node14 node19 Bernoulli(0,08) node20 Random node19->node20 dist node20->node14

Analyse du graphe de facteurs du système expert

Le graphe de facteurs ci-dessus visualise la structure du modèle bayesien :

Noeuds variables (ellipses) : - panne : Variable latente discrete (4 etats : OK, RAM, Disque, CPU) - avisUser : Observation de l’avis utilisateur (conditionne sur panne) - alerteRAM : Observation du capteur RAM (conditionne sur panne) - precLogs : Precision des logs (variable continue avec prior Gamma)

Noeuds facteurs (rectangles) : - DiscreteUniform : Prior uniforme sur la panne - Variable.Case : Branches conditionnelles selon la valeur de panne - Bernoulli : Modèle du capteur RAM avec probabilités conditionnelles - Discrete : Modèle de confusion de l’utilisateur

Flux d’information : 1. Le prior uniforme initialise P(panne) 2. Les observations (avisUser=2, alerteRAM=false) propagent l’evidence 3. EP combine les messages pour calculer le posterior

Note pedagogique : Ce graphe illustre la puissance des modèles generatifs. On specifie comment les observations sont generees (→), et l’inference automatique calcule P(cause|observations) (←).

// Système expert medical avec decision robuste

public class RobustMedicalDecision
{
    // Maladies possibles
    public string[] Maladies { get; } = { "Benin", "Modere", "Grave" };
    
    // Traitements possibles
    public string[] Traitements { get; } = { "Aucun", "Leger", "Intensif" };
    
    // Utilites (incluant effets secondaires)
    public double[,] Utilites { get; } = new double[,]
    {
        // Benin, Modere, Grave
        { 100, 60, 10 },    // Aucun traitement
        { 90, 80, 40 },     // Traitement leger
        { 70, 85, 80 }      // Traitement intensif
    };
    
    public void Analyze(double[] posteriors)
    {
        Console.WriteLine("=== Analyse Decision Medicale ===\n");
        Console.WriteLine($"Posterieurs : {string.Join(", ", Maladies.Zip(posteriors, (m, p) => $"{m}={p:P0}"))}\n");
        
        // 1. Max EU
        Console.WriteLine("1. Maximisation EU :");
        string bestEU = null;
        double maxEU = double.NegativeInfinity;
        
        for (int t = 0; t < Traitements.Length; t++)
        {
            double eu = 0;
            for (int m = 0; m < Maladies.Length; m++)
                eu += posteriors[m] * Utilites[t, m];
            
            Console.WriteLine($"   E[U({Traitements[t]})] = {eu:F1}");
            if (eu > maxEU)
            {
                maxEU = eu;
                bestEU = Traitements[t];
            }
        }
        Console.WriteLine($"   => {bestEU}\n");
        
        // 2. Minimax (pire cas)
        Console.WriteLine("2. Minimax (conservateur) :");
        var (minmaxBest, _) = MinimaxDecision.Solve(Traitements, Maladies, Utilites);
        Console.WriteLine($"\n   => {minmaxBest}\n");
        
        // 3. Minimax Regret
        Console.WriteLine("3. Minimax Regret :");
        var (regretBest, _, _) = MinimaxRegretDecision.Solve(Traitements, Maladies, Utilites);
        Console.WriteLine($"   => {regretBest}\n");
        
        // Recommandation
        Console.WriteLine("=== Recommandation ===\n");
        
        // Si forte probabilité de cas grave, etre conservateur
        if (posteriors[2] > 0.3)
        {
            Console.WriteLine($"ATTENTION : P(Grave) = {posteriors[2]:P0} > 30%");
            Console.WriteLine($"Recommandation conservative : {minmaxBest}");
        }
        else
        {
            Console.WriteLine($"Recommandation basee sur EU : {bestEU}");
        }
    }
}

var robustDecision = new RobustMedicalDecision();

// Scénario 1 : Faible risque
Console.WriteLine("\n========== SCENARIO 1 : Faible risque ==========");
robustDecision.Analyze(new[] { 0.70, 0.25, 0.05 });

// Scénario 2 : Risque eleve
Console.WriteLine("\n========== SCENARIO 2 : Risque eleve ==========");
robustDecision.Analyze(new[] { 0.20, 0.40, 0.40 });

========== SCENARIO 1 : Faible risque ==========
=== Analyse Decision Medicale ===

Posterieurs : Benin=70 %, Modere=25 %, Grave=5 %

1. Maximisation EU :
   E[U(Aucun)] = 85,5
   E[U(Leger)] = 85,0
   E[U(Intensif)] = 74,2
   => Aucun

2. Minimax (conservateur) :
Analyse Minimax :

Action          |    Benin |   Modere |    Grave | Min
------------------------------------------------------------
Aucun           |      100 |       60 |       10 |     10
Leger           |       90 |       80 |       40 |     40
Intensif        |       70 |       85 |       80 |     70

   => Intensif

3. Minimax Regret :
   => Intensif

=== Recommandation ===

Recommandation basee sur EU : Aucun

========== SCENARIO 2 : Risque eleve ==========
=== Analyse Decision Medicale ===

Posterieurs : Benin=20 %, Modere=40 %, Grave=40 %

1. Maximisation EU :
   E[U(Aucun)] = 48,0
   E[U(Leger)] = 66,0
   E[U(Intensif)] = 80,0
   => Intensif

2. Minimax (conservateur) :
Analyse Minimax :

Action          |    Benin |   Modere |    Grave | Min
------------------------------------------------------------
Aucun           |      100 |       60 |       10 |     10
Leger           |       90 |       80 |       40 |     40
Intensif        |       70 |       85 |       80 |     70

   => Intensif

3. Minimax Regret :
   => Intensif

=== Recommandation ===

ATTENTION : P(Grave) = 40 % > 30%
Recommandation conservative : Intensif

Interpretation des decisions medicales robustes

Scénario 1 : Faible risque (P(Grave) = 5%)

Critère Decision Raisonnement
Max EU Aucun E[U] = 85.5 (le patient est probablement sain)
Minimax Intensif Pire cas = 70 (evite le 10 du non-traitement si grave)
Minimax Regret Intensif Minimise le regret de ne pas traiter un cas grave

Recommandation finale : Aucun traitement - Car P(Grave) < 30%, on fait confiance aux probabilités - Le sur-traitement a des couts (effets secondaires, ressources)

Scénario 2 : Risque eleve (P(Grave) = 40%)

Critère Decision Raisonnement
Max EU Intensif E[U] = 80 (le risque justifie le traitement)
Minimax Intensif Même raisonnement conservateur
Minimax Regret Intensif Tous convergent !

Recommandation finale : Traitement intensif - P(Grave) > 30% → mode conservateur active - Tous les critères convergent vers la même decision - Cette convergence renforce la confiance dans la recommandation

Lecon medicale : - Quand le risque est eleve, les critères convergent naturellement - La divergence des critères est un signal d’incertitude a communiquer au patient

9. Exercice : Système Expert de Diagnostic

Enonce

Construisez un mini-système expert pour diagnostiquer des problemes informatiques.

Symptomes : lenteur, ecran_bleu, bruit_ventilateur, surchauffe Causes possibles : virus, disque_plein, RAM_defaillante, surchauffe_CPU

  1. Definissez les probabilités conditionnelles
  2. Implementez le diagnostic bayesien
  3. Proposez une action avec critère minimax regret

Indications pour l’exercice

Pour resoudre cet exercice, vous devrez :

  1. Définir des priors realistes bases sur la fréquence des problemes informatiques
  2. Définir des likelihoods coherents : chaque cause a un profil de symptomes caractéristique
  3. Appliquer le diagnostic bayesien : calcul des posterieurs par la formule de Bayes
  4. Implementer le minimax regret pour la decision de reparation

Testez votre code en modifiant les symptomes observes pour voir comment le diagnostic change.

// Exercice : Système expert informatique complet
// Inclut : diagnostic bayesien + decision par minimax regret

var causes = new[] { "virus", "disque_plein", "RAM_defaillante", "surchauffe_CPU" };
var symptomes_possible = new[] { "lenteur", "ecran_bleu", "bruit_ventilateur", "surchauffe" };

// Actions possibles de reparation
var actions = new[] { "antivirus", "nettoyage_disque", "remplacement_RAM", "nettoyage_ventilateur" };

// Symptomes observes
var symptomes_obs = new List<string> { "lenteur", "bruit_ventilateur" };

Console.WriteLine($"Symptomes observes : {string.Join(", ", symptomes_obs)}");

// TODO 1 : Définir les priors sur les causes (somme = 1)
// Indice : le disque plein est la cause la plus frequente (~35%), suivi du virus (~30%)

// TODO 2 : Définir les likelihoods P(symptome | cause)
// Indice : chaque cause a un profil de symptomes caractéristique
// Exemple : virus cause surtout de la lenteur (0.8), rarement un ecran bleu (0.1)
// Utilisez un Dictionary<(string cause, string symptome), double>

// TODO 3 : Définir la matrice d'utilite U(action, cause)
// Indice : chaque action est efficace contre une cause spécifique (+80 a +150)
//          et a un cout si appliquee a la mauvaise cause (-5 a -50)
// Utilisez un Dictionary<(string action, string cause), double>

// TODO 4 : Calculer les posteriors bayesiens P(cause | symptomes)
// Formule : P(cause | symptomes) = P(symptomes | cause) * P(cause) / P(symptomes)
// Indice : P(symptomes | cause) = produit sur chaque symptome de P(symptome_i | cause)
//          N'oubliez pas de normaliser pour que la somme = 1

// TODO 5 : Calculer la matrice de regret et trouver l'action minimax regret
// Étapes :
//   a) Pour chaque cause, trouver la meilleure utilite (meilleure action)
//   b) Regret(action, cause) = meilleure_utilite(cause) - U(action, cause)
//   c) MaxRegret(action) = max sur les causes du regret
//   d) Action minimax regret = argmin des MaxRegret

// TODO 6 : Calculer E[U] pour chaque action et trouver l'action Max E[U]
// Formule : E[U(action)] = somme sur les causes de P(cause | symptomes) * U(action, cause)

// TODO 7 : Comparer les deux decisions (minimax regret vs max EU)
// Sont-elles identiques ? Si non, pourquoi ?
Symptomes observes : lenteur, bruit_ventilateur

Analyse de vos résultats

Après avoir implemente le code, verifiez :

  1. Les posterieurs somment-ils bien a 1 ?
  2. La cause avec le posterior le plus eleve correspond-elle aux symptomes observes ?
  3. L’action minimax regret et l’action max E[U] sont-elles identiques ?
  4. Si vous ajoutez le symptome “surchauffe”, comment le diagnostic change-t-il ?

10. Resume

Concept Application
Systèmes experts Diagnostic, configuration, conseil
Minimax Decisions critiques, erreurs couteuses
Minimax Regret Compromis optimisme/pessimisme
Hurwicz Paramètre d’optimisme ajustable
Robustesse Analyse de sensibilite, ensembles de probabilités

Pour aller plus loin

Si vous voulez… Consultez…
Decisions séquentielles DecInfer-08-Sequential
Reinforcement Learning Serie RL dans MyIA.AI.Notebooks/RL/

Prochaine étape

Dans DecInfer-08-Sequential, nous verrons :

  • Les Processus de Decision Markoviens (MDPs)
  • L’itération de valeur et de politique
  • Le reward shaping
  • Les bandits multi-bras et l’indice de Gittins
  • Introduction aux POMDPs

Références

  • Shortliffe (1976) : MYCIN: Computer-Based Medical Consultations
  • Wald (1950) : Statistical Decision Functions
  • Savage (1951) : The Theory of Statistical Decision
Retour au sommet