Aller au contenu principal

Mini-projet : Pizza-Bot, déploiement et data

Ce que ce chapitre apporte7 points
  • Modéliser une décision stratégique par une matrice des gains et en trouver les équilibres de Nash.
  • Résoudre un jeu séquentiel par induction à rebours, et décider face à un adversaire de type inconnu.
  • Tester la robustesse d'une décision en faisant varier la croyance qui la fonde.
  • Calculer à la main puis programmer moyenne, variance, covariance, corrélation et droite des moindres carrés.
  • Prédire en respectant le domaine de validité d'un ajustement.
  • Construire une détection d'anomalies temps réel fondée sur la loi normale, et en régler le seuil en fausses alertes par jour.
  • Traduire chaque résultat chiffré en une recommandation compréhensible par un non-spécialiste.

Une jeune entreprise, Pizza-Bot, s'apprête à installer son premier distributeur automatique de pizzas. La direction pose quatre questions : où l'installer, quelle gamme proposer, combien de pizzas prévoir, et comment savoir que la machine se dérègle. Chacune relève d'un des quatre premiers chapitres du parcours, et le projet consiste à y répondre de bout en bout, du brouillon au programme, puis du programme à la phrase qu'on écrit à la direction.

Rien de neuf n'est enseigné ici. Chaque partie commence par un rappel de cours, assez complet pour travailler sans rouvrir le chapitre, mais le chapitre reste la référence : théorie des jeux, jeux séquentiels, corrélation et moindres carrés, supervision et anomalies.

Mini-projet à trous. Chaque bloc de code contient des TODO : on complète, puis on exécute. Un bloc s'exécute seul, sans voir les précédents : les données et les fonctions déjà écrites dont il a besoin sont recopiées en tête. Les lignes assert du bas servent de juge, sur de petits cas qui se calculent de tête ; si aucune ne proteste et que le message de validation s'affiche, la fonction est juste. Les résultats sur les vraies données, eux, s'affichent sans être jugés : c'est à chacun de les vérifier et de les interpréter. Aucune correction ne figure ici : elle est donnée à part, une fois le travail fait. Quelques coups de pouce, repliés, débloquent une étape sans en donner la solution.

Comment travailler

Chaque partie suit le même rythme : un rappel du cours, un temps au brouillon, du code, puis un temps pour interpréter. Le brouillon n'est pas facultatif. Un résultat qu'on ne sait pas obtenir à la main est un résultat qu'on ne sait pas vérifier, et un programme faux affiche ses nombres avec la même assurance qu'un programme juste.

Deux règles pour tout le code du projet :

  • Python de base uniquement : listes, dictionnaires, tuples, boucles, conditions, fonctions. Les fonctions natives sum, len, min, max, abs, round, zip et enumerate sont permises.
  • Aucun import, ni math, ni statistics, ni numpy. La racine carrée s'écrit x ** 0.5. L'objet du projet est d'écrire les formules, pas d'appeler une fonction qui les connaît.

Partie 1 : le duel d'emplacements

Deux emplacements sont libres pour le premier distributeur : le Campus et la Gare. Le concurrent historique, Mamma-Auto, prépare lui aussi une installation, et les deux entreprises décident en même temps, sans connaître le choix de l'autre. Le service marketing a estimé la marge mensuelle de chacune, en milliers d'euros, dans les quatre situations possibles.

Rappel : jeu sous forme normale et équilibre de Nash

Un jeu sous forme normale se décrit par trois choses : les joueurs, leurs stratégies, et les gains de chacun pour chaque combinaison de stratégies. À deux joueurs et deux stratégies, c'est une matrice : chaque case contient le couple (gain du joueur des lignes, gain du joueur des colonnes).

Équilibre de Nash

Un équilibre de Nash est une combinaison de stratégies telle qu'aucun joueur n'a intérêt à changer seul de stratégie, les choix des autres restant fixés. En notant uPBu_{PB} et uMAu_{MA} les gains de Pizza-Bot et de Mamma-Auto, la case (sPB,sMA)(s_{PB}^*, s_{MA}^*) est un équilibre si

uPB(sPB,sMA)uPB(sPB,sMA)  pour tout sPBetuMA(sPB,sMA)uMA(sPB,sMA)  pour tout sMA.u_{PB}(s_{PB}^*, s_{MA}^*) \geq u_{PB}(s_{PB}, s_{MA}^*) \ \text{ pour tout } s_{PB} \qquad \text{et} \qquad u_{MA}(s_{PB}^*, s_{MA}^*) \geq u_{MA}(s_{PB}^*, s_{MA}) \ \text{ pour tout } s_{MA}.

En clair : la première inégalité fixe la colonne de Mamma-Auto et fait varier la ligne de Pizza-Bot ; aucune autre ligne ne doit lui rapporter plus. La seconde fixe la ligne et fait varier la colonne. Deux mots de la définition portent tout le sens. Seul : on ne compare jamais deux cases où les deux joueurs auraient changé ensemble. Et les inégalités sont larges : un joueur peut être indifférent entre deux stratégies, il suffit qu'il ne gagne rien à changer.

La méthode des soulignements
  1. Colonne par colonne, souligner le meilleur gain du joueur des lignes.
  2. Ligne par ligne, souligner le meilleur gain du joueur des colonnes.
  3. Les cases où les deux gains sont soulignés sont les équilibres de Nash.

Ce n'est pas une recette : un gain souligné signifie « ce joueur n'a aucune déviation rentable depuis cette case ». Une case doublement soulignée est donc, mot pour mot, un équilibre.

Quand un jeu a plusieurs équilibres, la définition ne dit pas lequel sera joué. Le chapitre donne quatre critères pour trancher : l'efficacité (un équilibre meilleur pour tous au sens de Pareto), la prudence (celui où l'on risque le moins si l'autre se trompe), le point focal (celui qui saute aux yeux), et l'ordre de jeu (celui qui s'engage en premier impose l'équilibre qui l'avantage).

La matrice du duel

Dans chaque case, le premier nombre est la marge de Pizza-Bot, le second celle de Mamma-Auto, en k€ par mois.

Pizza-Bot \ Mamma-AutoCampusGare
Campus4 ; 49 ; 7
Gare6 ; 83 ; 3

Les nombres se lisent comme une histoire. Deux distributeurs au même endroit se partagent la clientèle et se font concurrence sur les prix (4 et 4 au Campus, 3 et 3 à la Gare, où le passage est plus rapide et le panier plus faible). Séparés, chacun garde sa clientèle ; le Campus rapporte davantage à celui qui l'occupe.

Étape 1.1 : au brouillon

  1. Recopier la matrice et appliquer la méthode des soulignements. Entourer les cases soulignées deux fois.
  2. Tracer les flèches de déviation : de chaque case, une flèche vers la case voisine chaque fois qu'un joueur gagne strictement à y aller seul. Verticale pour Pizza-Bot, qui change de ligne ; horizontale pour Mamma-Auto, qui change de colonne.
  3. L'un des deux joueurs a-t-il une stratégie dominante ?
  4. D'après le motif des flèches, à laquelle des quatre familles de jeux 2 × 2 appartient ce duel : dilemme du prisonnier, coordination, anti-coordination, pile ou face ?

Ce brouillon est la référence de toute la partie : le programme des deux étapes suivantes doit refaire exactement ces gestes, et retrouver exactement ces cases.

Étape 1.2 : les meilleures réponses

Dans le programme, la matrice devient un dictionnaire : la clé est la case, sous forme de couple (choix de Pizza-Bot, choix de Mamma-Auto), et la valeur est le couple de gains.

main.py
Sortie
>_ Prêt à exécuter…

Écrire les deux fonctions de meilleure réponse. Chacune fixe la stratégie de l'adversaire, parcourt les siennes, et garde celle qui lui rapporte le plus.

Les vérifications portent sur une autre matrice que celle du projet : un dilemme du prisonnier. Une fonction juste doit marcher sur n'importe quelle matrice, pas seulement sur celle qu'on avait sous les yeux en l'écrivant.

main.py
Sortie
>_ Prêt à exécuter…

Étape 1.3 : le test de la déviation

Écrire est_equilibre_nash, qui applique la définition à une case, puis trouver_equilibres_nash, qui renvoie la liste de toutes les cases d'équilibre. Un jeu peut en avoir zéro, une ou plusieurs.

Comparer des gains, pas des noms

L'écriture la plus tentante est : « la case est un équilibre si choix_pizzabot est la meilleure réponse de Pizza-Bot et choix_mamma celle de Mamma-Auto ». Elle fonctionne sur la matrice du projet, et elle est fausse. Quand deux stratégies rapportent autant, la fonction de meilleure réponse n'en renvoie qu'une, et la case de l'autre, tout aussi stable, est rejetée. La définition compare des gains avec des inégalités larges ; le programme doit faire de même. La dernière vérification ci-dessous porte précisément sur ce cas.

main.py
Sortie
>_ Prêt à exécuter…

Étape 1.4 : interpréter

Répondre par écrit, en quelques phrases.

  1. Le programme et le brouillon trouvent-ils les mêmes équilibres ?
  2. L'un des deux équilibres domine-t-il l'autre au sens de Pareto ? Lequel chaque entreprise préfère-t-elle ?
  3. Parmi les quatre critères du rappel, lequel peut trancher ici ? Que se passe-t-il si l'une des deux entreprises s'installe avant l'autre ?
Vérification rapideon peut se reprendre

1.Une case où Pizza-Bot est parfaitement satisfait mais où Mamma-Auto gagnerait à changer de colonne est-elle un équilibre de Nash ?

2.Un jeu 2 × 2 a deux équilibres de Nash. Qu'est-ce que cela implique ?

Bonus : l'équilibre en stratégies mixtes

Sans savoir qui arrivera le premier, chacun peut tirer son emplacement au sort. L'équilibre mixte s'obtient par le principe d'indifférence : chaque joueur choisit ses probabilités de façon à rendre l'autre indifférent entre ses deux stratégies.

Avec quelle probabilité qq Pizza-Bot doit-il choisir le Campus pour que Mamma-Auto gagne autant en moyenne au Campus qu'à la Gare ? Même question pour la probabilité pp avec laquelle Mamma-Auto choisit le Campus.


Partie 2 : ordre de jeu et choix de gamme

Mauvaise nouvelle au retour de la pause : Mamma-Auto a été plus rapide et s'est installé le premier. Le jeu n'est plus simultané. Et une seconde question se pose aussitôt : Pizza-Bot doit choisir sa gamme, Discount ou Premium, sans savoir laquelle Mamma-Auto a retenue.

Rappel : arbre, induction à rebours, types et croyance

Quand un joueur voit le choix de l'autre avant de décider, le jeu est séquentiel et se représente par un arbre : chaque nœud porte le joueur qui décide, chaque branche une action, chaque feuille le couple de gains.

Induction à rebours, en trois gestes
  1. Se placer au dernier nœud de décision. Le joueur qui y décide garde la branche qui maximise son propre gain.
  2. Remplacer ce nœud par le couple de gains de la branche retenue.
  3. Recommencer un cran plus haut, jusqu'à la racine.

Un jeu est à information incomplète quand un joueur ignore une caractéristique de l'adversaire lui-même : son coût, sa gamme, ses préférences.

Type, croyance et espérance

On suppose que l'adversaire est d'un type tt parmi plusieurs, et l'on attribue une probabilité P(t)P(t) à chacun : c'est la croyance. Le gain espéré d'une décision gg est alors la moyenne des gains contre chaque type, pondérée par la croyance :

E[ug]=tP(t)×u(g,t)E[u \mid g] = \sum_{t} P(t) \times u(g, t)

En clair : pour chaque type possible de l'adversaire, multiplier sa probabilité par ce que la décision rapporte contre lui, puis additionner. L'adversaire inconnu est remplacé par une loterie entre plusieurs adversaires connus, et le calcul redevient une espérance.

Une croyance s'estime, elle ne se mesure pas

Le résultat dépend entièrement des probabilités attribuées aux types. La conduite à tenir est donc de faire varier la croyance et de chercher à partir de quelle valeur la décision bascule. Si elle bascule tout près de l'estimation, l'analyse ne tranche pas et il faut aller chercher de l'information. Si elle bascule loin, la décision est robuste.

Étape 2.1 : qui s'installe en premier ?

Au brouillon. Reprendre la matrice du duel. Dessiner l'arbre du jeu quand Mamma-Auto joue en premier : la racine est Mamma-Auto, puis un nœud Pizza-Bot pour chacun de ses deux choix, puis quatre feuilles portant les couples (gain de Mamma-Auto ; gain de Pizza-Bot). Le résoudre par induction à rebours.

Code. Écrire induction_a_rebours, qui renvoie le triplet (choix_mamma, choix_pizzabot, gains_de_la_case). Les gains de la case gardent l'ordre de la matrice : (Pizza-Bot, Mamma-Auto).

main.py
Sortie
>_ Prêt à exécuter…

Étape 2.2 : la gamme, contre un adversaire inconnu

Reste la gamme, quel que soit l'emplacement retenu. Personne ne sait encore si le distributeur de Mamma-Auto est Discount ou Premium. Le service commercial estime à 70 % la probabilité qu'il soit Premium. Marge mensuelle de Pizza-Bot, en k€ :

Gamme de Pizza-Bot \ type de Mamma-AutoDiscountPremium
Discount28
Premium103

Deux gammes identiques se livrent une guerre des prix ; deux gammes différentes se partagent la clientèle.

Au brouillon. Calculer l'espérance de gain de chacune des deux gammes, et choisir.

Code. Écrire esperance_gain et meilleure_gamme. Le tableau devient un dictionnaire de dictionnaires : GAINS_GAMME["Premium"]["Discount"] est le gain de Pizza-Bot s'il choisit Premium et que Mamma-Auto est Discount.

main.py
Sortie
>_ Prêt à exécuter…

Étape 2.3 : la décision est-elle robuste ?

Les 70 % n'ont pas été mesurés, ils ont été estimés. Un collègue aussi sérieux aurait pu dire 60 %. La question utile n'est donc pas seulement « quelle gamme ? », mais « à partir de quelle croyance la décision bascule-t-elle ? ».

Au brouillon. Noter c=P(Premium)c = P(\text{Premium}), de sorte que P(Discount)=1cP(\text{Discount}) = 1 - c. Écrire les deux espérances en fonction de cc, puis résoudre l'équation qui les égale. On obtient la croyance de bascule cc^*, sous forme de fraction.

Code. Écrire croyance_de_bascule, qui trouve cc^* par balayage : essayer c=0c = 0, puis 0,0010{,}001, 0,0020{,}002, et ainsi de suite, jusqu'à la première valeur où Discount rapporte au moins autant que Premium.

Un compteur entier, pas un flottant qu'on incrémente

Afficher 0.1 + 0.2 dans la console : le résultat n'est pas exactement 0.3. Ajouter mille fois 0.001 accumule ces petites erreurs. Faire tourner un entier i de 0 à 1000 et calculer c = i / 1000 à chaque tour les évite.

main.py
Sortie
>_ Prêt à exécuter…

Puis répondre par écrit :

  1. Quelle valeur de cc^* le brouillon donne-t-il, et laquelle le programme ? Pourquoi diffèrent-elles légèrement ?
  2. Comparer cc^* à l'estimation de 0,7. La décision est-elle robuste ? Que conseiller à la direction ?

Étape 2.4 : bonus, la valeur de l'information

Une étude de marché permettrait de connaître le type de Mamma-Auto avant de choisir. Contre chaque type, on choisirait alors la meilleure gamme. Calculer l'espérance de gain dans ce cas, puis retrancher l'espérance de la meilleure décision sans information : c'est la valeur de l'information parfaite, le prix maximal qu'on peut payer pour cette étude.

Vérification rapideon peut se reprendre

1.Que représente un « type » dans la partie 2 ?

2.Une décision bascule pour une croyance de 0,45, et la croyance estimée vaut 0,5. Qu'en conclure ?


Partie 3 : prévoir les ventes

Le distributeur pilote a tourné seize jours. Chaque jour, on a relevé la température extérieure et le nombre de pizzas vendues. La direction veut savoir combien de pizzas prévoir selon la météo, et s'il existe des jours où la machine ne couvre plus ses frais : en dessous de 50 pizzas par jour, elle perd de l'argent.

Température (°C)3568101113151718202224262831
Ventes13212611011311210996929282826973616361

Rappel : série double, covariance, corrélation, moindres carrés

Chaque jour est un couple (xi,yi)(x_i, y_i) : c'est une série double. On note NN le nombre de couples, xˉ\bar{x} et yˉ\bar{y} les moyennes, G(xˉ,yˉ)G(\bar{x}, \bar{y}) le point moyen.

Les indicateurs
V(X)=1Ni=1N(xixˉ)2cov(X,Y)=1Ni=1N(xixˉ)(yiyˉ)r=cov(X,Y)σ(X)σ(Y)V(X) = \frac{1}{N}\sum_{i=1}^{N} (x_i - \bar{x})^2 \qquad \operatorname{cov}(X, Y) = \frac{1}{N}\sum_{i=1}^{N} (x_i - \bar{x})(y_i - \bar{y}) \qquad r = \frac{\operatorname{cov}(X, Y)}{\sigma(X)\,\sigma(Y)}

avec σ(X)=V(X)\sigma(X) = \sqrt{V(X)}. On divise par NN : on décrit les seize journées observées.

En clair : la covariance est la moyenne des produits des écarts à la moyenne. Elle est positive si les deux grandeurs montent ensemble, négative si l'une descend quand l'autre monte. Elle porte le produit des deux unités et ne se compare pas d'un jeu de données à l'autre ; rr, lui, est sans unité et compris entre 1-1 et 11. Son carré r2r^2 est la part de la variance de YY expliquée par la relation linéaire : c'est le nombre à écrire dans un rapport.

La droite des moindres carrés

La droite y=ax+by = ax + b qui minimise la somme des carrés des écarts verticaux entre les points et la droite a pour coefficients

a=cov(X,Y)V(X)etb=yˉaxˉa = \frac{\operatorname{cov}(X, Y)}{V(X)} \qquad \text{et} \qquad b = \bar{y} - a\,\bar{x}
Trois contrôles qui ne coûtent rien
  1. La droite passe par le point moyen G(xˉ,yˉ)G(\bar{x}, \bar{y}).
  2. aa et rr ont le même signe, puisqu'ils partagent le numérateur cov(X,Y)\operatorname{cov}(X, Y).
  3. rr se regarde avant d'écrire la droite. Si r|r| est faible, la droite existe quand même, elle ne sert simplement à rien.

Enfin, une droite ne vaut que sur la plage observée. Prédire à l'intérieur de cette plage est une interpolation ; au-dehors, une extrapolation, et rien ne garantit que la relation s'y prolonge.

Étape 3.1 : le nuage d'abord

Tous les indicateurs supposent une relation linéaire, et seul le nuage dit si l'hypothèse tient.

100102030température (°C)pizzas venduesproduit positifproduit positifproduit négatifproduit négatifG
Seize jours de ventes. Le point moyen partage le plan en quatre quadrants ; la couleur de chaque point donne le signe de son produit d'écarts. Point moyen G (16,06 ; 92,06) : 1 point de produit positif, 15 de produit négatif.

Avant de calculer quoi que ce soit, répondre au brouillon :

  1. Les points semblent-ils alignés ?
  2. La covariance sera-t-elle positive ou négative ? Le lire sur les quadrants : dans lesquels se trouvent presque tous les points ?
  3. Proposer, à l'œil, une droite qui passe au milieu du nuage, et noter sa pente approximative.

Garder ce brouillon : la pente estimée à l'œil servira de contrôle à l'étape 3.3. Un calcul qui tombe loin de ce qu'on a trouvé à la main est un calcul à reprendre.

Étape 3.2 : moyenne, variance, covariance

Écrire les trois fonctions à partir des formules du rappel. Les deux listes vont par paires : temperatures[i] et ventes[i] décrivent le même jour.

main.py
Sortie
>_ Prêt à exécuter…
Coup de pouce : parcourir deux listes à la fois

Deux écritures conviennent. Une boucle sur les indices, for i in range(len(x)):, donne accès à x[i] et à y[i]. Ou bien for xi, yi in zip(x, y):, qui fournit directement les couples, et se lit comme la formule.

Étape 3.3 : corrélation et droite

Écrire coefficient_correlation et droite_moindres_carres, puis, sous le commentaire prévu, écrire soi-même les trois contrôles du rappel sous forme d'assert sur les vraies données.

main.py
Sortie
>_ Prêt à exécuter…

Étape 3.4 : prédire, et jusqu'où

Au brouillon. Résoudre ax+b=50ax + b = 50 avec les valeurs trouvées : à quelle température la droite passe-t-elle sous le seuil de rentabilité ?

Code. Écrire dans_la_plage, qui dit si une température est une interpolation, puis compléter le calcul de la température de rentabilité.

main.py
Sortie
>_ Prêt à exécuter…

Puis répondre par écrit :

  1. Traduire la pente aa en une phrase qu'un directeur commercial comprendrait. Même chose pour r2r^2.
  2. Parmi les prévisions à 22 °C et à 40 °C, laquelle peut-on présenter à la direction ? Laquelle faut-il refuser, et pourquoi ?
  3. Que peut-on réellement affirmer sur la température à partir de laquelle la machine n'est plus rentable ?
  4. La chaleur fait-elle baisser les ventes ? Proposer une autre explication qui produirait la même corrélation.
Vérification rapideon peut se reprendre

1.Une droite ajustée sur des journées de 5 à 25 °C passe sous un seuil de rentabilité vers 28 °C. Que peut-on écrire dans un rapport ?

2.La covariance entre température et ventes d'un glacier vaut -120. Que dit ce nombre, pris seul ?


Partie 4 : superviser la buse à mozzarella

La buse du distributeur doit déposer 100 g de mozzarella par pizza. Chaque dose est pesée. L'atelier a fourni 50 pesées de calibration, réalisées sur une buse en bon état. En production, les pesées arrivent une à une. Il faut décider, à chaque pesée, si la buse fonctionne normalement, et arrêter la machine quand elle dérive.

Rappel : loi normale, z-score, seuil de vigilance

Une grandeur qui résulte de la somme de nombreux petits effets indépendants (humidité du fromage, température, vibrations de la machine) suit approximativement une loi normale N(μ,σ2)N(\mu, \sigma^2) : la courbe en cloche, symétrique autour de sa moyenne μ\mu, d'autant plus étalée que son écart-type σ\sigma est grand.

La règle des trois sigmas
  • environ 68 % des valeurs tombent dans [μσ;μ+σ][\mu - \sigma ; \mu + \sigma] ;
  • environ 95 % dans [μ2σ;μ+2σ][\mu - 2\sigma ; \mu + 2\sigma] ;
  • environ 99,7 % dans [μ3σ;μ+3σ][\mu - 3\sigma ; \mu + 3\sigma].

Valable uniquement si la série est normale.

μ−2σ96μ−σ98μ100μ+σ102μ+2σ104masse de mozzarella (g)densité0,9545
± 2 σ
σ = 2

entre 96 et 104, l'aire vaut 0,9545. Déplacer l'écart-type : elle ne bouge pas.

Une buse de moyenne 100 g et d'écart-type 2 g. L'aire teintée couvre deux écarts-types de part et d'autre de la moyenne.
À manipuler
Faire passer la largeur de la zone teintée de un à deux, puis à trois écarts-types, et lire la proportion couverte à chaque cran. Ce qui reste hors de la zone, ce sont les pesées d'une buse parfaitement saine qu'un seuil placé là déclarerait anormales.
Le z-score
z=xμσz = \frac{x - \mu}{\sigma}

En clair : de combien d'écarts-types la pesée xx s'éloigne de la moyenne. Une pesée sort de [μ2σ;μ+2σ][\mu - 2\sigma ; \mu + 2\sigma] exactement quand z>2|z| > 2.

Deux points de méthode viennent du chapitre de supervision.

  • Estimer, c'est diviser par N1N - 1. Les 50 pesées ne sont pas « la série » qu'on décrit, comme les seize journées de la partie 3 : ce sont un échantillon dont on se sert pour juger les pesées à venir. On estime alors l'écart-type en divisant par N1N - 1. Sur de grands échantillons, la différence s'efface.
  • Un seuil se règle en fausses alertes par jour, pas en sigmas. Et le levier le plus efficace pour les réduire sans rater les vraies pannes est la persistance : n'agir qu'après plusieurs anomalies consécutives. Un pic isolé disparaît, une dérive réelle passe.

Étape 4.1 : estimer le comportement normal

Écrire ecart_type_estime, en divisant par N1N - 1 :

σ=1N1i=1N(xiμ)2\sigma = \sqrt{\frac{1}{N - 1}\sum_{i=1}^{N} (x_i - \mu)^2}
main.py
Sortie
>_ Prêt à exécuter…

Répondre ensuite : pourquoi divise-t-on par N1N - 1 ici, et par NN dans la partie 3 ? Que change ce choix sur 50 pesées ?

Étape 4.2 : l'intervalle et ses fausses alertes

Écrire z_score et est_anomalie, puis compter les pesées de l'historique qui sortent de l'intervalle.

main.py
Sortie
>_ Prêt à exécuter…

Répondre ensuite :

  1. L'historique a été enregistré sur une buse saine. Combien de ses pesées sortent pourtant de l'intervalle ? Est-ce anormal ?
  2. Le distributeur sert environ 400 pizzas par jour. Combien de fausses alertes quotidiennes produit un seuil à 2σ2\sigma sur une buse parfaitement réglée ? Et à 3σ3\sigma ?

La figure suivante montre le même arbitrage sur une autre machine : une buse de restauration collective, pesée une fois par minute, jour et nuit, avec trois incidents injectés. Elle compte les fausses alertes et les incidents manqués pour chaque réglage du seuil.

seuil à
103,796,3100,0fausse alerte · relevé 4 · 103.8fausse alerte · relevé 8 · 105.4fausse alerte · relevé 33 · 96fausse alerte · relevé 94 · 95.7fausse alerte · relevé 111 · 96.1fausse alerte · relevé 121 · 104.2fausse alerte · relevé 150 · 95.8fausse alerte · relevé 155 · 95.6fausse alerte · relevé 205 · 103.8fausse alerte · relevé 237 · 95fausse alerte · relevé 243 · 104.8fausse alerte · relevé 248 · 96.3fausse alerte · relevé 281 · 104.2fausse alerte · relevé 295 · 94.3anomalie détectée · relevé 300 · 106.5fausse alerte · relevé 342 · 96.3fausse alerte · relevé 386 · 96fausse alerte · relevé 398 · 103.8fausse alerte · relevé 441 · 105.1fausse alerte · relevé 447 · 96.1fausse alerte · relevé 522 · 96.3fausse alerte · relevé 547 · 106.1fausse alerte · relevé 563 · 95.3fausse alerte · relevé 596 · 95.5fausse alerte · relevé 602 · 94.9fausse alerte · relevé 666 · 104fausse alerte · relevé 686 · 103.8fausse alerte · relevé 721 · 95.1fausse alerte · relevé 739 · 103.9fausse alerte · relevé 748 · 96fausse alerte · relevé 759 · 103.9fausse alerte · relevé 763 · 95.6fausse alerte · relevé 765 · 104fausse alerte · relevé 768 · 96.2fausse alerte · relevé 807 · 104.3fausse alerte · relevé 809 · 96.2anomalie détectée · relevé 820 · 93.8fausse alerte · relevé 852 · 96.2fausse alerte · relevé 871 · 95fausse alerte · relevé 886 · 104.9fausse alerte · relevé 968 · 95.9fausse alerte · relevé 971 · 104.1fausse alerte · relevé 991 · 104.8fausse alerte · relevé 1026 · 105.7fausse alerte · relevé 1076 · 104.3fausse alerte · relevé 1089 · 105.2fausse alerte · relevé 1154 · 96.1fausse alerte · relevé 1179 · 92.9fausse alerte · relevé 1199 · 95.1anomalie détectée · relevé 1200 · 104.6fausse alerte · relevé 1244 · 106.6fausse alerte · relevé 1279 · 104.7fausse alerte · relevé 1280 · 96.1fausse alerte · relevé 1347 · 95.7fausse alerte · relevé 1351 · 104.1fausse alerte · relevé 1355 · 104.7fausse alerte · relevé 1360 · 94.9fausse alerte · relevé 1361 · 105.9fausse alerte · relevé 1384 · 103.8fausse alerte · relevé 1413 · 95.4fausse alerte · relevé 1421 · 93.8fausse alerte · relevé 1429 · 104.1fausse alerte · relevé 1437 · 104.3masse de mozzarella (g) · 1440 relevés

anomalies détectées

3 / 3

aucune manquée

fausses alertes

60

sur 1440 relevés

cadence attendue

environ 66 par jour

4,6 % hors du seuil

C'est la dernière colonne qu'on présente à une équipe d'exploitation, pas le sigma : à 2σ sur un relevé toutes les minutes, la métrique déclenche environ 66 par jour sans qu'il ne se passe rien.

Une journée de pesées à la minute sur une buse saine, et trois incidents injectés. Monter le seuil fait disparaître les fausses alertes, puis les incidents les plus discrets.

Ce qu'il faut y regarder, c'est l'incident à 104,6 g, le plus discret des trois. C'est lui qui fixe le seuil : dès qu'on monte assez pour faire taire les fausses alertes, c'est lui qu'on cesse de voir. Il n'existe pas de réglage sans perte, et la persistance, à l'étape suivante, est le moyen de desserrer cet arbitrage sans toucher au seuil.

Étape 4.3 : la boucle temps réel, avec persistance

Les pesées de production arrivent dans l'ordre de la liste flux_temps_reel. Écrire surveiller, qui les traite une par une, comme si elles arrivaient en direct :

  • chaque pesée affiche une ligne [OK] ou [ALERTE], avec son indice, sa valeur et son z-score ;
  • chaque anomalie est ajoutée à la liste des alertes, sous forme de couple (indice, valeur) ;
  • après persistance anomalies consécutives, la fonction affiche [ARRÊT] et s'interrompt : les pesées suivantes ne sont pas traitées ;
  • elle renvoie le couple (alertes, indice_arret), où indice_arret vaut None si la machine n'a jamais été arrêtée.
main.py
Sortie
>_ Prêt à exécuter…
Coup de pouce : le compteur d'anomalies consécutives

Un compteur qui ne fait qu'augmenter compte les anomalies au total. Pour compter celles qui se suivent, il faut décider de ce qu'il devient quand une pesée normale arrive. La deuxième vérification sert à le trouver.

Étape 4.4 : interpréter

Répondre par écrit :

  1. Quelles pesées ont déclenché une alerte ? À quelle pesée la machine s'est-elle arrêtée ?
  2. La pesée à 103,2 g dépasse nettement la dose de 100 g. Pourquoi n'a-t-elle déclenché aucune alerte ?
  3. Distinguer dans le flux les pics isolés et la dérive. Qu'aurait fait le programme sans la règle de persistance ?
  4. Expliquer en quelques lignes à un technicien de maintenance pourquoi la machine ne s'arrête pas dès la première alerte.

Bonus : l'effet de masquage

L'historique contient une pesée à 93,2 g, étonnamment basse pour une buse saine. Recalculer μ\mu et σ\sigma sans elle, puis comparer le z-score de 93,2 g dans les deux cas. Qu'en conclure sur la façon de constituer un historique de calibration ?

Vérification rapideon peut se reprendre

1.Pourquoi exiger trois anomalies consécutives avant d'arrêter la machine ?

2.Sur une buse de moyenne 100 g et d'écart-type 2 g, une pesée à 103 g a un z-score de 1,5. Que conclure ?


Le rapport à la direction

Le projet se termine là où il a commencé : quatre questions posées par quelqu'un qui ne lira ni matrice, ni formule, ni code. Rédiger une note d'une page qui y répond. Pour chaque question : la recommandation, le chiffre qui la fonde, et la limite de ce chiffre. Une recommandation sans sa limite ne permet pas à la direction de décider en connaissance de cause.

Pour aller plus loin

Le programme complet. Rassembler toutes les fonctions du projet dans un seul fichier pizza_bot.py, qui affiche en une exécution les quatre résultats du rapport. C'est l'occasion de vérifier que chaque fonction ne dépend que de ce qu'on lui passe : un programme qui ne marchait qu'au milieu de la page, avec des variables laissées par un bloc précédent, casse à cet endroit.

Trois emplacements. Ajouter l'Hôpital comme troisième emplacement. Les fonctions de la partie 1 marchent-elles sans modification ? Si elles parcourent EMPLACEMENTS au lieu d'écrire « Campus » et « Gare » en dur, oui. Inventer des gains et chercher les équilibres.

Régler la persistance. Faire varier la persistance de 1 à 5 et compter, sur le flux, les arrêts et le nombre de pizzas servies entre le début de la dérive et l'arrêt. C'est l'arbitrage du seuil, transposé au temps : plus on attend pour être sûr, plus on sert de pizzas mal garnies.

Un seuil qui apprend. Mettre à jour μ\mu et σ\sigma au fil des pesées normales, sur les cinquante dernières par exemple. Que se passe-t-il si la buse dérive très lentement ? Le seuil suit la dérive, et ne la voit plus jamais : c'est la raison pour laquelle on garde en général une référence fixe à côté de la référence glissante.

La méthode

Les quatre parties ont suivi le même chemin, et c'est lui, plus que les formules, qui se réemploie sur un autre cas.

  1. Poser le modèle au brouillon : les joueurs et leurs gains, l'arbre, le nuage, la loi. Si le calcul ne se fait pas à la main sur les données, il ne se fera pas mieux en Python.
  2. Programmer le geste du brouillon, et lui seul : parcourir les stratégies comme on soulignait, remonter l'arbre comme on le remontait, sommer les écarts comme la formule les somme.
  3. Tester sur un cas qui se calcule de tête avant de lancer les vraies données : trois points alignés, une matrice où tout est égal, un flux de sept pesées.
  4. Confronter le programme au brouillon, et aux contrôles que la théorie fournit gratuitement : le point moyen, le signe commun de aa et rr, la croyance de bascule.
  5. Traduire chaque nombre en une phrase, et l'accompagner de sa limite : la croyance sur laquelle repose la décision, la plage hors de laquelle la droite ne dit rien, le nombre de fausses alertes que coûte un seuil.

Synthèse

  • Un équilibre de Nash est une case où aucun joueur ne gagne à dévier seul ; le programme compare des gains, avec la même inégalité large que la définition.
  • Quand plusieurs équilibres coexistent, l'ordre de jeu peut trancher : l'induction à rebours part des feuilles, et chaque joueur y maximise son gain.
  • Face à un adversaire inconnu, on remplace l'inconnue par une croyance sur ses types, et l'on décide sur l'espérance. La décision ne vaut que ce que vaut la croyance : chercher la bascule dit si elle est robuste.
  • Le nuage d'abord, les indicateurs ensuite. r2r^2 se traduit en pourcentage de variance expliquée, la pente en une phrase métier, et la droite ne se prolonge pas hors de la plage observée.
  • Pour décrire une série, on divise par NN ; pour estimer un comportement à partir d'un échantillon, par N1N - 1.
  • Un seuil se juge en fausses alertes par jour. La persistance sépare les pics isolés des dérives, à condition de remettre le compteur à zéro à chaque pesée normale.