Infer-1-Setup : Introduction et Installation

Série : Programmation Probabiliste avec Infer.NET (1/19)
Duree estimee : 15 minutes
Prerequis : Notions de base en C# et statistiques


Objectifs

  • Comprendre les bases de la programmation probabiliste
  • Installer et configurer Infer.NET
  • Créer votre premier modèle probabiliste
  • Maîtriser les 3 étapes fondamentales : modèle, moteur, inférence

1. Introduction a la Programmation Probabiliste

Le problème de l’incertitude

Les ordinateurs sont rigoureusement logiques, mais le monde reel ne l’est pas. Considerez ces exemples :

  • Un système de reconnaissance d’ecriture manuscrite : le gribouillis peut correspondre a “hill”, “bull” ou “hello”
  • Un diagnostic medical : les symptomes peuvent indiquer plusieurs maladies possibles
  • Un système de recommandation : les préférences de l’utilisateur sont partiellement connues

Dans tous ces cas, nous devons raisonner avec de l’incertitude.

Variables aléatoires

Les variables conventionnelles (bool, int, double) ont des valeurs bien définies. La programmation probabiliste introduit les variables aléatoires qui représentent un ensemble de valeurs possibles, chacune associée a une probabilité.

// Variable classique - valeur fixe
bool estFace = true;

// Variable aléatoire - distribution de probabilité
Variable<bool> estFace = Variable.Bernoulli(0.5);  // 50% de chance

Les 3 piliers de la programmation probabiliste

  1. Modèles probabilistes : Definissent comment les observations sont générées
  2. Inférence bayesienne : Raisonne de l’effet vers la cause
  3. Apprentissage : Les paramètres eux-mêmes sont des variables aléatoires

2. Presentation d’Infer.NET

Qu’est-ce que Infer.NET ?

Infer.NET est un framework Microsoft pour l’inférence bayesienne dans les modèles graphiques. Il fait partie de la bibliotheque ML.NET.

Caractéristiques principales :

Caractéristique Description
Langage de modelisation Variables continues et discretes, univariees et multivariees
Algorithmes d’inférence Expectation Propagation (EP), Variational Message Passing (VMP), Gibbs Sampling
Performance Compile les modèles en code source optimise
Extensibilite Ajout de distributions, facteurs et algorithmes personnalises

Comment fonctionne Infer.NET ?

1. Définition du modèle    -->  API de modelisation
        |
        v
2. Compilation du modèle   -->  Génération de code source
        |
        v
3. Exécution de l'inférence -->  Calcul des distributions posterieures

3. Installation

Avant de commencer, nous devons installer les packages NuGet necessaires. Infer.NET est distribue en deux packages : - Microsoft.ML.Probabilistic : Contient les distributions, le moteur d’inférence et les algorithmes - Microsoft.ML.Probabilistic.Compiler : Permet la compilation dynamique des modèles en code optimise

L’installation via #r "nuget: télécharge automatiquement les packages et leurs dépendances.

Important : Si Graphviz est installe, la cellule suivante l’ajoute automatiquement au PATH pour permettre la visualisation des graphes de facteurs.

// Configuration multiplateforme du PATH pour Graphviz
// Cette cellule doit s'executer avant le chargement d'Infer.NET pour que le moteur trouve 'dot'.

using System.Runtime.InteropServices;

var graphvizPaths = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
    ? new[] {
        @"C:\Program Files\Graphviz\bin",
        @"C:\Program Files (x86)\Graphviz\bin"
    }
    : RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
        ? new[] {
            "/opt/homebrew/bin",  // Homebrew sur Apple Silicon
            "/usr/local/bin"      // Homebrew sur Mac Intel
        }
        : new[] {
            "/usr/bin",
            "/usr/local/bin"
        };

var dotExecutable = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dot.exe" : "dot";

foreach (var gvPath in graphvizPaths)
{
    var dotPath = System.IO.Path.Combine(gvPath, dotExecutable);
    if (!System.IO.File.Exists(dotPath))
        continue;

    var currentPath = Environment.GetEnvironmentVariable("PATH") ?? "";
    var pathEntries = currentPath.Split(
        System.IO.Path.PathSeparator,
        StringSplitOptions.RemoveEmptyEntries
    );
    var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
        ? StringComparison.OrdinalIgnoreCase
        : StringComparison.Ordinal;

    if (!pathEntries.Any(entry => string.Equals(entry, gvPath, comparison)))
    {
        Environment.SetEnvironmentVariable(
            "PATH",
            gvPath + System.IO.Path.PathSeparator + currentPath
        );
        Console.WriteLine($"Graphviz configure pour cette plateforme : {gvPath}");
    }
    else
    {
        Console.WriteLine($"Graphviz deja dans le PATH : {gvPath}");
    }

    break;
}

// Verification avec ProcessStartInfo correctement configure
try
{
    var psi = new System.Diagnostics.ProcessStartInfo {
        FileName = "dot",
        Arguments = "-V",
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        CreateNoWindow = true
    };
    using var proc = System.Diagnostics.Process.Start(psi);
    var stderr = proc.StandardError.ReadToEnd();
    var stdout = proc.StandardOutput.ReadToEnd();
    proc.WaitForExit();
    var versionOutput = string.IsNullOrWhiteSpace(stderr) ? stdout : stderr;
    if (proc.ExitCode == 0 || versionOutput.Contains("graphviz", StringComparison.OrdinalIgnoreCase))
        Console.WriteLine($"Verification OK : {versionOutput.Trim()}");
    else
        Console.WriteLine($"Graphviz trouve mais verification echouee (code {proc.ExitCode})");
}
catch
{
    var installHint = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
        ? "winget install Graphviz.Graphviz"
        : RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
            ? "brew install graphviz"
            : "sudo apt install graphviz";
    Console.WriteLine($"Graphviz non disponible (optionnel). Installation : {installHint}");
}
Graphviz configure pour cette plateforme : C:\Program Files\Graphviz\bin
Verification OK : dot - graphviz version 14.1.5 (20260411.2331)

Verification de la configuration Graphviz

La cellule précédente détecte la plateforme, recherche dot dans les emplacements usuels et utilise le séparateur de PATH fourni par .NET (; sous Windows, : sous Linux et macOS).

Message Signification
Graphviz configure pour cette plateforme : ... Le répertoire de dot a été ajouté au PATH du kernel
Graphviz deja dans le PATH : ... Le répertoire est déjà configuré dans la session
Verification OK : dot - graphviz version ... Le binaire Graphviz répond correctement
Graphviz trouve mais verification echouee (code N) Le binaire a démarré mais a renvoyé un code inattendu
Graphviz non disponible ... Installation : ... Graphviz est absent ; la commande adaptée à Windows, macOS ou Linux est indiquée

Commandes d’installation usuelles :

  • Windows : winget install Graphviz.Graphviz
  • macOS : brew install graphviz
  • Debian/Ubuntu : sudo apt install graphviz

Note : Graphviz reste optionnel pour l’inférence. Sans dot, Infer.NET peut générer les fichiers .gv, visualisables ensuite sur viz-js.com.

// Installation des packages NuGet Infer.NET
#r "nuget: Microsoft.ML.Probabilistic"
#r "nuget: Microsoft.ML.Probabilistic.Compiler"
Installing Packages
  • Microsoft.ML.Probabilistic
  • Microsoft.ML.Probabilistic.Compiler

Note : Infer.NET fait desormais partie de la bibliotheque ML.NET de Microsoft.

// Import des espaces de noms essentiels
using Microsoft.ML.Probabilistic;
using Microsoft.ML.Probabilistic.Distributions;
using Microsoft.ML.Probabilistic.Utilities;
using Microsoft.ML.Probabilistic.Math;
using Microsoft.ML.Probabilistic.Models;
using Microsoft.ML.Probabilistic.Algorithms;
using Microsoft.ML.Probabilistic.Compiler;

Console.WriteLine("Infer.NET charge avec succes !");
Console.WriteLine($"  - Microsoft.ML.Probabilistic.Models : Variables aleatoires et modeles");
Console.WriteLine($"  - Microsoft.ML.Probabilistic.Distributions : Gaussian, Beta, Dirichlet...");
Console.WriteLine($"  - Microsoft.ML.Probabilistic.Algorithms : EP, VMP, Gibbs");
Console.WriteLine($"  - Microsoft.ML.Probabilistic.Compiler : Compilation Roslyn des modeles");
Infer.NET charge avec succes !
  - Microsoft.ML.Probabilistic.Models : Variables aleatoires et modeles
  - Microsoft.ML.Probabilistic.Distributions : Gaussian, Beta, Dirichlet...
  - Microsoft.ML.Probabilistic.Algorithms : EP, VMP, Gibbs
  - Microsoft.ML.Probabilistic.Compiler : Compilation Roslyn des modeles

Chargement du helper de visualisation des graphes de facteurs.

// Chargement du helper pour la visualisation des factor graphs
// (Une version inline est aussi definie plus bas dans ce notebook)
#load "FactorGraphHelper.cs"

Console.WriteLine("FactorGraphHelper.cs charge depuis le fichier externe !");
Console.WriteLine("Usage: moteur.ShowFactorGraph = true; puis display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml()))");
FactorGraphHelper.cs charge depuis le fichier externe !
Usage: moteur.ShowFactorGraph = true; puis display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml()))

Espaces de noms Infer.NET charges

La cellule précédente a importe les espaces de noms essentiels. Voici leur rôle :

Namespace Contenu Usage principal
Microsoft.ML.Probabilistic.Models Variable<T>, VariableArray<T>, Range Définition des modèles probabilistes
Microsoft.ML.Probabilistic.Distributions Gaussian, Beta, Gamma, Dirichlet Distributions de probabilité
Microsoft.ML.Probabilistic.Algorithms ExpectationPropagation, VariationalMessagePassing Algorithmes d’inférence
Microsoft.ML.Probabilistic.Compiler InferenceEngine, CompilerChoice Compilation et exécution
Microsoft.ML.Probabilistic.Math Fonctions mathematiques Opérations sur distributions

Architecture Infer.NET : Le moteur d’inférence compile votre modèle en code C# optimise, stocke dans GeneratedSource/. Cette approche “compile une fois, execute plusieurs fois” offre d’excellentes performances.

4. Premier Exemple : Le Lancer de Deux Pieces

Enonce du problème

Quelle est la probabilité d’obtenir deux faces lors du lancer de deux pieces non biaisées ?

Solution mathematique

\[P(\text{deux faces}) = P(\text{face}_1) \times P(\text{face}_2) = 0.5 \times 0.5 = 0.25\]

Solution avec Infer.NET

// Étape 1 : Définition du modèle probabiliste
Variable<bool> premierePiece = Variable.Bernoulli(0.5);   // Piece 1 : 50% face
Variable<bool> deuxiemePiece = Variable.Bernoulli(0.5);   // Piece 2 : 50% face
Variable<bool> deuxFaces = premierePiece & deuxiemePiece; // ET logique

// Étape 2 : Création du moteur d'inference
InferenceEngine moteur = new InferenceEngine();
moteur.Compiler.CompilerChoice = CompilerChoice.Roslyn;  // Necessaire pour .NET Interactive

// Étape 3 : Execution de l'inference
var resultat = moteur.Infer(deuxFaces);
Console.WriteLine($"Probabilite d'obtenir deux faces : {resultat}");
Compiling model...done.
Probabilite d'obtenir deux faces : Bernoulli(0,25)

Analyse de la sortie

Le résultat Bernoulli(0,25) représente une distribution de Bernoulli avec paramètre p = 0.25 :

  • Interpretation : La variable deuxFaces a 25% de chance d’être vraie
  • Coherence mathematique : 0.5 × 0.5 = 0.25 ✓

Remarquez les messages “Compiling model… done.” lors de la première exécution. Infer.NET compile dynamiquement votre modèle en code C# optimise, stocke dans un répertoire GeneratedSource/. Cette compilation n’a lieu qu’une fois par modèle.

Pourquoi une distribution et pas juste 0.25 ?
Infer.NET retourne toujours des distributions complètes, pas seulement des valeurs ponctuelles. Cela permet de propager l’incertitude dans des modèles plus complexes.

5. Les 3 Étapes Fondamentales

Tout programme Infer.NET suit ces 3 étapes :

Étape 1 : Définition du Modèle

Créez des variables aléatoires et definissez leurs relations.

// Variables aléatoires avec leurs distributions a priori
Variable<bool> A = Variable.Bernoulli(0.5);
Variable<bool> B = Variable.Bernoulli(0.5);

// Variable derivee (dependante)
Variable<bool> C = A & B;  // C depend de A et B

Étape 2 : Création du Moteur d’Inférence

Le moteur compile le modèle et prepare l’algorithme d’inférence.

InferenceEngine moteur = new InferenceEngine();
moteur.Compiler.CompilerChoice = CompilerChoice.Roslyn;  // Pour notebooks

Étape 3 : Exécution de l’Inférence

Demandez au moteur de calculer la distribution d’une variable.

var résultat = moteur.Infer(C);  // Distribution marginale de C

6. Types de Distributions Fondamentales

Infer.NET supporte de nombreuses distributions. Voici les plus courantes :

Distribution Type Usage Exemple
Bernoulli Discrete Événement binaire Pile/Face, Vrai/Faux
Discrete Discrete Catégorie parmi N Choix d’un jour, couleur
Gaussian Continue Valeur reelle avec incertitude Taille, temperature
Gamma Continue Precision, variance Bruit, fiabilite
Beta Continue Probabilité inconnue Taux de succes
Dirichlet Continue Vecteur de probabilités Poids de mélange
// Exemples de création de variables aleatoires

// Bernoulli : probabilité d'un événement binaire
Variable<bool> pluie = Variable.Bernoulli(0.3);  // 30% de chance de pluie

// Discrete : choix parmi N options
Variable<int> jourSemaine = Variable.DiscreteUniform(7);  // Uniform sur 0-6

// Gaussian : valeur continue avec incertitude
Variable<double> temperature = Variable.GaussianFromMeanAndVariance(20, 4);  // Moyenne 20, variance 4

// Gamma : precision (inverse de la variance)
Variable<double> precision = Variable.GammaFromShapeAndScale(2, 0.5);

Console.WriteLine("Variables creees avec succes !");
Variables creees avec succes !

Interpretation des variables créées

La cellule précédente a crée quatre variables aléatoires. Voici ce que représente chacune :

Variable Type Distribution Paramètres Interpretation
pluie Variable<bool> Bernoulli(0.3) p = 0.3 30% de chance de pluie
jourSemaine Variable<int> DiscreteUniform(7) n = 7 Chaque jour equiprobable (1/7)
temperature Variable<double> Gaussian(20, 4) mu = 20, var = 4 Temperature moyenne 20 degrés, ecart-type 2
precision Variable<double> Gamma(2, 0.5) shape = 2, scale = 0.5 Precision avec moyenne 1, mode 0.5

Rappel sur les parametrisations :

  • Gaussian(mean, variance) : La variance est le carre de l’ecart-type
  • Gamma(shape, scale) : Moyenne = shape × scale, mode = (shape - 1) × scale si shape >= 1

Attention : Ces variables ne sont pas encore “liées” entre elles. Dans un modèle complet, on definirait des dépendances (ex: la temperature influence la probabilité de pluie).

7. Exemple Avance : Pièce Biaisée

Considerons une pièce potentiellement biaisée. Nous observons 7 faces sur 10 lancers. Quel est le biais de la pièce ?

Modèle bayesien pour l’estimation de biais

Le code suivant illustre les étapes clés d’un modèle bayesien :

  1. Prior : Beta(1,1) = distribution uniforme, aucune croyance initiale sur le biais
  2. Vraisemblance : Chaque lancer suit Bernoulli(biais)
  3. Observations : 7 faces sur 10 lancers
  4. Inférence : Calcul de la distribution posterieure du biais

Beta(1,1) est un prior “non-informatif” : il donne la même probabilité a toutes les valeurs de biais entre 0 et 1. C’est équivalent a dire “je n’ai aucune idee a priori du biais de la pièce”.

// Modèle pour estimer le biais d'une piece

// A priori : le biais peut être n'importe quelle valeur entre 0 et 1
// Beta(1,1) = distribution uniforme sur [0,1]
Variable<double> biais = Variable.Beta(1, 1);

// Observations : 10 lancers
int nombreLancers = 10;
Range lancers = new Range(nombreLancers);
VariableArray<bool> resultats = Variable.Array<bool>(lancers);

// Chaque lancer suit une distribution Bernoulli avec le biais inconnu
using (Variable.ForEach(lancers))
{
    resultats[lancers] = Variable.Bernoulli(biais);
}

// Observations : 7 faces (true), 3 piles (false)
resultats.ObservedValue = new bool[] { true, true, true, true, true, true, true, false, false, false };

// Inference
InferenceEngine moteur = new InferenceEngine();
moteur.Compiler.CompilerChoice = CompilerChoice.Roslyn;

Beta biaisPosterieur = moteur.Infer<Beta>(biais);
Console.WriteLine($"Distribution a posteriori du biais : {biaisPosterieur}");
Console.WriteLine($"Moyenne estimee du biais : {biaisPosterieur.GetMean():F3}");
Console.WriteLine($"Intervalle de confiance (ecart-type) : +/- {Math.Sqrt(biaisPosterieur.GetVariance()):F3}");
Compiling model...done.
Distribution a posteriori du biais : Beta(8,4)[mean=0,6667]
Moyenne estimee du biais : 0,667
Intervalle de confiance (ecart-type) : +/- 0,131

Visualisation du graphe de facteurs du modèle de biais de pièce.

// Visualisation du factor graph pour le modèle de piece biaisée
// Recreons le modèle avec ShowFactorGraph = true

Variable<double> biaisViz = Variable.Beta(1, 1).Named("biais");
int nLancers = 10;
Range lancersViz = new Range(nLancers).Named("lancers");
VariableArray<bool> resultatsViz = Variable.Array<bool>(lancersViz).Named("resultats");
using (Variable.ForEach(lancersViz))
{
    resultatsViz[lancersViz] = Variable.Bernoulli(biaisViz);
}
resultatsViz.ObservedValue = new bool[] { true, true, true, true, true, true, true, false, false, false };

InferenceEngine moteurViz = new InferenceEngine();
moteurViz.Compiler.CompilerChoice = CompilerChoice.Roslyn;
moteurViz.ShowFactorGraph = true;

Beta biaisPost = moteurViz.Infer<Beta>(biaisViz);
Console.WriteLine($"Biais estime: {biaisPost}");

// Afficher le factor graph
display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml()));
Compiling model...done.
Biais estime: Beta(8,4)[mean=0,6667]
Model_09_12_26_13_44_35_46.svg
Model node0 Beta(1,1)[mean=0,5] node1 Random node0->node1 dist node2 biais node1->node2 node3 Bernoulli node2->node3 probTrue node4 resultats[lancers] node3->node4

Factor Graph : Modèle de Pièce Biaisée

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

Legende du graphe : - Rectangles : Facteurs (distributions de probabilité) - Ellipses : Variables aléatoires - Fleches : Connexions entre facteurs et variables

Structure du modèle :

[Beta(1,1)] --> (biais) --> [Bernoulli] --> (résultats[0..9]) <-- [Observed: T,T,T,T,T,T,T,F,F,F]

Interpretation : 1. Le prior Beta(1,1) initialise notre croyance sur le biais de la pièce (uniforme) 2. La variable biais est partagee par tous les facteurs Bernoulli (conjugaison) 3. Chaque résultat[i] est génère independamment par Bernoulli(biais) 4. Les observations (7 faces, 3 piles) contraignent le modèle

Flux des messages : - Durant l’inférence, les messages de vraisemblance remontent des observations vers le biais - Le facteur Beta combine ces messages avec le prior pour produire le posterior Beta(8,4)

Rendu Graphviz : Graphviz est détecté dans le PATH du kernel et le helper convertit le fichier .gv généré par Infer.NET en SVG affiché directement dans la sortie précédente. Si dot est absent sur une autre plateforme, le fichier .gv reste visualisable sur viz-js.com ou edotor.net.

Analyse detaillee du résultat

Sortie obtenue : Beta(8,4)[mean=0,6667]

La distribution Beta(α, β) représente notre croyance mise a jour sur le biais de la pièce :

Paramètre Valeur Signification
α (alpha) 8 1 (prior) + 7 (faces observées)
β (beta) 4 1 (prior) + 3 (piles observes)
Moyenne 0.667 α / (α + β) = 8/12
Ecart-type 0.131 Mesure de l’incertitude residuelle

Pourquoi Beta(8,4) et pas simplement 7/10 ?

  1. Integration du prior : Nous avons commence avec Beta(1,1), équivalent a avoir déjà observe 1 face et 1 pile
  2. Formule de mise a jour : Beta(α + n_faces, β + n_piles) = Beta(1+7, 1+3) = Beta(8,4)
  3. Moyenne legerement biaisée : 0.667 vs 0.700 (l’effet du prior diminue avec plus d’observations)

Concept clé : Conjugaison
La distribution Beta est conjuguee a Bernoulli : prior Beta + observations Bernoulli = posterior Beta.
Cette propriete permet une mise a jour analytique exacte, sans approximation.

8. Exemple guide : Trois Pieces

Enonce

Vous lancez trois pieces non biaisées. Calculez :

  1. La probabilité d’obtenir exactement trois faces
  2. La probabilité d’obtenir au moins deux faces

Indice

Pour “au moins deux faces”, vous pouvez utiliser plusieurs variables derivees : - deuxOuTrois = (p1 & p2) | (p1 & p3) | (p2 & p3)

Solution

Code de l’exercice

La cellule suivante contient la solution de référence. Essayez de comprendre chaque ligne avant de l’executer :

  • Lignes 4-6 : Création de trois variables Bernoulli independantes
  • Ligne 9 : Conjonction logique (AND) des trois variables
  • Ligne 13 : Disjonction logique (OR) de paires - structure plus complexe
// Exemple guide : Completez ce code

// 1. Definir les trois pieces
Variable<bool> piece1 = Variable.Bernoulli(0.5);
Variable<bool> piece2 = Variable.Bernoulli(0.5);
Variable<bool> piece3 = Variable.Bernoulli(0.5);

// 2. Definir "trois faces"
Variable<bool> troisFaces = piece1 & piece2 & piece3;

// 3. Definir "au moins deux faces"
// Hint: (p1&p2) | (p1&p3) | (p2&p3)
Variable<bool> auMoinsDeuxFaces = (piece1 & piece2) | (piece1 & piece3) | (piece2 & piece3);

// 4. Inference
InferenceEngine moteur = new InferenceEngine();
moteur.Compiler.CompilerChoice = CompilerChoice.Roslyn;

Console.WriteLine($"P(trois faces) = {moteur.Infer(troisFaces)}");
Console.WriteLine($"P(au moins deux faces) = {moteur.Infer(auMoinsDeuxFaces)}");

// Verification mathematique :
// P(trois faces) = 0.5^3 = 0.125
// P(au moins 2) = P(2) + P(3) = C(3,2)*0.5^3 + 0.5^3 = 3*0.125 + 0.125 = 0.5
Console.WriteLine("\nVerification : P(3 faces) = 1/8 = 0.125, P(>=2 faces) = 4/8 = 0.5");
Compiling model...done.
P(trois faces) = Bernoulli(0,125)
P(au moins deux faces) = Bernoulli(0,5781)

Verification : P(3 faces) = 1/8 = 0.125, P(>=2 faces) = 4/8 = 0.5

Analyse des résultats de l’exercice

Résultats obtenus : - P(trois faces) = Bernoulli(0,125) ✓ Exact (1/8) - P(au moins deux faces) = Bernoulli(0,5781) ≠ Valeur théorique (0.5)

Pourquoi cette différence ?

La valeur 0.5781 au lieu de 0.5 s’explique par l’algorithme d’inférence approchee utilise par Infer.NET (Expectation Propagation).

Pour “au moins deux faces”, la vraie probabilité est : - P(exactement 2 faces) = C(3,2) × 0.5³ = 3 × 0.125 = 0.375 - P(exactement 3 faces) = 0.125 - Total = 0.5

L’ecart vient de la complexite des correlations entre variables dans l’expression (p1&p2) | (p1&p3) | (p2&p3). Les mêmes variables apparaissent dans plusieurs termes, creant des dépendances que l’algorithme EP approxime.

Bon a savoir : Infer.NET utilise des algorithmes d’inférence variationnelle qui peuvent introduire de legeres erreurs sur des modèles avec des structures de dépendance complexes. Pour des calculs exacts sur des modèles discrets simples, d’autres approches (enumeration, junction tree) seraient plus précises.

8bis. Troubleshooting et Debogage

Cette section couvre les problemes courants avec Infer.NET et leurs solutions.

Erreurs Communes

Erreur Cause Solution
Model has no support Variable observée incompatible avec le prior Verifier que les observations sont dans le support du prior
Improper distribution Divergence de l’inférence Utiliser des priors plus informatifs ou changer d’algorithme
Could not find method Opération non supportée Reformuler le modèle avec des opérations supportées
Compiler error Syntax ou type invalide Verifier les types des variables et opérateurs

Choix de l’Algorithme d’Inférence

Algorithme Force Faiblesse Usage recommande
EP Rapide, bon pour gaussiennes Approximatif, peut diverger Modèles continus, facteurs mixtes
VMP Stable, bon pour discret Sous-estime l’incertitude LDA, modèles a composantes
Gibbs Exact asymptotiquement Lent, convergence difficile Validation, petits modèles

Comment changer d’algorithme

// Expectation Propagation (defaut)
moteur.Algorithm = new ExpectationPropagation();

// Variational Message Passing
moteur.Algorithm = new VariationalMessagePassing();

// Gibbs Sampling
moteur.Algorithm = new GibbsSampling();

Note : La configuration de Graphviz a ete effectuee en section 3. Les fichiers .gv generes peuvent être visualises sur viz-js.com si Graphviz n’est pas installe.

// Inspection du code genere et du factor graph

Console.WriteLine("=== Options de Debug Infer.NET ===\n");

// Verification de Graphviz (le PATH a ete configure au debut du notebook)
bool graphvizInstalle = false;
try
{
    var psi = new System.Diagnostics.ProcessStartInfo {
        FileName = "dot",
        Arguments = "-V",
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        CreateNoWindow = true
    };
    using var proc = System.Diagnostics.Process.Start(psi);
    var stderr = proc.StandardError.ReadToEnd();
    proc.WaitForExit();
    if (proc.ExitCode == 0 || stderr.Contains("graphviz"))
    {
        Console.WriteLine($"Graphviz disponible : {stderr.Trim()}");
        graphvizInstalle = true;
    }
}
catch (Exception ex) 
{ 
    Console.WriteLine($"Verification Graphviz echouee : {ex.Message}");
}

if (!graphvizInstalle)
{
    Console.WriteLine("Graphviz non detecte - les fichiers .gv seront generes");
    Console.WriteLine("Visualisez-les sur : https://viz-js.com/\n");
}
Console.WriteLine();

// Création d'un modèle simple pour demonstration
Variable<double> x = Variable.GaussianFromMeanAndPrecision(0, 1).Named("x");
Variable<double> y = Variable.GaussianFromMeanAndPrecision(x, 1).Named("y");
y.ObservedValue = 2.0;

// Moteur avec options de debug
InferenceEngine moteurDebug = new InferenceEngine();
moteurDebug.Compiler.CompilerChoice = CompilerChoice.Roslyn;

// Option 1 : Afficher le schedule d'inference
moteurDebug.ShowSchedule = true;
Console.WriteLine("1. ShowSchedule = true : Affiche l'ordre des calculs de messages\n");

// Option 2 : Afficher le factor graph (genere .gv et .svg si Graphviz disponible)
moteurDebug.ShowFactorGraph = true;
Console.WriteLine("2. ShowFactorGraph = true : Genere fichier DOT" + 
    (graphvizInstalle ? " + SVG" : " (SVG si Graphviz disponible)") + "\n");

// Option 3 : Sauvegarder le code genere
moteurDebug.Compiler.WriteSourceFiles = true;
moteurDebug.Compiler.GeneratedSourceFolder = "GeneratedSource";
Console.WriteLine("3. WriteSourceFiles = true : Sauvegarde le code C# genere dans GeneratedSource/\n");

// Option 4 : Montrer les avertissements de compilation
moteurDebug.Compiler.ShowWarnings = true;
Console.WriteLine("4. ShowWarnings = true : Affiche les avertissements du compilateur\n");

// Executer l'inference
Console.WriteLine("--- Execution avec options debug ---\n");
var xPost = moteurDebug.Infer<Gaussian>(x);
Console.WriteLine($"\nResultat : x ~ {xPost}");

// Note : Le FactorGraphHelper est défini plus bas dans ce notebook.
// Une fois charge, utilisez : display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml()))
Console.WriteLine("\n--- Factor Graph ---");
Console.WriteLine("Fichiers .gv generes. Voir section FactorGraphHelper ci-dessous pour l'affichage inline.");
=== Options de Debug Infer.NET ===

Graphviz disponible : dot - graphviz version 14.1.5 (20260411.2331)

1. ShowSchedule = true : Affiche l'ordre des calculs de messages

2. ShowFactorGraph = true : Genere fichier DOT + SVG

3. WriteSourceFiles = true : Sauvegarde le code C# genere dans GeneratedSource/

4. ShowWarnings = true : Affiche les avertissements du compilateur

--- Execution avec options debug ---

Compiling model...done.

Resultat : x ~ Gaussian(1, 0,5)

--- Factor Graph ---
Fichiers .gv generes. Voir section FactorGraphHelper ci-dessous pour l'affichage inline.

Interpretation du résultat d’inférence en mode debug

Sortie obtenue : x ~ Gaussian(1, 0.5)

Ce résultat représente la distribution posterieure de la variable x après avoir observe y = 2.0.

Paramètre Valeur Signification
Moyenne 1.0 Estimation centrale de x
Variance 0.5 Incertitude sur x (ecart-type ~ 0.71)

Pourquoi cette mise a jour ?

  1. Prior : x ~ N(0, 1) - nous pensions que x était proche de 0
  2. Observation : y = 2.0, avec y ~ N(x, 1)
  3. Posterior : x ~ N(1, 0.5) - compromis entre le prior et l’observation

Le posterior est “tire” vers l’observation (moyenne passe de 0 a 1) tout en reduisant l’incertitude (variance passe de 1 a 0.5).

Formule analytique (cas gaussien) :
La moyenne posterieure = (prior_precision × prior_mean + likelihood_precision × observation) / (prior_precision + likelihood_precision)
Soit : (1×0 + 1×2) / (1+1) = 1

Utilisation des Options de Debug

Quand utiliser chaque option :

Option Utilite
ShowSchedule Comprendre l’ordre des messages, identifier les boucles infinies
ShowFactorGraph Visualiser le modèle, verifier les connections
WriteSourceFiles Inspecter le code génère, optimiser manuellement
ShowWarnings Detecter les approximations, opérateurs experimentaux

Fichiers generes :

Le dossier GeneratedSource/ contient : - Model_EP.cs : Code d’inférence pour EP - Model_VMP.cs : Code d’inférence pour VMP - Ces fichiers sont du C# pur, compilable et executable

Visualisation des Factor Graphs

Lorsque ShowFactorGraph = true est active, Infer.NET génère des fichiers .gv (format DOT Graphviz) decrivant la structure du modèle.

Avec Graphviz installe : La conversion en SVG est automatique.

Sans Graphviz : Les fichiers .gv peuvent être visualises sur viz-js.com ou edotor.net.

Structure d’un Factor Graph :

[Prior N(0,1)] ----> (Variable x) ----> [GaussianFromMeanAndPrecision] ----> (Variable y = 2.0 observe)

Dans ce graphe : - Les rectangles représentent les facteurs (distributions) - Les cercles représentent les variables aléatoires - Les fleches indiquent le flux de messages entre facteurs et variables

Helper : Affichage inline des Factor Graphs

Le code suivant définit un helper FactorGraphHelper pour afficher les graphes de facteurs directement dans les cellules du notebook. Ce helper : - Detecte automatiquement les fichiers .gv generes par Infer.NET - Les convertit en SVG via Graphviz - Les affiche inline dans la sortie de la cellule

// Helper pour afficher les graphes de facteurs (SVG) inline dans le notebook

using System.IO;
using System.Linq;
using Microsoft.DotNet.Interactive.Formatting;

/// <summary>
/// Helper pour generer et afficher les factor graphs Infer.NET dans les notebooks
/// </summary>
public static class FactorGraphHelper
{
    private static bool? _graphvizAvailable = null;
    
    /// <summary>
    /// Verifie si Graphviz (dot) est disponible
    /// </summary>
    public static bool IsGraphvizAvailable()
    {
        if (_graphvizAvailable.HasValue) return _graphvizAvailable.Value;
        try
        {
            var psi = new System.Diagnostics.ProcessStartInfo {
                FileName = "dot", Arguments = "-V",
                RedirectStandardError = true, RedirectStandardOutput = true,
                UseShellExecute = false, CreateNoWindow = true
            };
            using var proc = System.Diagnostics.Process.Start(psi);
            proc.WaitForExit(3000);
            _graphvizAvailable = (proc.ExitCode == 0);
        }
        catch { _graphvizAvailable = false; }
        return _graphvizAvailable.Value;
    }
    
    /// <summary>
    /// Retourne le HTML pour afficher le dernier fichier SVG genere par Infer.NET
    /// Usage: display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml()))
    /// </summary>
    public static string GetLatestFactorGraphHtml(int maxWidth = 800)
    {
        var svgFiles = Directory.GetFiles(Environment.CurrentDirectory, "Model_*.svg");
        
        if (svgFiles.Length > 0)
        {
            var latestSvg = svgFiles
                .OrderByDescending(f => new FileInfo(f).LastWriteTime)
                .First();
            return GetSvgFileHtml(latestSvg, maxWidth);
        }
        else
        {
            return ConvertAndGetLatestGvHtml(maxWidth);
        }
    }

    /// <summary>
    /// Retourne le HTML pour afficher un fichier SVG inline
    /// </summary>
    public static string GetSvgFileHtml(string svgPath, int maxWidth = 800)
    {
        if (!File.Exists(svgPath))
            return $"<div style='color:red'>Fichier non trouve : {svgPath}</div>";

        var svgContent = File.ReadAllText(svgPath);
        var fileName = Path.GetFileName(svgPath);
        return GetSvgContentHtml(svgContent, fileName, maxWidth);
    }
    
    /// <summary>
    /// Retourne le HTML pour afficher du contenu SVG
    /// </summary>
    public static string GetSvgContentHtml(string svgContent, string title = "Factor Graph", int maxWidth = 800)
    {
        return $@"
<div style=""margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px; background: #fafafa;"">
    <div style=""font-weight: bold; margin-bottom: 8px; color: #333;"">{title}</div>
    <div style=""max-width: {maxWidth}px; overflow: auto;"">
        {svgContent}
    </div>
</div>";
    }

    /// <summary>
    /// Convertit un fichier .gv en SVG et retourne le HTML
    /// </summary>
    public static string ConvertAndGetGvHtml(string gvPath, int maxWidth = 800)
    {
        if (!File.Exists(gvPath))
            return $"<div style='color:red'>Fichier .gv non trouve : {gvPath}</div>";

        if (!IsGraphvizAvailable())
        {
            var fileName = Path.GetFileName(gvPath);
            return $@"<div style='padding: 10px; border: 1px solid #f0ad4e; background: #fcf8e3; border-radius: 5px;'>
                <strong>Graphviz non disponible.</strong><br/>
                Copiez le contenu de <code>{fileName}</code> sur <a href='https://viz-js.com/' target='_blank'>viz-js.com</a>
            </div>";
        }

        var svgPath = Path.ChangeExtension(gvPath, ".svg");

        try
        {
            var psi = new System.Diagnostics.ProcessStartInfo
            {
                FileName = "dot",
                Arguments = $"-Tsvg \"{gvPath}\" -o \"{svgPath}\"",
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            using var proc = System.Diagnostics.Process.Start(psi);
            proc.WaitForExit(5000);

            if (proc.ExitCode == 0 && File.Exists(svgPath))
                return GetSvgFileHtml(svgPath, maxWidth);
            else
            {
                var error = proc.StandardError.ReadToEnd();
                return $"<div style='color:red'>Erreur Graphviz : {error}</div>";
            }
        }
        catch (Exception ex)
        {
            return $"<div style='color:red'>Erreur : {ex.Message}</div>";
        }
    }

    /// <summary>
    /// Trouve et convertit le dernier fichier .gv
    /// </summary>
    public static string ConvertAndGetLatestGvHtml(int maxWidth = 800)
    {
        var gvFiles = Directory.GetFiles(Environment.CurrentDirectory, "Model_*.gv");

        if (gvFiles.Length == 0)
        {
            return @"<div style='padding: 10px; border: 1px solid #d9534f; background: #f2dede; border-radius: 5px;'>
                Aucun fichier .gv trouve.<br/>
                Activez <code>ShowFactorGraph = true</code> sur le moteur d'inference.
            </div>";
        }

        var latestGv = gvFiles
            .OrderByDescending(f => new FileInfo(f).LastWriteTime)
            .First();

        return ConvertAndGetGvHtml(latestGv, maxWidth);
    }

    /// <summary>
    /// Nettoie les fichiers .gv et .svg generes
    /// </summary>
    public static int CleanupGeneratedFiles()
    {
        var files = Directory.GetFiles(Environment.CurrentDirectory, "Model_*.gv")
            .Concat(Directory.GetFiles(Environment.CurrentDirectory, "Model_*.svg"))
            .Concat(Directory.GetFiles(Environment.CurrentDirectory, "Task_Graph_*.gv"));

        int count = 0;
        foreach (var file in files)
        {
            try { File.Delete(file); count++; } catch { }
        }
        return count;
    }
}

Console.WriteLine("FactorGraphHelper charge !");
Console.WriteLine("  - display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml())) : affiche le dernier graphe");
Console.WriteLine("  - FactorGraphHelper.CleanupGeneratedFiles() : nettoie les fichiers generes");
FactorGraphHelper charge !
  - display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml())) : affiche le dernier graphe
  - FactorGraphHelper.CleanupGeneratedFiles() : nettoie les fichiers generes

Utilisation du FactorGraphHelper

Une fois la cellule précédente executee, vous pouvez afficher les graphes de facteurs directement dans le notebook :

// Afficher le dernier graphe génère
display(HTML(FactorGraphHelper.GetLatestFactorGraphHtml()));

// Nettoyer les fichiers temporaires
FactorGraphHelper.CleanupGeneratedFiles();

Méthodes disponibles :

Méthode Description
GetLatestFactorGraphHtml() Affiche le dernier graphe SVG inline
GetSvgFileHtml(path) Affiche un fichier SVG spécifique
ConvertAndGetGvHtml(path) Convertit un .gv en SVG et l’affiche
CleanupGeneratedFiles() Supprime les fichiers .gv et .svg generes
IsGraphvizAvailable() Verifie si Graphviz est installe

9. Resume

Dans ce notebook, vous avez appris :

Concept Description
Programmation probabiliste Representer l’incertitude avec des variables aléatoires
Infer.NET Framework Microsoft pour l’inférence bayesienne
Les 3 étapes Modèle -> Moteur -> Inférence
Distributions de base Bernoulli, Beta, Gaussian, Gamma
Mise a jour bayesienne A priori + Observations = A posteriori

Pour aller plus loin

Si vous voulez… Consultez…
Approfondir les distributions continues Infer-2-Gaussian-Mixtures
Comprendre les graphes de facteurs Infer-3-Factor-Graphs
Debugger un modèle qui ne converge pas Infer-2b-Debugging-Bonnes-Pratiques
Trouver une définition Glossaire

Prochaine étape

Dans le notebook suivant Infer-2-Gaussian-Mixtures, nous approfondirons :

  • Les distributions gaussiennes et leur apprentissage
  • Les modèles de mélange pour les données multimodales
  • L’utilisation de Variable.Switch pour les modèles a composantes

Ressources

10. Exercice : Lancer de Deux Des

Enonce

Modelisez le lancer de deux des a 6 faces avec Infer.NET.

  1. Créez deux variables aléatoires discretes uniformes représentant chaque de (6 faces)
  2. Definissez leur somme comme variable dependante
  3. Calculez la probabilité que la somme soit superieure ou egale a 10
  4. Conditionnez sur le fait que le premier de affiche 6 (valeur = 5 en 0-indexe) et re-inferez

Indice : Utilisez Variable.DiscreteUniform(6) et Variable.If() pour conditionner.

// Exercice : Modèle de deux des a 6 faces

// TODO: Creer le moteur d inference avec Roslyn
// Indice: InferenceEngine + CompilerChoice.Roslyn


// TODO: Creer les deux variables aleatoires pour les des (valeurs 0-5 representant faces 1-6)
// Indice: utilisez Variable.DiscreteUniform() avec le bon nombre de faces


// TODO: Inferer la distribution marginale de chaque de (avant observation)
// Indice: engine.Infer<Discrete>(variable)


// TODO: Observer que le premier de montre 6 (valeur = 5) et re-inferer de2
// Indice: utilisez .ObservedValue puis re-inferez la distribution de l autre de


// Question: La distribution de de2 change-t-elle apres observation de de1 ? Pourquoi ?
Console.WriteLine("Exercice a completer");
Exercice a completer

Conclusion

Ce notebook d’introduction a pose les fondements de la programmation probabiliste avec Infer.NET :

Concept Apport du notebook
Variable<T> Variables aléatoires Bernoulli, Gaussian, Beta, Gamma, Discrete
InferenceEngine Compilation Roslyn du modèle, choix de l’algorithme (EP, VMP, Gibbs)
Flux bayesien Prior + Vraisemblance + Observations = Posterior
Conjugaison Prior Beta + vraisemblance Bernoulli = posterior Beta analytique
Factor graphs Visualisation de la structure du modèle via Graphviz
Debugging ShowFactorGraph, ShowSchedule, WriteSourceFiles

Point clé : Infer.NET retourne toujours des distributions complètes (Gaussian, Beta, Bernoulli), jamais de simples valeurs ponctuelles, ce qui permet de propager l’incertitude a travers tout le modèle.

Retour au sommet