The below script needs to be able to find the current output cell; this is an easy method to get it.
Installed Packages
Microsoft.ML.Probabilistic, 0.4.2504.701
Microsoft.ML.Probabilistic.Compiler, 0.4.2504.701
Infer.NET charge !
Chargement des utilitaires de visualisation des graphes de facteurs.
// Chargement du helper pour visualisation des graphes de facteurs// (FactorGraphHelper.cs vit dans Probas/Infer/ ; chemin relatif ../../Infer pour re-exec headless CWD=notebook-dir.)#load "../../Infer/FactorGraphHelper.cs"Console.WriteLine("FactorGraphHelper charge !");Console.WriteLine($"Graphviz disponible : {FactorGraphHelper.IsGraphvizAvailable()}");
Le message “Infer.NET charge !” confirme que les packages NuGet sont correctement installes. Nous disposons maintenant de :
Package
Rôle
Microsoft.ML.Probabilistic
Moteur d’inference bayesienne
Microsoft.ML.Probabilistic.Compiler
Compilation du graphe de facteurs
Note : Dans ce notebook, Infer.NET sera utilise principalement pour l’inference des poids (section 10 bis) et le calcul d’utilite esperee sous incertitude (section 9). Les sections sur MAUT et SMART utilisent des calculs déterministes classiques.
// Exemple : Achat d'une voiturepublicclass Voiture{publicstring Nom {get;set;}publicdouble Prix {get;set;}// EURpublicint Securite {get;set;}// 1-5 etoilespublicdouble Consommation {get;set;}// L/100kmpublicint Confort {get;set;}// 1-10}var voitures =new List<Voiture>{new Voiture { Nom ="Economique A", Prix =15000, Securite =3, Consommation =5.0, Confort =6},new Voiture { Nom ="Familiale B", Prix =28000, Securite =5, Consommation =6.5, Confort =8},new Voiture { Nom ="Sport C", Prix =45000, Securite =4, Consommation =9.0, Confort =7},new Voiture { Nom ="Hybride D", Prix =32000, Securite =5, Consommation =4.0, Confort =8},};Console.WriteLine("Options disponibles :\n");Console.WriteLine("Nom | Prix | Securite | Conso | Confort");Console.WriteLine("---------------|---------|----------|---------|--------");foreach(var v in voitures){ Console.WriteLine($"{v.Nom,-14} | {v.Prix,7:N0} | {v.Securite,8} | {v.Consommation,7:F1} | {v.Confort,7}");}
Options disponibles :
Nom | Prix | Securite | Conso | Confort
---------------|---------|----------|---------|--------
Economique A | 15 000 | 3 | 5,0 | 6
Familiale B | 28 000 | 5 | 6,5 | 8
Sport C | 45 000 | 4 | 9,0 | 7
Hybride D | 32 000 | 5 | 4,0 | 8
Interpretation du tableau des options
Le tableau ci-dessus presente quatre voitures avec des profils très différents :
Voiture
Profil
Points forts
Points faibles
Economique A
Budget-friendly
Prix bas, bonne conso
Securite moyenne, confort limite
Familiale B
Equilibree
Securite max, bon confort
Prix eleve, consommation moyenne
Sport C
Performance
Securite correcte
Prix très eleve, forte conso
Hybride D
Eco-responsable
Securite max, conso min
Prix assez eleve
Observation cle : Aucune voiture ne domine les autres sur tous les critères. C’est précisément ce type de situation qui necessite une méthode de decision multi-critères structuree. Si une option etait superieure sur tous les attributs, le choix serait trivial.
2. Fonctions de Valeur vs Fonctions d’Utilite
Distinction importante
Type
Contexte
Propriete
Fonction de valeur V(x)
Certitude
Préférences sur outcomes certains
Fonction d’utilite U(x)
Incertitude
Préférences sur loteries
En multi-attributs
Valeur : V(prix, securite, conso, confort) represente vos préférences sous certitude
Utilite : U(.) integre en plus votre aversion au risque sur chaque attribut
Dans ce notebook, nous commencerons par les fonctions de valeur (plus simples), puis passerons aux fonctions d’utilite.
3. Indépendance Preferentielle
L’indépendance preferentielle est la cle qui permet de decomposer un problème multi-critères en sous-problemes simples. Sans elle, les interactions entre attributs rendraient le problème intraitable.
Definition
L’attribut X est preferentiellement indépendant de Y si : Les préférences sur X ne dependent pas du niveau de Y
Test pratique pour verifier l’indépendance
Pour tester si Prix et Securite sont independants :
Fixez Securite = niveau bas (ex: 2 etoiles)
Comparez : Prix 15000 vs Prix 25000
Notez votre préférence
Fixez Securite = niveau haut (ex: 5 etoiles)
Comparez les mêmes prix : 15000 vs 25000
Notez votre préférence
Verdict : Si votre préférence est la même dans les deux cas, Prix est indépendant de Securite
Exemple
Est-ce que votre préférence entre 15000 EUR et 20000 EUR (prix) depend du niveau de securite ?
Si OUI : prix et securite ne sont PAS independants. Cela peut arriver si, par exemple, vous n’acceptez un prix eleve que si la securite est excellente.
Si NON : ils sont preferentiellement independants. Vous preferez toujours le prix bas, quel que soit le niveau de securite.
Indépendance mutuelle
Les attributs {X, Y, Z} sont mutuellement preferentiellement independants si : Chaque sous-ensemble est preferentiellement indépendant de son complement
Cette condition est forte mais souvent raisonnable. Elle permet d’utiliser des formes additives ou multiplicatives simples, ce qui reduit exponentiellement la complexite du problème.
// Test d'independance preferentielleConsole.WriteLine("Test d'independance : Prix vs Securite\n");Console.WriteLine("Question : Preferez-vous A ou B ?");Console.WriteLine();// Contexte 1 : Securite = 3 etoilesConsole.WriteLine("Contexte 1 (Securite = 3 etoiles) :");Console.WriteLine(" A : Prix = 15000 EUR");Console.WriteLine(" B : Prix = 20000 EUR");Console.WriteLine(" => Vous preferez probablement A (moins cher)\n");// Contexte 2 : Securite = 5 etoilesConsole.WriteLine("Contexte 2 (Securite = 5 etoiles) :");Console.WriteLine(" A : Prix = 15000 EUR");Console.WriteLine(" B : Prix = 20000 EUR");Console.WriteLine(" => Preferez-vous toujours A ?\n");Console.WriteLine("Si votre reponse est la meme dans les deux contextes,");Console.WriteLine("alors Prix et Securite sont preferentiellement independants.");
Test d'independance : Prix vs Securite
Question : Preferez-vous A ou B ?
Contexte 1 (Securite = 3 etoiles) :
A : Prix = 15000 EUR
B : Prix = 20000 EUR
=> Vous preferez probablement A (moins cher)
Contexte 2 (Securite = 5 etoiles) :
A : Prix = 15000 EUR
B : Prix = 20000 EUR
=> Preferez-vous toujours A ?
Si votre reponse est la meme dans les deux contextes,
alors Prix et Securite sont preferentiellement independants.
4. Theoreme d’Additivite (Debreu-Gorman)
Enonce
Si les attributs X1, X2, …, Xn sont mutuellement preferentiellement independants, alors la fonction de valeur est additive :
V(x1, x2, …, xn) = Summe wi x vi(xi)
Ou : - vi(xi) est la fonction de valeur marginale pour l’attribut i (normalisee 0-1) - wi est le poids de l’attribut i (Summe wi = 1)
Construction
Pour chaque attribut, définir vi(xi) : [xi_min, xi_max] -> [0, 1]
Determiner les poids wi par comparaison des “swings”
Calculer V(.) pour chaque alternative
Implementation du modèle de decision multi-attributs avec la forme additive.
// Implementation de la forme additivepublicclass MultiAttributeValue{// Fonctions de valeur marginale (normalisees 0-1)// Pour le prix : plus bas = mieux (inverser)publicstaticdoubleV_prix(double prix,double prixMin =15000,double prixMax =45000){return1-(prix - prixMin)/(prixMax - prixMin);}// Pour la securite : plus haut = mieuxpublicstaticdoubleV_securite(int securite,int secMin =1,int secMax =5){return(double)(securite - secMin)/(secMax - secMin);}// Pour la consommation : plus bas = mieux (inverser)publicstaticdoubleV_conso(double conso,double consoMin =4,double consoMax =10){return1-(conso - consoMin)/(consoMax - consoMin);}// Pour le confort : plus haut = mieuxpublicstaticdoubleV_confort(int confort,int confMin =1,int confMax =10){return(double)(confort - confMin)/(confMax - confMin);}}// Poids (swing weights) - a ajuster selon vos preferencesdouble w_prix =0.35;double w_securite =0.30;double w_conso =0.20;double w_confort =0.15;Console.WriteLine($"Poids : Prix={w_prix:P0}, Securite={w_securite:P0}, Conso={w_conso:P0}, Confort={w_confort:P0}\n");Console.WriteLine("Voiture | v_prix | v_sec | v_conso | v_conf | V_total");Console.WriteLine("---------------|--------|--------|---------|--------|--------");foreach(var v in voitures){double vp = MultiAttributeValue.V_prix(v.Prix);double vs = MultiAttributeValue.V_securite(v.Securite);double vc = MultiAttributeValue.V_conso(v.Consommation);double vf = MultiAttributeValue.V_confort(v.Confort);double V_total = w_prix * vp + w_securite * vs + w_conso * vc + w_confort * vf; Console.WriteLine($"{v.Nom,-14} | {vp,6:F3} | {vs,6:F3} | {vc,7:F3} | {vf,6:F3} | {V_total,6:F3}");}var meilleure = voitures.OrderByDescending(v => w_prix * MultiAttributeValue.V_prix(v.Prix)+ w_securite * MultiAttributeValue.V_securite(v.Securite)+ w_conso * MultiAttributeValue.V_conso(v.Consommation)+ w_confort * MultiAttributeValue.V_confort(v.Confort)).First();Console.WriteLine($"\n=> Meilleur choix : {meilleure.Nom}");
Lecture : pourquoi l’Hybride D gagne — et pourquoi la Sport C s’effondre
Le classement final (D 0,768 > A 0,750 > B 0,732 > C 0,358) se lit colonne par colonne dans la décomposition. La Sport C perd d’abord sur toute la ligne : sa valeur de prix est 0,000 — c’est la voiture la plus chère, et la fonction de valeur prix est construite pour valoir zéro au pire —, et elle ne rattrape nulle part ailleurs (0,75 en sécurité, 0,17 en consommation : la plus gourmande du lot). À l’inverse, l’Hybride D n’est pénalisée sur aucun attribut : elle partage le sommet en sécurité avec la Familiale B (1,000 toutes deux), domine seule la consommation (1,000) et ne descend jamais sous 0,43. C’est la leçon structurelle de la forme additive — la victoire revient rarement au spécialiste, presque toujours au généraliste équilibré dès que les poids sont répartis. L’Économique A (0,750) s’en sort bien par son prix imbattable (1,000), qui compense une sécurité moyenne (0,500).
// Decomposition des valeurs par attribut pour chaque voiture// Utilise les fonctions de valeur et poids definis ci-dessusConsole.WriteLine("Decomposition de la valeur multi-attributs :");Console.WriteLine();Console.WriteLine("Voiture | Prix | Securite | Conso | Confort | Contributions");Console.WriteLine(" | (w=35%) | (w=30%) | (w=20%) | (w=15%) | ponderees");Console.WriteLine("---------------|---------|----------|---------|---------|---------------");foreach(var v in voitures){double vp = MultiAttributeValue.V_prix(v.Prix);double vs = MultiAttributeValue.V_securite(v.Securite);double vc = MultiAttributeValue.V_conso(v.Consommation);double vf = MultiAttributeValue.V_confort(v.Confort);double cp = w_prix * vp;double cs = w_securite * vs;double cc = w_conso * vc;double cf = w_confort * vf; Console.WriteLine($"{v.Nom,-14} | {cp,7:F3} | {cs,8:F3} | {cc,7:F3} | {cf,7:F3} | V={cp+cs+cc+cf:F3}");}Console.WriteLine();Console.WriteLine("Interpretation :");Console.WriteLine(" - Chaque colonne = contribution ponderee de l'attribut (wi * vi)");Console.WriteLine(" - V total = somme des contributions");
Decomposition de la valeur multi-attributs :
Voiture | Prix | Securite | Conso | Confort | Contributions
| (w=35%) | (w=30%) | (w=20%) | (w=15%) | ponderees
---------------|---------|----------|---------|---------|---------------
Economique A | 0,350 | 0,150 | 0,167 | 0,083 | V=0,750
Familiale B | 0,198 | 0,300 | 0,117 | 0,117 | V=0,732
Sport C | 0,000 | 0,225 | 0,033 | 0,100 | V=0,358
Hybride D | 0,152 | 0,300 | 0,200 | 0,117 | V=0,768
Interpretation :
- Chaque colonne = contribution ponderee de l'attribut (wi * vi)
- V total = somme des contributions
Lecture du budget de valeur : chaque ligne est un bilan de contributions
La décomposition transforme le score opaque en budget lisible : chaque case est wᵢ × vᵢ, la part du score total achetée par cet attribut, et la ligne somme à V. Pour l’Économique A, près de la moitié de la valeur (0,350 sur 0,750) vient du seul attribut prix — une décision « à botte concentrée », vulnérable si le poids du prix bouge. Pour l’Hybride D, les contributions sont étalées (0,152 / 0,300 / 0,200 / 0,117) — une décision robuste, aucun attribut ne porte plus de 40 % du score. Cette lecture « portefeuille de valeur » annonce l’analyse de sensibilité de la partie SMART ci-dessous, qui dira précisément à partir de quel poids l’équilibre bascule.
5. Determination des Poids (Swing Weights)
La méthode des swing weights est la technique standard pour determiner les poids de maniere coherente et justifiable. Elle evite les biais cognitifs lies a l’estimation directe des poids.
Principe fondamental
L’idee est de comparer la valeur des ameliorations (swings) plutot que les attributs eux-mêmes. Un swing est le passage du pire au meilleur niveau d’un attribut.
Méthode des swings en 5 étapes
Étape 1 : Point de reference - Imaginez l’option la pire possible (tous attributs au minimum) - C’est votre point de depart
Étape 2 : Identifier les swings - Pour chaque attribut, definissez le swing : pire niveau -> meilleur niveau - Ex: Prix 45000 EUR -> 15000 EUR, Securite 1 etoile -> 5 etoiles
Étape 3 : Classer les swings - Demandez-vous : “Quel swing prefererais-je realiser en premier ?” - Classez tous les swings par ordre de préférence
Étape 4 : Attribuer des points - Donnez 100 points au swing le plus important - Attribuez des points aux autres proportionnellement a leur valeur relative - Ex: Si le swing Securite vaut 100, et le swing Prix vaut “les 9/10 de Securite”, donnez 90 a Prix
Étape 5 : Normaliser - Divisez chaque score par la somme totale - Vous obtenez les poids wi avec Summe wi = 1
Exemple
// Methode des swing weightsConsole.WriteLine("Situation de depart (pire option) :\n");Console.WriteLine(" Prix : 45000 EUR (maximum)");Console.WriteLine(" Securite : 1 etoile (minimum)");Console.WriteLine(" Consommation : 10 L/100km (maximum)");Console.WriteLine(" Confort : 1/10 (minimum)");Console.WriteLine();Console.WriteLine("Classez les swings suivants par ordre de preference :\n");Console.WriteLine(" A) Prix : 45000 -> 15000 (economie de 30000 EUR)");Console.WriteLine(" B) Securite : 1 -> 5 etoiles");Console.WriteLine(" C) Consommation : 10 -> 4 L/100km");Console.WriteLine(" D) Confort : 1 -> 10\n");// Exemple de classement personnelvar swingPoints =new Dictionary<string,int>{{"Securite",100},// Le plus important{"Prix",90},// Presque aussi important{"Consommation",50},// Moitie de la securite{"Confort",40}// Le moins prioritaire};double totalSwing = swingPoints.Values.Sum();Console.WriteLine("Points swing et poids normalises :\n");foreach(var kv in swingPoints.OrderByDescending(x => x.Value)){double poidsSwing = kv.Value/ totalSwing; Console.WriteLine($" {kv.Key,-12} : {kv.Value,3} points => poids = {poidsSwing:P1}");}
Situation de depart (pire option) :
Prix : 45000 EUR (maximum)
Securite : 1 etoile (minimum)
Consommation : 10 L/100km (maximum)
Confort : 1/10 (minimum)
Classez les swings suivants par ordre de preference :
A) Prix : 45000 -> 15000 (economie de 30000 EUR)
B) Securite : 1 -> 5 etoiles
C) Consommation : 10 -> 4 L/100km
D) Confort : 1 -> 10
Points swing et poids normalises :
Securite : 100 points => poids = 35,7 %
Prix : 90 points => poids = 32,1 %
Consommation : 50 points => poids = 17,9 %
Confort : 40 points => poids = 14,3 %
Exercice 1 : Determination de swing weights pour un choix d’appartement
Note pedagogique : ce bloc est positionne en tete de sequence pour l’ordre canonique header -> stub de l’exercice (cf. PR #7880).
Objectif : Appliquez la méthode des swing weights pour determiner les poids d’une decision d’achat d’appartement.
Contexte : Vous cherchez un appartement parmi 4 options. Les attributs sont : Prix, Surface, Quartier (note 1-10), et Duree de trajet travail. Avant de choisir, vous devez determiner vos poids.
Étapes : 1. Definissez le pire scénario pour chaque attribut (votre point de reference) 2. Classez les 4 swings (amelioration du pire au meilleur) par ordre de préférence 3. Attribuez des points (100 au meilleur swing, proportionnellement aux autres) 4. Normalisez pour obtenir les poids wi 5. Appliquez la forme additive pour classer les appartements
Indices : - Indice 1 : Le pire scénario est celui ou TOUS les attributs sont a leur valeur minimale - Indice 2 : Un swing “Prix: 500k -> 200k” peut etre plus ou moins important qu’un swing “Trajet: 60min -> 10min” - Indice 3 : Les points refletent la valeur subjective de chaque amelioration, pas l’importance abstraite de l’attribut
// Exercice : Swing weights pour un choix d'appartement// 4 appartements disponiblesvar appartements =new[]{new{ Nom ="Studio Centre", Prix =350000, Surface =25, Quartier =8, Trajet =10},new{ Nom ="T2 Banlieue", Prix =200000, Surface =45, Quartier =5, Trajet =45},new{ Nom ="T3 Peripherie", Prix =280000, Surface =65, Quartier =6, Trajet =30},new{ Nom ="Loft Industriel", Prix =420000, Surface =80, Quartier =7, Trajet =20},};// TODO 1 : Definir les pires et meilleurs niveaux pour chaque attribut// Indice : Prix: pire=450000, meilleur=180000 | Surface: pire=20, meilleur=90// Quartier: pire=3, meilleur=9 | Trajet: pire=60min, meilleur=5min// TODO 2 : Classer les 4 swings et attribuer des points (100 au meilleur)// Swing A : Prix 450k->180k = ___ points// Swing B : Surface 20->90 = ___ points// Swing C : Quartier 3->9 = ___ points// Swing D : Trajet 60->5min = ___ points// TODO 3 : Normaliser les points en poids (somme = 1)// Indice : w_i = points_i / somme(points)// TODO 4 : Definir les fonctions de valeur marginale v_i(x_i) normalisees [0, 1]// Indice : pour le prix et le trajet, "moins = mieux" => inverser la normalisation// TODO 5 : Calculer V_total pour chaque appartement et classerConsole.WriteLine("Exercice a completer : swing weights et classement des appartements");
Exercice a completer : swing weights et classement des appartements
6. Utilite Multiplicative pour les Loteries
Contexte : Quand la forme additive ne suffit plus
Quand les outcomes sont incertains (loteries), la forme additive ne suffit plus. En effet, l’additivite suppose que l’aversion au risque est la même quel que soit le niveau des autres attributs, ce qui est rarement vrai.
Conditions d’application de la forme multiplicative
La forme multiplicative est appropriee quand :
Les attributs sont mutuellement independants en utilite (condition plus forte que l’indépendance preferentielle)
Il existe des interactions entre attributs dans l’attitude face au risque
Σkᵢ ≠ 1 (si Σkᵢ = 1, on retombe sur la forme additive)
Forme multiplicative (Keeney-Raiffa)
Si les attributs sont mutuellement independants en utilite :
Synergies : avoir les deux attributs eleves vaut plus que la somme
k < 0
Substituts : ameliorer un attribut reduit l’importance de l’autre
// Utilite multiplicative pour decisions sous incertitudepublicclass MultiplicativeUtility{privatedouble[] k;// Poids individuels kiprivatedouble K;// Facteur d'echelle KpublicMultiplicativeUtility(double[] weights){ k = weights;// Trouver K tel que: prod(1 + K*ki) = 1 + K K =FindK(weights);}privatedoubleFindK(double[] weights){// Recherche par Newton-Raphsondouble sumK = weights.Sum();// Si sum(ki) = 1, alors K = 0 (cas additif)if(Math.Abs(sumK -1)<1e-6)return0;// Sinon, resoudre prod(1 + K*ki) = 1 + Kdouble K = sumK >1?-0.5:0.5;// Initialisationfor(int iter =0; iter <100; iter++){double prod =1;foreach(var ki in weights) prod *=(1+ K * ki);double f = prod -1- K;if(Math.Abs(f)<1e-10)break;// Deriveedouble fprime =-1;for(int i =0; i < weights.Length; i++){double term = weights[i];for(int j =0; j < weights.Length; j++)if(j != i) term *=(1+ K * weights[j]); fprime += term;} K -= f / fprime;}return K;}publicdoubleCompute(double[] u_values){if(Math.Abs(K)<1e-10){// Forme additivereturn u_values.Zip(k,(u, ki)=> ki * u).Sum();}else{// Forme multiplicativedouble prod =1;for(int i =0; i < u_values.Length; i++) prod *=(1+ K * k[i]* u_values[i]);return(prod -1)/ K;}}publicdoubleGetK()=> K;}// Exemple avec 3 attributsvar mu =newMultiplicativeUtility(new[]{0.4,0.35,0.35});// sum > 1 => K != 0Console.WriteLine($"Poids : k1=0.4, k2=0.35, k3=0.35 (sum = 1.1)");Console.WriteLine($"Facteur K calcule : {mu.GetK():F4}");Console.WriteLine();// Comparaison forme additive vs multiplicativedouble[] u_high ={1.0,1.0,1.0};double[] u_mixed ={1.0,0.5,0.5};double[] u_low ={0.5,0.5,0.5};Console.WriteLine("Comparaison des formes :");Console.WriteLine($" u = (1,1,1) : U_mult = {mu.Compute(u_high):F3}");Console.WriteLine($" u = (1,0.5,0.5) : U_mult = {mu.Compute(u_mixed):F3}");Console.WriteLine($" u = (0.5,0.5,0.5) : U_mult = {mu.Compute(u_low):F3}");
Poids : k1=0.4, k2=0.35, k3=0.35 (sum = 1.1)
Facteur K calcule : -0,2565
Comparaison des formes :
u = (1,1,1) : U_mult = 1,000
u = (1,0.5,0.5) : U_mult = 0,707
u = (0.5,0.5,0.5) : U_mult = 0,525
Lecture : K = −0,26 — des attributs substituts, pas complémentaires
Les poids annoncés somment à 1,1 (0,4 + 0,35 + 0,35) : la normalisation multiplicative exige ce dépassement, et le facteur d’interaction recalé K = −0,2565 rétablit la cohérence. Le signe de K porte le sens économique : négatif, les attributs se comportent comme des substituts — être médiocre sur deux attributs à la fois coûte moins cher que la somme des deux médiocrités, car l’un compense partiellement l’autre. Positif, ils seraient des compléments — l’excellence ne paierait que conjointe (prix ET sécurité ensemble). La forme additive vue plus haut est le cas neutre K = 0 : aucun échange entre attributs, chacun compte pour son poids, point.
// Comparaison forme additive vs multiplicative// Creer une instance de MultiplicativeUtility pour comparaisonvar muCompare =newMultiplicativeUtility(new[]{0.5,0.5});double K = muCompare.GetK();Console.WriteLine("Comparaison forme additive vs multiplicative :");Console.WriteLine($"Poids : w1=0.5, w2=0.5, K={K:F4}");Console.WriteLine();// Scenarios de testvar scenarios =new(string nom,double v1,double v2)[]{("Bon X, mauvais Y",0.9,0.2),("Mauvais X, bon Y",0.2,0.9),("Mediocre partout",0.5,0.5),("Excellent partout",0.9,0.9)};Console.WriteLine("Scenario | U Additif | U Multiplicatif | Diff");Console.WriteLine("-------------------------|-----------|-----------------|------");foreach(var(nom, v1, v2)in scenarios){double uAdd =0.5* v1 +0.5* v2;double uMult = muCompare.Compute(new[]{ v1, v2 }); Console.WriteLine($"{nom,-24} | {uAdd,9:F3} | {uMult,15:F3} | {uMult-uAdd,+5:F3}");}Console.WriteLine();if(K <0) Console.WriteLine("K < 0 : Les attributs sont substituts (un bon compense un mauvais)");elseif(K >0) Console.WriteLine("K > 0 : Les attributs sont complements (un mauvais penalise le bon)");else Console.WriteLine("K = 0 : Forme additive (pas d'interaction)");
Comparaison forme additive vs multiplicative :
Poids : w1=0.5, w2=0.5, K=0,0000
Scenario | U Additif | U Multiplicatif | Diff
-------------------------|-----------|-----------------|------
Bon X, mauvais Y | 0,550 | 0,550 | 0,000
Mauvais X, bon Y | 0,550 | 0,550 | 0,000
Mediocre partout | 0,500 | 0,500 | 0,000
Excellent partout | 0,900 | 0,900 | 0,000
K = 0 : Forme additive (pas d'interaction)
Lecture : un tableau volontairement dégénéré — K = 0 fait coïncider les deux formes
Toutes les différences sont 0,000 : ce n’est pas un résultat à interpréter, c’est une contre-épreuve de construction. Le scénario est calibré avec deux poids égaux (0,5 / 0,5), et la forme multiplicative se réduit alors algébriquement à la forme additive — les deux colonnes doivent coïncider, et coïncident : le modèle est auto-cohérent. La comparaison intéressante est celle de la section précédente (K = −0,26), où les interactions séparent des scénarios que l’addition traitait à égalité. L’exercice 2 vous fait explorer l’autre bord : un choix d’investissement où K ≠ 0, et où être bon partout ne vaut plus forcément le même prix qu’être excellent sur un seul attribut décisif.
7. Méthode SMART
Simple Multi-Attribute Rating Technique
SMART est une méthode pratique en 7 étapes :
Identifier les attributs pertinents
Définir les echelles pour chaque attribut
Classer les attributs par importance
Ponderer par la méthode des swings
Evaluer chaque alternative sur chaque attribut
Normaliser les evaluations (0-1)
Calculer la valeur totale : V = Summe wi x vi
Exercice 2 : Forme multiplicative pour un choix d’investissement
Note pedagogique : ce bloc est positionne en tete de sequence pour l’ordre canonique header -> stub de l’exercice (cf. PR #7880).
Objectif : Utilisez la forme multiplicative de l’utilite pour comparer des options d’investissement avec interactions entre attributs.
Contexte : Vous choisissez entre 3 investissements avec les profils suivants (utilites normalisees 0-1) :
Investissement
Rendement (u1)
Risque (u2, 0=sur)
Liquidite (u3)
Obligations
0.3
0.9
0.7
Actions
0.8
0.3
0.5
Immobilier
0.6
0.5
0.2
Étapes : 1. Créez une instance de MultiplicativeUtility avec des poids {0.4, 0.35, 0.35} 2. Calculez U_mult pour chaque investissement 3. Comparez avec la forme additive (U_add = somme wi * ui) 4. Interpretez le signe de K : les attributs sont-ils complements ou substituts ?
Indices : - Indice 1 : K < 0 signifie que les attributs sont substituts (un bon compense un mauvais) - Indice 2 : La différence entre U_mult et U_add est maximale quand les attributs sont extremes (0 ou 1) - Indice 3 : Si K est proche de 0, la forme additive est une bonne approximation
// Exercice : Forme multiplicative pour un choix d'investissement// Profils d'investissement (utilites normalisees 0-1)var investissements =new[]{new{ Nom ="Obligations", u_rend =0.3, u_risque =0.9, u_liquidite =0.7},new{ Nom ="Actions", u_rend =0.8, u_risque =0.3, u_liquidite =0.5},new{ Nom ="Immobilier", u_rend =0.6, u_risque =0.5, u_liquidite =0.2},};// TODO 1 : Creez une instance de MultiplicativeUtility avec des poids {0.4, 0.35, 0.35}// Indice : var muInvest = new MultiplicativeUtility(new[] { 0.4, 0.35, 0.35 });// TODO 2 : Pour chaque investissement, calculez U_mult et U_add// Indice : U_add = w1*u1 + w2*u2 + w3*u3// U_mult = muInvest.Compute(new[] { u1, u2, u3 })// TODO 3 : Affichez un tableau comparatif// | Investissement | U_additif | U_multiplicatif | Difference |// TODO 4 : Interpretez le signe de K// Indice : muInvest.GetK() < 0 => substituts, > 0 => complementsConsole.WriteLine("Exercice a completer : forme multiplicative pour choix d'investissement");
Exercice a completer : forme multiplicative pour choix d'investissement
// Application complete de SMART : Choix de carrierepublicclass CareerOption{publicstring Name {get;set;}publicdouble Salary {get;set;}// k EUR/anpublicint WorkLifeBalance {get;set;}// 1-10publicint Passion {get;set;}// 1-10publicint JobSecurity {get;set;}// 1-10}var careers =new List<CareerOption>{new CareerOption { Name ="Startup", Salary =45, WorkLifeBalance =3, Passion =9, JobSecurity =4},new CareerOption { Name ="Grande Entreprise", Salary =55, WorkLifeBalance =6, Passion =5, JobSecurity =8},new CareerOption { Name ="Fonction Publique", Salary =38, WorkLifeBalance =8, Passion =4, JobSecurity =10},new CareerOption { Name ="Freelance", Salary =60, WorkLifeBalance =5, Passion =8, JobSecurity =3},new CareerOption { Name ="Recherche", Salary =35, WorkLifeBalance =7, Passion =10, JobSecurity =6},};// Etape 4 : Poids SMART (exemple)var weights =new Dictionary<string,double>{{"Salary",0.30},{"WorkLifeBalance",0.25},{"Passion",0.25},{"JobSecurity",0.20}};// Etapes 5-6 : Evaluation et normalisationdouble salaryMin = careers.Min(c => c.Salary);double salaryMax = careers.Max(c => c.Salary);Func<double,double> normSalary = s =>(s - salaryMin)/(salaryMax - salaryMin);Func<int,double> norm10 = x =>(x -1)/9.0;Console.WriteLine("=== SMART : Choix de Carriere ===");Console.WriteLine($"Poids : Salaire={weights["Salary"]:P0}, Equilibre={weights["WorkLifeBalance"]:P0}, ");Console.WriteLine($" Passion={weights["Passion"]:P0}, Securite={weights["JobSecurity"]:P0}\n");Console.WriteLine("Carriere | v_sal | v_wlb | v_pas | v_sec | V_total");Console.WriteLine("-------------------|-------|-------|-------|-------|--------");var results =new List<(string name,double V)>();foreach(var c in careers){double v_sal =normSalary(c.Salary);double v_wlb =norm10(c.WorkLifeBalance);double v_pas =norm10(c.Passion);double v_sec =norm10(c.JobSecurity);double V = weights["Salary"]* v_sal + weights["WorkLifeBalance"]* v_wlb + weights["Passion"]* v_pas + weights["JobSecurity"]* v_sec; results.Add((c.Name, V)); Console.WriteLine($"{c.Name,-18} | {v_sal,5:F3} | {v_wlb,5:F3} | {v_pas,5:F3} | {v_sec,5:F3} | {V,6:F3}");}var best = results.OrderByDescending(x => x.V).First();Console.WriteLine($"\n=> Meilleur choix avec ces poids : {best.name} (V = {best.V:F3})");
Les poids sont souvent incertains. L’analyse de sensibilite montre comment le choix change quand on fait varier les poids.
// Analyse de sensibilite sur le poids du salaireConsole.WriteLine("Analyse de sensibilite : poids du Salaire\n");Console.WriteLine("w_salaire | Meilleur choix | V_meilleur");Console.WriteLine("----------|----------------------|-----------");for(double w_sal =0.1; w_sal <=0.5; w_sal +=0.1){// Redistribuer le poids restant proportionnellementdouble remaining =1- w_sal;double w_wlb =0.25/0.70* remaining;double w_pas =0.25/0.70* remaining;double w_sec =0.20/0.70* remaining;var best_option = careers.Select(c =>new{ Name = c.Name, V = w_sal *normSalary(c.Salary)+ w_wlb *norm10(c.WorkLifeBalance)+ w_pas *norm10(c.Passion)+ w_sec *norm10(c.JobSecurity)}).OrderByDescending(x => x.V).First(); Console.WriteLine($"{w_sal,9:P0} | {best_option.Name,-20} | {best_option.V,9:F3}");}Console.WriteLine();Console.WriteLine("=> Le choix optimal change selon l'importance accordee au salaire.");
Analyse de sensibilite : poids du Salaire
w_salaire | Meilleur choix | V_meilleur
----------|----------------------|-----------
10 % | Recherche | 0,679
20 % | Grande Entreprise | 0,623
30 % | Freelance | 0,650
40 % | Freelance | 0,700
50 % | Freelance | 0,750
=> Le choix optimal change selon l'importance accordee au salaire.
Lecture : trois gagnants pour un même jeu de données — la décision tient à un poids
Le choix optimal change deux fois de gagnant sur la plage : Recherche tant que le poids du salaire reste bas (10 % : sa passion 1,000 et son équilibre 0,667 portent, malgré le salaire 0,000), Grande Entreprise sur la bande intermédiaire (~20-25 % — le profil le plus plat de la table : 0,80 / 0,56 / 0,44 / 0,78, jamais extrême), puis Freelance au-delà (~25-30 % : son salaire 1,000 prend le dessus). Aucune de ces réponses n’est « la » bonne : chacune est optimale pour des préférences différentes — c’est la réponse honnête à « quelle carrière choisir ? » quand le poids du salaire est mal connu. La question opérationnelle devient : mon choix est-il robuste ? Si votre poids de salaire estimé est 30 % mais que votre vraie préférence pourrait être 20 %, la décision change de gagnant — d’où l’intérêt de la fin du notebook, où l’inférence bayésienne estime les poids depuis des choix observés au lieu de les demander. Dix ou vingt choix observés resserreraient le posterior autour des poids révélés, et diraient si l’écart entre déclaré et révélé se referme avec la pratique.
// Analyse de sensibilite : tableau des valeurs V par carriere et poids salaire// (Remplace Chart.Combine Plotly.NET qui echoue en CS0103 sur re-execution fraiche)var wSalaryRange = Enumerable.Range(1,9).Select(i => i *0.05+0.05).ToArray();// 0.10 a 0.50// Stocker les valeurs V pour chaque carriere et chaque w_salaryvar sensitivityData =new Dictionary<string,double[]>();foreach(var career in careers){var Vvalues =new List<double>();foreach(var w_sal in wSalaryRange){double remaining =1- w_sal;double w_wlb_s =0.25/0.70* remaining;double w_pas_s =0.25/0.70* remaining;double w_sec_s =0.20/0.70* remaining;double V = w_sal *normSalary(career.Salary)+ w_wlb_s *norm10(career.WorkLifeBalance)+ w_pas_s *norm10(career.Passion)+ w_sec_s *norm10(career.JobSecurity); Vvalues.Add(V);} sensitivityData[career.Name]= Vvalues.ToArray();}// Afficher le tableau de sensibiliteConsole.WriteLine("Tableau de sensibilite : V selon le poids du Salaire");Console.WriteLine();Console.Write("w_salaire |");foreach(var c in careers) Console.Write($"{c.Name.Substring(0, Math.Min(8, c.Name.Length)),-9} |");Console.WriteLine();Console.Write("-----------|");for(int i =0; i < careers.Count; i++) Console.Write("---------|");Console.WriteLine();foreach(var w_sal in wSalaryRange){ Console.Write($"{w_sal,10:P0} |");double maxV =double.MinValue;string maxName ="";foreach(var career in careers){int idx = Array.IndexOf(wSalaryRange, w_sal);double V = sensitivityData[career.Name][idx];if(V > maxV){ maxV = V; maxName = career.Name;} Console.Write($"{V,8:F3} |");} Console.Write($" <-- {maxName}"); Console.WriteLine();}Console.WriteLine();Console.WriteLine("Interpretation :");Console.WriteLine("- Les valeurs en gras (max par colonne) indiquent le meilleur choix");Console.WriteLine("- Freelance domine pour w_salaire > 25%");Console.WriteLine("- Recherche domine pour w_salaire < 15%");Console.WriteLine("=> Le choix optimal depend fortement des preferences sur le salaire");
Exercice 3 : Analyse de sensibilite multi-attributs
Note pedagogique : ce bloc est positionne en tete de sequence pour l’ordre canonique header -> stub de l’exercice (cf. PR #7880).
Objectif : Realisez une analyse de sensibilite complete pour determiner quel poids est le plus critique dans un choix de carriere.
Contexte : Vous avez 5 options de carriere et 4 attributs. Les poids que vous avez determines par la méthode des swings sont : Salaire=30%, Equilibre=25%, Passion=25%, Securite=20%. Mais vous n’etes pas sur de vos poids. Determinez quels poids sont critiques pour le choix final.
Étapes : 1. Utilisez les carrieres définies dans la section SMART (Startup, Grande Entreprise, etc.) 2. Faites varier chaque poids individuellement de +/-10% (en redistribuant proportionnellement les autres) 3. Pour chaque variation, identifiez si le meilleur choix change 4. Identifiez les poids critiques (ceux dont la variation fait changer le choix)
Indices : - Indice 1 : Enregistrez le classement complet (pas seulement le meilleur) pour detecter les changements subtils - Indice 2 : Un poids est “critique” si une variation de 5% ou moins fait changer le choix optimal - Indice 3 : Les poids non-critiques peuvent etre ajustes sans affecter la decision
// Exercice : Analyse de sensibilite multi-attributs// Reutiliser les carrieres et fonctions de normalisation definies dans la section SMART// careers, normSalary, norm10, weights// Poids nominauxdouble[] w_nominal ={0.30,0.25,0.25,0.20};// sal, wlb, pas, sec// TODO 1 : Implementer une fonction qui calcule le classement pour des poids donnes// Indice : pour chaque carriere, calculer V = w_sal*v_sal + w_wlb*v_wlb + w_pas*v_pas + w_sec*v_sec// puis trier par V descendant// TODO 2 : Pour chaque attribut i, faire varier w_i de -10% a +10% par pas de 2%// Indice : redistribuer proportionnellement : w_j_adjusted = w_j * (1 - w_i_new) / (1 - w_i_old)// Verifier que la somme reste = 1// TODO 3 : Pour chaque variation, enregistrer le meilleur choix// Indice : si le meilleur change par rapport au nominal, le poids est "critique"// TODO 4 : Afficher un tableau recapitulatif// | Attribut varie | Variation | Meilleur choix | Change? |// TODO 5 : Conclure sur les poids critiques vs robustesConsole.WriteLine("Exercice a completer : analyse de sensibilite multi-attributs");
Exercice a completer : analyse de sensibilite multi-attributs
9. Integration avec Infer.NET
Scénario avec incertitude
Si les attributs sont incertains, on utilise Infer.NET pour calculer les distributions posterieures, puis on calcule l’utilite esperee.
// Decision multi-attributs avec incertitude sur les attributs// Scenario : Choisir entre deux projets d'investissement// Les rendements et risques sont incertains// Projet A : Rendement modere, risque modereVariable<double> rendementA = Variable.GaussianFromMeanAndVariance(0.08,0.01).Named("rendA");Variable<double> risqueA = Variable.GaussianFromMeanAndVariance(0.15,0.005).Named("risqueA");// Projet B : Rendement eleve, risque eleveVariable<double> rendementB = Variable.GaussianFromMeanAndVariance(0.12,0.02).Named("rendB");Variable<double> risqueB = Variable.GaussianFromMeanAndVariance(0.25,0.01).Named("risqueB");InferenceEngine engine =newInferenceEngine();engine.Compiler.CompilerChoice= Microsoft.ML.Probabilistic.Compiler.CompilerChoice.Roslyn;var distRendA = engine.Infer<Gaussian>(rendementA);var distRisqueA = engine.Infer<Gaussian>(risqueA);var distRendB = engine.Infer<Gaussian>(rendementB);var distRisqueB = engine.Infer<Gaussian>(risqueB);Console.WriteLine("Distributions des attributs :\n");Console.WriteLine($"Projet A : Rendement ~ {distRendA}, Risque ~ {distRisqueA}");Console.WriteLine($"Projet B : Rendement ~ {distRendB}, Risque ~ {distRisqueB}");Console.WriteLine();// Fonction d'utilite multi-attributs// U(rendement, risque) = w_rend * v(rend) + w_risque * (1 - v(risque))// Ou v normalise [0, 0.20] -> [0, 1]double w_rend =0.6;double w_risque =0.4;Func<double,double> v_rend = r => Math.Max(0, Math.Min(1, r /0.20));Func<double,double> v_risque = r =>1- Math.Max(0, Math.Min(1, r /0.40));// Calcul par Monte Carlo de E[U]Microsoft.ML.Probabilistic.Math.Rand.Restart(42);// Gaussian.Sample() tire dans Rand, le générateur global d'Infer.NETint N =5000;double sumU_A =0, sumU_B =0;for(int i =0; i < N; i++){double rA = distRendA.Sample();double riA = distRisqueA.Sample();double U_A = w_rend *v_rend(rA)+ w_risque *v_risque(riA); sumU_A += U_A;double rB = distRendB.Sample();double riB = distRisqueB.Sample();double U_B = w_rend *v_rend(rB)+ w_risque *v_risque(riB); sumU_B += U_B;}double EU_A = sumU_A / N;double EU_B = sumU_B / N;Console.WriteLine($"E[U(Projet A)] = {EU_A:F4}");Console.WriteLine($"E[U(Projet B)] = {EU_B:F4}");Console.WriteLine();Console.WriteLine($"=> Decision optimale : {(EU_A > EU_B ? "Projet A" : "Projet B")}");
Compiling model...done.
Compiling model...done.
Compiling model...done.
Compiling model...done.
Distributions des attributs :
Projet A : Rendement ~ Gaussian(0,08, 0,01), Risque ~ Gaussian(0,15, 0,005)
Projet B : Rendement ~ Gaussian(0,12, 0,02), Risque ~ Gaussian(0,25, 0,01)
E[U(Projet A)] = 0,5101
E[U(Projet B)] = 0,4884
=> Decision optimale : Projet A
Interpretation des résultats Infer.NET : Decision d’investissement
Sorties obtenues :
Projet
Distribution Rendement
Distribution Risque
A
Gaussian(0.08, 0.01)
Gaussian(0.15, 0.005)
B
Gaussian(0.12, 0.02)
Gaussian(0.25, 0.01)
Lecture des Gaussiennes : Gaussian(moyenne, variance). Ainsi le Projet A a un rendement espere de 8% avec une variance de 1%, soit un ecart-type de 10%.
Résultats de l’utilite esperee
Projet
E[U]
Interpretation
A
0.5101
Gagnant
B
0.4884
Perdant
Ecart : environ 4.3% en faveur du Projet A
Bruit Monte Carlo : ces deux valeurs sont des moyennes sur 5000 tirages. La graine Rand.Restart(42) les rend reproductibles d’une exécution à l’autre : Gaussian.Sample() tire dans Rand, le générateur global d’Infer.NET, et non dans un System.Random. Une autre graine déplacerait chaque E[U] de quelques millièmes, soit bien moins que l’écart de 0.02 entre les deux projets : la décision ne dépend pas de la graine.
Decomposition de la decision
La fonction d’utilite multi-attributs utilisee : \[U(\text{rend}, \text{risque}) = 0.6 \times \frac{\text{rend}}{0.20} + 0.4 \times \left(1 - \frac{\text{risque}}{0.40}\right)\]
Observation : Le projet B a un meilleur rendement espere, mais son risque eleve fait basculer la decision en faveur de A. C’est l’arbitrage rendement-risque classique en finance.
Rôle d’Infer.NET dans cette approche
Étape
Outil
Rôle
1. Modelisation des incertitudes
Infer.NET
Posterieurs analytiques
2. Calcul d’utilite esperee
Monte Carlo
Integration numérique
Note technique : Cette approche hybride est courante en pratique. Infer.NET excelle pour l’inference exacte ou approchee des posterieurs, mais les fonctions d’utilite non-lineaires necessitent souvent une integration Monte Carlo.
10. Exemple guide : Votre Decision Multi-Attributs
Enonce
Appliquez la méthode SMART a une decision personnelle :
Choisissez un problème (appartement, voyage, achat…)
Identifiez 3-5 attributs
Definissez vos poids par la méthode des swings
Evaluez 3-4 alternatives
Calculez et comparez
// Template pour votre decision SMART// 1. Definissez votre probleme et alternativesvar alternatives =new[]{"Option 1","Option 2","Option 3"};// 2. Definissez vos attributs et poidsvar attributs =new Dictionary<string,double>{{"Attribut A",0.4},{"Attribut B",0.35},{"Attribut C",0.25}};// 3. Evaluez chaque alternative (valeurs normalisees 0-1)var evaluations =new Dictionary<string,double[]>{{"Option 1",new[]{0.8,0.6,0.7}},{"Option 2",new[]{0.5,0.9,0.6}},{"Option 3",new[]{0.7,0.7,0.9}}};// 4. Calculez les valeurs totalesConsole.WriteLine("Vos resultats SMART :\n");foreach(var alt in alternatives){var vals = evaluations[alt];var poids = attributs.Values.ToArray();double V = vals.Zip(poids,(v, w)=> v * w).Sum(); Console.WriteLine($"{alt} : V = {V:F3}");}Console.WriteLine();Console.WriteLine("Modifiez les valeurs ci-dessus pour votre probleme personnel !");
Vos resultats SMART :
Option 1 : V = 0,705
Option 2 : V = 0,665
Option 3 : V = 0,750
Modifiez les valeurs ci-dessus pour votre probleme personnel !
10 bis. Apprentissage Bayesien des Poids avec Infer.NET
Motivation
Jusqu’ici, les poids MAUT sont determines par introspection (méthode des swings). Mais on peut aussi les inferer a partir des choix observes d’un decideur !
Approche Bayesienne
Prior : Distribution Dirichlet sur les poids (non-informatif ou expert)
Vraisemblance : Modèle de choix probabiliste (ex: softmax sur utilites)
Posterior : Poids mis a jour après observation des choix
Avantages
Quantifie l’incertitude sur les poids
Permet de combiner expertise et données
Base pour l’elicitation de préférences automatique
// Apprentissage des poids MAUT avec prior Dirichletusing Microsoft.ML.Probabilistic.Math;// Scenario : Un decideur a fait 5 choix entre des alternatives.// Nous observons quel attribut semble dominer ses decisions.// Donnees observees : pour chaque choix, l'attribut "gagnant" (simplifie)// 0=Prix, 1=Securite, 2=Consommation, 3=Confortint[] choixDominants ={1,0,1,1,0};// Securite semble importante// Modele Infer.NET// Prior Dirichlet non-informatif (pseudo-counts = 1 pour chaque attribut)double[] pseudoCounts ={1.0,1.0,1.0,1.0};Variable<Vector> poids = Variable.Dirichlet(pseudoCounts).Named("poids");// Observations : chaque choix est un tirage selon les poidsint numChoix = choixDominants.Length;Range choixRange =newRange(numChoix).Named("choix");VariableArray<int> observations = Variable.Array<int>(choixRange).Named("obs");observations[choixRange]= Variable.Discrete(poids).ForEach(choixRange);observations.ObservedValue= choixDominants;// InferenceInferenceEngine engineDirichlet =newInferenceEngine();engineDirichlet.Compiler.CompilerChoice= Microsoft.ML.Probabilistic.Compiler.CompilerChoice.Roslyn;Dirichlet posteriorPoids = engineDirichlet.Infer<Dirichlet>(poids);Console.WriteLine("=== Inference Bayesienne des Poids MAUT ===\n");Console.WriteLine($"Choix observes : {string.Join(",", choixDominants.Select(c => new[] { "Prix", "Securite", "Conso", "Confort" }[c]))}\n");Console.WriteLine("Prior (Dirichlet uniforme) :");Console.WriteLine($" Pseudo-counts : [{string.Join(",", pseudoCounts)}]\n");Console.WriteLine("Posterior (apres 5 observations) :");Vector meanPoids = posteriorPoids.GetMean();string[] noms ={"Prix","Securite","Consommation","Confort"};for(int i =0; i <4; i++){ Console.WriteLine($" w_{noms[i],-12} = {meanPoids[i]:P1}");}Console.WriteLine($"\nDistribution Dirichlet posterior : {posteriorPoids}");Console.WriteLine();// Comparaison avec la methode des swings manuelleConsole.WriteLine("Comparaison :");Console.WriteLine(" Swing weights (manuel) : Prix=35%, Securite=30%, Conso=20%, Confort=15%");Console.WriteLine($" Inference bayesienne : Prix={meanPoids[0]:P0}, Securite={meanPoids[1]:P0}, Conso={meanPoids[2]:P0}, Confort={meanPoids[3]:P0}");Console.WriteLine();Console.WriteLine("=> L'inference bayesienne revele les poids IMPLICITES du decideur !");Console.WriteLine(" Ici, la Securite domine (3/5 choix), confirmant son importance.");
Mécanisme Dirichlet : La distribution Dirichlet “compte” les observations. Chaque fois qu’un attribut domine un choix, son pseudo-count augmente de 1. Le posterior est proportionnel a (prior + observations).
Comparaison des méthodes d’elicitation
Méthode
Poids Securite
Avantage
Inconvenient
Swing weights (manuel)
35.7%
Explicite, justifiable
Subjectif, biais cognitifs
Inference bayesienne
44.4%
Base sur les choix reels
Necessite des données
Revelation : L’inference bayesienne suggere que le decideur accorde plus d’importance a la securite qu’il ne le declare explicitement (44% vs 36%). C’est un cas classique ou les préférences revelees (choix reels) différent des préférences declarees.
Applications pratiques de l’inference des poids
Application
Description
Validation
Verifier si les poids declares sont coherents avec les choix passes
Prediction
Predire les choix futurs d’un decideur
Elicitation
Aider les experts qui ont du mal a exprimer leurs poids
Marketing
Inferer les préférences des consommateurs a partir de leurs achats
// Comparaison Prior vs Posterior des poids MAUT// Le posterior Dirichlet a ete calcule dans la cellule precedente: posteriorPoids// Prior Dirichlet(1,1,1,1) - uniformedouble[] priorMean ={0.25,0.25,0.25,0.25};// Extraire les moyennes du posteriorVector postMean = posteriorPoids.GetMean();string[] attributNames ={"Prix","Securite","Consommation","Confort"};Console.WriteLine("Evolution des poids MAUT : Prior -> Posterior");Console.WriteLine();Console.WriteLine("Attribut | Prior (uniforme) | Posterior (infere) | Evolution");Console.WriteLine("--------------|------------------|--------------------|-----------");for(int i =0; i < attributNames.Length; i++){double evolution = postMean[i]- priorMean[i];string arrow = evolution >0.05?" ^": evolution <-0.05?" v":" ="; Console.WriteLine($"{attributNames[i],-13} | {priorMean[i],16:P0} | {postMean[i],18:P1} | {evolution,+8:F3}{arrow}");}Console.WriteLine();Console.WriteLine("Interpretation :");Console.WriteLine(" ^ : L'attribut est plus important que prevu (choisi souvent)");Console.WriteLine(" v : L'attribut est moins important que prevu");Console.WriteLine(" = : L'importance reste proche du prior");
Evolution des poids MAUT : Prior -> Posterior
Attribut | Prior (uniforme) | Posterior (infere) | Evolution
--------------|------------------|--------------------|-----------
Prix | 25 % | 33,3 % | 0,083 ^
Securite | 25 % | 44,4 % | 0,194 ^
Consommation | 25 % | 11,1 % | -0,139 v
Confort | 25 % | 11,1 % | -0,139 v
Interpretation :
^ : L'attribut est plus important que prevu (choisi souvent)
v : L'attribut est moins important que prevu
= : L'importance reste proche du prior
Lecture : élicitation contre révélation — deux sources de poids, deux vérités
Le tableau confronte deux manières d’obtenir les poids du même décideur. Les swing weights (méthode des points) viennent d’une élicitation directe : Sécurité 35,7 %, Prix 32,1 % — ce que le décideur dit. L’inférence bayésienne observe 5 choix répétés et révèle : Sécurité 44,4 %, Prix 33,3 % — ce que le décideur fait. L’écart n’est pas une erreur de mesure : c’est l’écart classique entre préférence déclarée et préférence révélée, ici quantifié attribut par attribut. La mécanique est la conjugaison Dirichlet–multinomiale : prior uniforme (1,1,1,1) + comptage des choix (2 prix, 3 sécurité) = posterior (3,4,1,1) — lisible sans calcul, comme le Beta-Bernoulli du notebook précédent. Réserve honnête : cinq observations laissent un posterior très plat (Consommation et Confort retombent à 11,1 % sans avoir jamais été choisis — sont-ils négligeables, ou simplement jamais décisifs dans les choix proposés ?) ; l’inférence gagne à être nourrie.
// Factor graph du modele d'apprentissage bayesien des poids// Recreer un modele simplifie pour illustrationdouble[] pcFG ={1.0,1.0,1.0,1.0};Variable<Vector> poidsFG = Variable.Dirichlet(pcFG).Named("poids");int nObsFG =3;Range obsRangeFG =newRange(nObsFG).Named("observations");VariableArray<int> obsFG = Variable.Array<int>(obsRangeFG).Named("choix_observes");obsFG[obsRangeFG]= Variable.Discrete(poidsFG).ForEach(obsRangeFG);obsFG.ObservedValue=new[]{0,1,1};var enginePoidsFG =newInferenceEngine();enginePoidsFG.Compiler.CompilerChoice= Microsoft.ML.Probabilistic.Compiler.CompilerChoice.Roslyn;// Rendu SVG reel du factor graph via Graphviz (meme demarche que Infer-3 / Infer-4).// Infer.NET ecrit le fichier .gv ; FactorGraphHelper le convertit en SVG via l'outil `dot`.enginePoidsFG.ShowFactorGraph=true;var resultFG = enginePoidsFG.Infer<Dirichlet>(poidsFG);Console.WriteLine("Structure du modele bayesien des poids (rendu SVG ci-dessous).");Console.WriteLine();Console.WriteLine($"Posterior : {resultFG}");Console.WriteLine();Console.WriteLine("Lecture du factor graph :");Console.WriteLine(" - Le noeud Dirichlet (1,1,1,1) encode le prior uniforme sur les poids.");Console.WriteLine(" - La variable latente `poids` (simplex de dimension 4) est tiree de ce prior.");Console.WriteLine(" - Chaque observation Discrete (choix_observes) conditionne ce prior.");Console.WriteLine(" - Le posterior Dirichlet(2 3 1 1) traduit les 3 choix observes (index 1 dominant).");FactorGraphHelper.GetLatestFactorGraphHtml().DisplayAs("text/html");
Compiling model...done.
Structure du modele bayesien des poids (rendu SVG ci-dessous).
Posterior : Dirichlet(2 3 1 1)
Lecture du factor graph :
- Le noeud Dirichlet (1,1,1,1) encode le prior uniforme sur les poids.
- La variable latente `poids` (simplex de dimension 4) est tiree de ce prior.
- Chaque observation Discrete (choix_observes) conditionne ce prior.
- Le posterior Dirichlet(2 3 1 1) traduit les 3 choix observes (index 1 dominant).
Model_09_23_26_09_20_10_31.svg
Exercice 4 : Decision Multi-Critères avec Incertitude
Note pedagogique : ce bloc est positionne en tete de sequence pour l’ordre canonique header -> stub de l’exercice (cf. PR #7880).
Objectif
Etendre la méthode SMART pour gerer des valeurs d’attributs incertaines modèles par des distributions Gaussiennes dans Infer.NET, et calculer la distribution de la valeur totale V pour chaque alternative.
Contexte
Dans la section précédente, nous avons suppose que les valeurs des attributs etaient connues avec certitude. En realite, elles sont souvent incertaines :
Le salaire d’un poste “Freelance” est variable : Gaussian(60k, 100) (moyenne 60k, variance 100)
L’equilibre vie/travail en startup est incertain : Gaussian(4, 1) sur une echelle 1-10
La securite d’emploi d’une startup est difficile a evaluer : Gaussian(3, 2)
Avec Infer.NET, on peut propager cette incertitude jusqu’a la valeur totale V, obtenant une distribution sur V plutot qu’un simple score ponctuel.
Travail a realiser
Reprenez le problème de choix de carriere (Startup, Grande Entreprise, Freelance) de la section 7.
Modeliser l’incertitude : Remplacer les valeurs certaines par des Variable avec distribution Gaussienne
Exemple : Variable<double> salaire_startup = Variable.GaussianFromMeanAndVariance(45, 25).Named("salaire_s");
Utilisez une variance de 5-25 selon le niveau d’incertitude sur chaque attribut
Normaliser les variables : Créer des variables normalisees vSalaire = (salaire - salMin) / (salMax - salMin)
Hint : En Infer.NET, les opérations arithmetiques sur Variable sont supportees directement
Calculer V avec les poids : Créer Variable<double> V = w1*v1 + w2*v2 + w3*v3 + w4*v4
Inferer la distribution de V : Gaussian distV = engine.Infer<Gaussian>(V)
Comparer les distributions : Afficher moyenne et ecart-type de V pour chaque carriere
Analyser l’incertitude : Une carriere avec une V moyenne elevee mais très variable peut etre moins attractive qu’une carriere stable avec V moderee - interpreter le compromis
// Exercice : Decision SMART avec attributs incertains via Infer.NET// Poids SMART (repris de la section 7)double w_sal = 0.30;double w_wlb = 0.25;double w_pas = 0.25;double w_sec = 0.20;// Bornes de normalisation (repris de la section 7)double salMin = 35.0, salMax = 60.0;// TODO 1 : Modeliser la Startup avec des attributs incertains// Valeurs nominales : Salaire=45k, Equilibre=3, Passion=9, Securite=4// Indice: Variable.GaussianFromMeanAndVariance(moyenne, variance) cree une variable aleatoire// Utilisez des variances raisonnables (salaire: 25, attributs 1-10: variance 0.5-2)// TODO 2 : Calculer les valeurs normalisees pour la Startup// Indice: normalisation salaire = (sal - salMin) / (salMax - salMin)// normalisation echelle 1-10 = (val - 1) / 9// Les operations arithmetiques sur Variable<double> fonctionnent directement// TODO 3 : Calculer la valeur totale V pour la Startup// Indice: V = w_sal * v_sal + w_wlb * v_wlb + w_pas * v_pas + w_sec * v_sec// TODO 4 : Repeter pour Grande Entreprise (Salaire=55k, Equilibre=6, Passion=5, Securite=8)// et Freelance (Salaire=60k, Equilibre=5, Passion=8, Securite=3)// TODO 5 : Inferer les distributions de V pour les 3 carrieres// Indice: creez un InferenceEngine avec CompilerChoice.Roslyn// puis engine.Infer<Gaussian>(variable) pour chaque carriere// TODO 6 : Afficher et comparer les distributions// Pour chaque distribution, afficher moyenne, ecart-type et un commentaire sur l incertitude// TODO 7 : Identifier la meilleure option en tenant compte de l incertitude// Indice: Si deux V moyennes sont proches, l option avec le plus petit ecart-type est plus sureConsole.WriteLine("Exercice 4 a completer : modelisation Infer.NET des attributs incertains, propagation de l incertitude vers V.");