# MANIFEST des figures README

Provenance des images de `assets/readme/` (EPIC #5654, source 1 = extraction d'outputs de notebooks).

**Note d'audit c.488 (2026-07-14)** : migration vague-1 → format standard (sections *Contenu réel vérifié* + *Ce qui n'est PAS dans la figure* par figure). Audit vision **G.1 firsthand** par lecture directe des 6 PNG via `Read` tool. Méthode 4-pass formalisée c.481b-L1 : (1) lecture G.1 PNG + (2) croisement alt-text + (3) croisement MANIFEST + (4) `sha256sum` (ici : 6 SHA distincts — pas de permutation). Bilan : **5 ACCURATE sans correction** (ml21, ml22, ml24, ml25, ml26) + **1 correction réelle** (ml23 — l'alt-text v1 sur-vendait « OLS vs MLE » alors que la figure ne contient que la sigmoïde seule, sans droite OLS superposée).

**Note de migration c.769bis (2026-07-22)** : ajout du champ canonique `Description visuelle` à chacune des 6 entrées existantes, dans l'ordre canonique (Source → SHA → Description visuelle → Alt-text FR → Contenu réel vérifié → Verdict → Ce qui n'est PAS). Champs pré-existants préservés intacts (audit c.488 = 5/6 ACCURATE + 1 correction réelle ml23 déjà fixée c.488 = 0 correction supplémentaire requise c.769bis). Vision-QA MiniMax M3 re-confirmée 6/6 ACCURATE par lecture directe des 6 PNG via `Read` (mandat 2026-07-11 — vision ACTIVE lanes CoursIA-2). Partition-cœur ML/DataScienceWithAgents (10ᵉ famille distincte c.754+).

**Note d'ajout c.16116 (2026-09-14)** : **7ᵉ entrée** — `ml27b-smo-frontiere.png`, produite par la re-exécution Papermill canonique (`scripts/notebook_tools/notebook_tools.py execute`) du notebook `2.7b-SMO-From-Scratch.ipynb` (PR #16116, accrétion `b` de 2.7). L'image était auparavant orpheline (présente sous `assets/readme/` mais absente de la galerie du README et de ce manifeste). Géométrie (`1418×536`, mode RGBA), poids et SHA-256 **mesurés sur l'artefact** par `PIL` + `hashlib` ; verdict de contenu établi par **lecture directe G.1** du PNG via `Read` (les deux titres de panneaux ont été lus dans la figure, et non déduits de la prose du notebook).

## ml21-overfitting.png

- **Source** : notebook `2.1-Workflow-ML.ipynb` (cellule 11, output 0, `display_data` `image/png`)
- **SHA** : `40f2e98a9d17a56f` (taille 33 087 octets)
- **Description visuelle** : Figure matplotlib single panel 630×470 px (std 39.8/38.0/41.6 RGB) « Surapprentissage visible : train vs test selon la complexité ». Axes x = profondeur arbre (1→25), y = accuracy (0.70→1.00). 2 courbes marqueurs ronds : bleue « accuracy train » (saturée à 1.0 dès profondeur 8, mémorisation) + orange « accuracy test » (pic 0.825 à profondeur 4 puis descente 0.77 = surapprentissage). Ligne pointillée verticale grise « max test (profondeur=4) » marque le compromis biais-variance optimal. Légende coin bas-droit.
- **Alt-text (FR)** : Surapprentissage rendu visible : la courbe de score (train vs test) diverge quand la profondeur de l'arbre croît.
- **Poids** : 32.3 KB (natif)
- **Contenu réel vérifié (2026-07-14, c.488)** : **Single panel** matplotlib sans barre d'erreur. Axe x « profondeur de l'arbre (max_depth) » de 1 à 25 (25 points marqués) ; axe y « accuracy » de 0.70 à 1.00. Courbe **bleue « accuracy train »** monte rapidement de ~0.74 (profondeur=1) à 1.00 (profondeur=8) et reste à 1.00 (mémorisation). Courbe **orange « accuracy test »** monte de ~0.705 (profondeur=1) à un pic ~0.825 vers profondeur=4 puis redescend à ~0.77 (profondeur=8+) et reste stable (surapprentissage). Ligne pointillée verticale **grise « max test (profondeur=4) »** matérialise le compromis biais-variance optimal. Title « Surapprentissage visible : train vs test selon la complexité ». Légende en bas à droite.
- **Verdict** : **VRAI** — l'alt-text capture l'enseignement pédagogique (divergence train vs test avec la profondeur). Précision transférable : le pic test = 0.825 à profondeur=4 = compromis biais-variance optimal ; train sature à 1.0 (mémorisation du bruit).
- **Ce qui n'est PAS dans la figure** : pas de barre d'erreur ni IC 95% sur les courbes (un seul `train_test_split`) ; pas de courbe de validation croisée k-fold ; pas d'annotation du score AUC (la métrique est accuracy seulement).

## ml22-learning-rate.png

- **Source** : notebook `2.2-Descente-de-gradient.ipynb` (cellule 13, output 0, `display_data` `image/png`)
- **SHA** : `de5b8cbc136b2e58` (taille 31 297 octets)
- **Description visuelle** : Figure matplotlib single panel 700×470 px (std 34.7/32.0/35.2 RGB) « Effet du learning rate sur la convergence ». Axe x = iteration (0→100), axe y = MSE en **échelle log** (10⁶ à 10⁷¹, soit 60 ordres de grandeur). 3 courbes : bleue « trop petit (0.001) » quasi-plate à 10⁶, orange « bon (0.05) » qui se stabilise en <20 itérations, verte « trop grand (1.5) » qui explose linéairement en log de 10⁶ à ~10⁶² sur 100 itérations. Légende coin haut-gauche.
- **Alt-text (FR)** : Effet du learning rate : trois régimes (trop lent, bon, divergent) sur la même fonction de coût.
- **Poids** : 30.6 KB (natif)
- **Contenu réel vérifié (2026-07-14, c.488)** : **Single panel** matplotlib **échelle log-y**. Axe x « iteration » 0→100 ; axe y « MSE » en log 10⁶ à 10⁷¹ (60 ordres de grandeur). Trois courbes : **« trop petit (0.001) »** bleu (quasi-plat à ~10⁶, descend très lentement), **« bon (0.05) »** orange (descend rapidement et se stabilise en <20 itérations), **« trop grand (1.5) »** vert (explose linéairement en log → de 10⁶ à ~10⁶² sur 100 itérations). Title « Effet du learning rate sur la convergence ». Légende en haut à gauche.
- **Verdict** : **VRAI** — alt-text capture l'enseignement (3 régimes divergent).
- **Ce qui n'est PAS dans la figure** : pas de visualisation du paysage de perte (loss surface) ni des trajectoires dans le plan des paramètres ; pas d'annotation de l'itération où la divergence devient irréversible ; pas de comparaison à un optimizer adaptatif (Adam, RMSProp).

## ml23-sigmoid.png

- **Source** : notebook `2.3-Regression-lineaire-logistique.ipynb` (cellule 13, output 0, `display_data` `image/png`)
- **SHA** : `2c8b33f802054e90` (taille 26 530 octets)
- **Description visuelle** : Figure matplotlib single panel 691×393 px (std 34.5/31.7/30.5 RGB) « La fonction sigmoid ». Axes x = score z=w.x+b (-6→6), y = sigmoid(z)=probabilite (0.0→1.0). **Une seule courbe bleue lisse** passant par (0, 0.5), forme sigmoïde classique. 2 annotations verticales : dashed gris « seuil 0.5 » (asymptote horizontale y=0.5) + dotted gris « z = 0 » (axe de symétrie). Légende coin haut-gauche listant les 2 annotations. **Aucune droite OLS superposée** (audit c.488 a corrigé l'alt-text v1 qui sur-vendait la comparaison).
- **Alt-text (FR)** : Fonction sigmoïde : le score linéaire est écrasé en probabilité dans [0, 1] par la fonction de lien logistique (MLE).
- **Poids** : 25.9 KB (natif)
- **Contenu réel vérifié (2026-07-14, c.488)** : **Single panel** matplotlib **uniquement la fonction sigmoïde**. Axe x « score z = w.x + b » de -6 à 6 ; axe y « sigmoid(z) = probabilite » de 0.0 à 1.0. Courbe **bleue** lisse passant par (0, 0.5). Deux annotations verticales : **dashed gris « seuil 0.5 »** à z=0 et **dotted gris « z = 0 »** (axe de symétrie de la sigmoïde). Title « La fonction sigmoid ». Légende en haut à gauche avec les deux annotations.
- **Verdict** : **CORRIGÉ** (2026-07-14, c.488) — alt-text v1 affirmait « OLS vs MLE : droite vs sigmoïde », sous-entendant une superposition OLS droite dans la même figure. **La cellule ne contient que la sigmoïde seule, sans droite OLS** (la comparaison OLS droite est probablement dans une cellule antérieure ou postérieure du même notebook, ou dans une autre figure). Alt-text corrigé pour ne décrire que ce qui est réellement visible (la fonction sigmoïde seule et son seuil 0.5).
- **Ce qui n'est PAS dans la figure** : pas de droite OLS superposée ; pas de points de données observés (synthetic `make_classification`) ; pas de marqueurs sur le seuil (le seul annoté « 0.5 » est l'asymptote horizontale, pas un point expérimental) ; pas de MLE fitting (c'est la fonction de lien canonique, pas le résultat d'un ajustement).

## ml24-frontiere.png

- **Source** : notebook `2.4-Arbres-Forets-Ensembles.ipynb` (cellule 13, output 0, `display_data` `image/png`)
- **SHA** : `e2f86181761686d8` (taille 180 300 octets)
- **Description visuelle** : Figure matplotlib **2 panneaux côte-à-côte** 1070×441 px (std 51.6/58.8/57.5 RGB, std le plus élevé du lot = signature des fonds pastel rouge/bleu). Axes communs x = mean radius (7.5→27.5), y = mean texture (10→40). ~570 points **bleus** (maligne) / **rouges** (bénigne) entremêlés du dataset `load_breast_cancer` réel, projection 2 features sur 30. **Gauche** : « Arbre seul (forte variance) » — frontière en marches d'escalier rectangles rigides ~5-6 paliers. **Droite** : « Foret aleatoire (variance reduite) » — frontière plus lisse mais peu différente visuellement (l'enseignement est dans l'alignement meilleur des rectangles de fond sur les classes). std G/B très élevés = signature des 2 fonds pastel rouge/bleu distincts.
- **Alt-text (FR)** : Forêt aléatoire : la frontière de décision, escalier d'un arbre seul vs lissage par l'ensemble (réduction de variance géométrique, jeu de cancer du sein `mean radius` × `mean texture`).
- **Poids** : 176.1 KB (natif)
- **Contenu réel vérifié (2026-07-14, c.488)** : **2 panneaux côte à côte** sur le dataset `load_breast_cancer` réel, projection 2D des deux features `mean radius` (axe x 7.5→27.5) × `mean texture` (axe y 10→40). Points **bleus** (classe maligne) et **rouges** (classe bénigne) entremêlés. **Gauche : « Arbre seul (forte variance) »** — frontière en marche d'escalier avec rectangles rigides, ~5-6 paliers verticaux/horizontaux. **Droite : « Foret aleatoire (variance reduite) »** — frontière plus lisse (moins de paliers anguleux) mais pas radicalement différente visuellement ; l'enseignement est dans la **réduction du bruit** (les rectangles rouges/bleus de fond sont mieux alignés sur les classes). Légende par `cmap="coolwarm"` sur `c=y` (0=malignant→bleu, 1=benign→rouge), explicitée par la colorbar « classe (0=malignant, 1=benign) » de la cellule scatter compagnon (le notebook n'utilise pas `ListedColormap`).
- **Verdict** : **VRAI** — alt-text capture l'enseignement (escalier vs lissage, réduction de variance).
- **Ce qui n'est PAS dans la figure** : pas de score/AUC affiché par panneau (la visualisation est géométrique, pas métrique) ; pas de frontière d'un troisième modèle (ex: SVM linéaire) pour comparaison supplémentaire ; pas d'indication du nombre d'arbres dans la forêt ; les deux features choisies ne sont pas les plus discriminantes du dataset (le panel retient 2 features sur 30 pour la lisibilité visuelle, le modèle complet en utilise davantage).

## ml25-roc.png

- **Source** : notebook `2.5-Biais-Variance-CV-ROC.ipynb` (cellule 15, output 1, `display_data` `image/png`)
- **SHA** : `a0a81cae1ece87bc` (taille 33 556 octets)
- **Description visuelle** : Figure matplotlib single panel 590×490 px (std 40.3/37.3/36.1 RGB) « Courbe ROC - Diagnostic cancer du sein ». Axes x = Taux de faux positifs FPR (0.0→1.0), y = Taux de vrais positifs TPR=rappel (0.0→1.0). **1 courbe bleue en marches d'escalier** « Regression logistique (AUC = 0.988) » qui monte quasi-verticalement dans le coin haut-gauche (TPR ~0.95 à FPR ~0.05), puis s'aplatit progressivement vers (1.0, 1.0). **Diagonale pointillée noire** « Hasard (AUC = 0.5) » du coin (0,0) au coin (1,1). Légende coin bas-droite avec les 2 labels et AUC. AUC 0.988 = quasi-parfait.
- **Alt-text (FR)** : Courbe ROC et AUC : le coût du seuil, arbitrage entre faux positifs et faux négatifs (régression logistique sur cancer du sein, AUC = 0.988).
- **Poids** : 32.8 KB (natif)
- **Contenu réel vérifié (2026-07-14, c.488)** : **Single panel** matplotlib. Axe x « Taux de faux positifs (FPR) » 0.0→1.0 ; axe y « Taux de vrais positifs (TPR = rappel) » 0.0→1.0. **Courbe bleue « Regression logistique (AUC = 0.988) »** qui monte quasi-verticalement dans le coin supérieur gauche (TPR ~0.95 à FPR ~0.05), puis s'aplatit progressivement vers (1.0, 1.0). **Diagonale pointillée noire « Hasard (AUC = 0.5) »** du coin (0,0) au coin (1,1). Title « Courbe ROC - Diagnostic cancer du sein ». Légende en bas à droite avec les deux labels et leurs AUC.
- **Verdict** : **VRAI** — alt-text capture l'enseignement (compromis FN/FP via le seuil).
- **Ce qui n'est PAS dans la figure** : pas de courbe ROC comparative d'un deuxième modèle (ex: arbre, SVM) — un seul modèle visualisé ; pas de marqueurs des seuils optimaux selon différents coûts métier (le coût asymétrique FN vs FP est mentionné dans le README mais pas annoté sur la courbe) ; pas d'intervalle de confiance bootstrap sur l'AUC ; pas de matrice de confusion annexée.

## ml26-pca.png

- **Source** : notebook `2.6-Clustering-KMeans-PCA.ipynb` (cellule 11, output 1, `display_data` `image/png`)
- **SHA** : `852911045e527679` (taille 168 736 octets)
- **Description visuelle** : Figure matplotlib **scatter single panel** 656×490 px (std 47.1/46.1/50.7 RGB) « Projection PCA (2 composantes) - couleur = vrai chiffre ». Axes x = Composante principale 1 (-35→35), y = Composante principale 2 (-30→30). 1 797 points colorés selon vrai chiffre (dataset `load_digits` 8×8). 10 amas relativement distincts avec recouvrement significatif (4/5/6, 3/5/8). **Colorbar verticale droite** discrète labels 0→9 + label « chiffre (vrai label, pour verification) ». std ~47 sur les 3 canaux = signature des 10 couleurs vives distinctes. Titre précise « couleur = vrai chiffre » = usage a posteriori pour vérification, pas clustering.
- **Alt-text (FR)** : Réduction de dimension (ACP) : structure latente des chiffres manuscrits retrouvée sans étiquettes sur les deux premières composantes principales (colorisation a posteriori par vrai chiffre, colorbar 0-9).
- **Poids** : 164.8 KB (natif)
- **Contenu réel vérifié (2026-07-14, c.488)** : **Single panel** scatter matplotlib sur le dataset `load_digits` (1 797 images 8×8 = 1 797 points). Axe x « Composante principale 1 » -35→35 ; axe y « Composante principale 2 » -30→30. Points colorés selon le **vrai chiffre** (colorbar discrète à droite avec labels 0/1/2/3/4/5/6/7/8/9 et label « chiffre (vrai label, pour verification) »). Amas relativement distincts mais avec recouvrement significatif entre chiffres visuellement proches (4/5/6, 3/5/8). Title « Projection PCA (2 composantes) - couleur = vrai chiffre ».
- **Verdict** : **VRAI** — alt-text capture l'enseignement (structure retrouvée sans étiquettes via PCA).
- **Ce qui n'est PAS dans la figure** : pas d'annotation du pourcentage de variance expliquée par chaque composante (information essentielle pour interpréter la projection 2D) ; pas de marqueurs des centres KMeans si le notebook enchaîne clustering après PCA (le titre précise « couleur = vrai chiffre » = usage a posteriori, pas clustering) ; pas de graphe de la variance expliquée cumulée (scree plot) ; la colorbar explicite « pour vérification » rappelle que les étiquettes n'ont pas servi à l'ajustement.

## ml27b-smo-frontiere.png

- **Source** : notebook `2.7b-SMO-From-Scratch.ipynb` (cellule 15, output 0, `display_data` `image/png`)
- **SHA** : `b9f12abe7968c218` (taille 158 188 octets)
- **Description visuelle** : Figure matplotlib **2 panneaux côte-à-côte** 1418×536 px (mode RGBA, std 45.8/48.9/45.8). Fond de décision saturé bicolore (régions rose/bleu pastel `#FFAAAA`/`#AAAAFF`) = signature d'un std élevé sur les 3 canaux. Dataset `make_moons` binaire projeté en 2D (axes « x1 » × « x2 »), nuage de points bleus/rouges entremêlé. Les deux panneaux portent les contours de décision −1 / 0 / +1 (le contour central en trait plus épais = frontière f(x)=0), les lignes de marge en pointillés, et les vecteurs supports encerclés. **Gauche** : « SMO from-scratch — acc=0.967, |SV|=48, b=-0.074 » avec légende distinguant 3 régimes KKT : points `non-SV` (petits), `0<α<C (marge)` (gros ronds), `α=C (slack)` (gros carrés). **Droite** : « sklearn SVC — acc=0.967, |SV|=48, b=-0.074 », vecteurs supports en cercles à bords noirs (`facecolors='none'`). Les deux titres portent le même `b = -0.074` à la précision affichée (`:+.3f`), signature visuelle de l'accord SMO ↔ sklearn après propagation du biais.
- **Alt-text (FR)** : Frontière de décision d'un SVM RBF sur `make_moons` : le SMO écrit à la main (gauche) et le `SVC` de sklearn (droite) trouvent la même frontière, les mêmes 48 vecteurs supports et le même biais b = −0.074 dans les deux panneaux.
- **Poids** : 154.5 KB (natif)
- **Contenu réel vérifié (2026-09-14, c.16116)** : **2 panneaux côte à côte**, figure 1418×536 px en mode RGBA. Jeu de données `make_moons` binaire (2 croissants entremêlés, points bleus/rouges). Axes « x1 » (horizontal) × « x2 » (vertical) sur les deux panneaux. Contours de décision à −1 / 0 / +1, régions remplies rose/bleu (`contourf` sur `np.sign(Z)`, couleurs `['#FFAAAA', '#AAAAFF']`). Panneau gauche « SMO from-scratch — acc=0.967, |SV|=48, b=-0.074 » : légende à 3 entrées `non-SV` / `0<α<C (marge)` / `α=C (slack)` (le carré marque les points bornés à α=C). Panneau droit « sklearn SVC — acc=0.967, |SV|=48, b=-0.074 » : mêmes contours, vecteurs supports en cercles creux noirs. **Aucune durée ni timing dans les titres** (les deux titres n'affichent que acc / |SV| / b). Le `b = -0.074` des deux panneaux correspond à la mesure du notebook (cellule 17 : b_SMO −0.0737 vs b_sklearn −0.0741, tous deux formatés `:+.3f` → `-0.074`).
- **Verdict** : **VRAI** — alt-text capture l'enseignement (le solveur écrit à la main retrouve la frontière et les supports de sklearn). Précision transférable : acc=0.967 identique, |SV|=48 identique, b=−0.074 identique à l'affichage ; la propagation corrigée du biais dans `smo_fit` est ce qui rend l'accord visible (avant le fix, le panneau SMO titrait b=+0.000).
- **Ce qui n'est PAS dans la figure** : pas de trace d'itération ni de convergence de la boucle SMO (le panneau est une photo à l'optimum, pas une courbe d'apprentissage) ; pas de « duality gap » ni de valeur KKT annotée (chiffrés dans la cellule 21, pas ici) ; pas de sous-figure du score (`α_i y_i` ou `‖w‖²`) ; pas de barre d'erreur ni de recouvrement inter-seeds (une seule exécution sur un split fixe) ; pas de durée d'exécution affichée (les timings machine-dépendants ont été retirés des titres).
