Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
cb36bf2
fix(density,#17040): redressement 1.2 NumPy — 6 cellules Lecture supp…
jsboige Sep 20, 2026
1033298
fix(density,#17040): redressement 2.1 Workflow ML — 3 cellules Lectur…
jsboige Sep 20, 2026
efb7da8
fix(density,#17040): redressement 2.11b Proximal Operators — 8 cellul…
jsboige Sep 20, 2026
52f070c
fix(density,#17040): redressement 2.2 Descente de gradient — 4 cellul…
jsboige Sep 20, 2026
2681487
fix(density,#17040): redressement 2.3 Regression lineaire logistique …
jsboige Sep 20, 2026
2fafa74
fix(density,#17040): redressement 2.3b Naive Bayes — 3 cellules guide…
jsboige Sep 20, 2026
2ca67d2
fix(density,#17040): redressement 2.3d LDA QDA — 5 cellules markdown …
jsboige Sep 20, 2026
163e065
fix(density,#17040): redressement 2.4 Arbres Forets — 5 cellules Lect…
jsboige Sep 20, 2026
5a54c64
fix(density,#17040): redressement 2.5 Biais Variance CV ROC — 7 cellu…
jsboige Sep 20, 2026
d8b0274
fix(density,#17040): redressement 2.6 Clustering KMeans PCA — 5 cellu…
jsboige Sep 20, 2026
207ef08
fix(density,#17040): redressement Lab12b Sequential Orchestration — 5…
jsboige Sep 20, 2026
337453c
fix(density,#17040): redressement Lab12c Agent Handoff — 5 cellules '…
jsboige Sep 20, 2026
cacc5db
fix(#17040): retablir la section Objectif(s) perdue dans 2.11b-Proxim…
claude Sep 20, 2026
f3c42b5
fix(#17040): retablir 7 sections de plan perdues (2.11b, 2.3b, 2.3d, …
claude Sep 21, 2026
e2b7b7e
fix(#17066): retitrer distinctement les 'Lecture du resultat' x4 (Lab…
claude Sep 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -216,36 +216,6 @@
"print(\"dtype :\", mon_array.dtype)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Lecture de la création et inspection d'un tableau** (cellule code[3]) :\n",
"\n",
"La cellule ci-dessus illustre les 4 opérations fondamentales sur un `ndarray` :\n",
"\n",
"1. **`np.array([1, 2, 3, 4, 5])`** : crée un tableau 1D à partir d'une liste\n",
" Python. Le `dtype` est inféré : `int64` sur Linux/Mac, `int32` sur Windows\n",
" (dépend de la plateforme — un piège classique de portabilité).\n",
"2. **`type(mon_array)`** : retourne `numpy.ndarray`. C'est l'identité du type —\n",
" toutes les opérations NumPy sont définies sur cet objet.\n",
"3. **`mon_array.shape`** : retourne `(5,)` — un tuple d'une dimension. Notez la\n",
" virgule : c'est un tuple 1D, pas une simple parenthèse.\n",
"4. **`mon_array.dtype`** : `int64` (ou `int32` sur Windows). Le type des éléments\n",
" est *figé* à la création ; pour changer, il faut créer un nouveau tableau.\n",
"\n",
"**Pourquoi le `dtype` est inféré à la création** : NumPy ne fait pas de\n",
"conversion implicite. Si on mélange `int` et `float`, NumPy *upcast* vers le\n",
"type le plus large (e.g. `int + float → float`). C'est une règle simple mais\n",
"utile pour comprendre pourquoi `np.array([1, 2, 3.0])` produit un tableau\n",
"`float64`.\n",
"\n",
"**Sortie attendue** : un message console montrant la version de NumPy, le\n",
"tableau, son type (`numpy.ndarray`), sa forme (`(5,)`) et son dtype.\n",
"\n",
"**Coût** : < 0.05 seconde (import + création + 4 inspections)."
]
},
{
"cell_type": "markdown",
"id": "8900f682",
Expand Down Expand Up @@ -447,40 +417,6 @@
"print(f\"Vitesse : x{t_boucle / t_vect:.0f} plus rapide en vectorise\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Lecture de la mesure vectorisation** (cellule code[7]) :\n",
"\n",
"La cellule ci-dessus mesure le gain de performance de la vectorisation NumPy\n",
"sur un cas concret : la somme des entiers de 0 à 999 999.\n",
"\n",
"**Le protocole** :\n",
"\n",
"1. Une fonction `somme_boucle(n)` qui boucle en Python pur (`for i in range(n)`).\n",
"2. Une opération vectorisée `x.sum()` où `x = np.arange(N, dtype=np.int64)`.\n",
"3. Chronométrage avec `time.perf_counter()` (haute précision).\n",
"4. Calcul du rapport `t_boucle / t_vect`.\n",
"\n",
"**Pourquoi cette mesure est convaincante** : 1 million d'itérations d'une boucle\n",
"Python implique 1 million de dispatches dans l'interpréteur Python (lookup de\n",
"bytecode, gestion de la pile, etc.). L'opération vectorisée `x.sum()` fait *un\n",
"seul* appel C qui itère sur le tableau via BLAS/LAPACK.\n",
"\n",
"**Sortie attendue** : deux chronométrages et un rapport ~25 (dépend du CPU et\n",
"de la version de NumPy/BLAS). Sur un laptop moderne avec OpenBLAS, on observe\n",
"typiquement 50-100 ms pour la boucle et 2-5 ms pour la version vectorisée.\n",
"\n",
"**Le piège classique** : croire que la vectorisation est « magique ». Elle ne\n",
"l'est pas : c'est juste que le code C est ~25× plus rapide que l'interpréteur\n",
"Python pour cette opération. Le facteur dépend du type d'opération (les boucles\n",
"triviales ont un facteur plus élevé ; les opérations complexes ont un facteur\n",
"plus bas).\n",
"\n",
"**Coût** : < 1 seconde (2 chronométrages sur 1M éléments)."
]
},
{
"cell_type": "markdown",
"id": "26106520",
Expand Down Expand Up @@ -616,52 +552,6 @@
"print(\"m + row shape =\", (m + row).shape)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Lecture du broadcasting cas 1 + 2** (cellules code[9-10]) :\n",
"\n",
"Les deux premières cellules montrent les cas les plus courants du broadcasting :\n",
"\n",
"**Cas 1 (cellule code[9]) : scalaire + tableau**\n",
"\n",
"```python\n",
"a = np.arange(3) # forme (3,)\n",
"a + 10 # broadcasting scalaire -> forme (3,)\n",
"```\n",
"\n",
"Le scalaire `10` est *promu* à la forme `(3,)` avant l'addition. NumPy duplique\n",
"le scalaire pour chaque élément de `a`. C'est l'idiome pour ajouter un offset\n",
"constant à tout un tableau (centrage, normalisation, etc.).\n",
"\n",
"**Cas 2 (cellule code[10]) : vecteur ligne + matrice**\n",
"\n",
"```python\n",
"m = np.arange(6).reshape(2, 3) # forme (2, 3)\n",
"row = np.array([10, 20, 30]) # forme (3,)\n",
"m + row # broadcasting -> forme (2, 3)\n",
"```\n",
"\n",
"Le vecteur ligne `row` (forme `(3,)`) est diffusé à *chaque ligne* de la matrice\n",
"`m` (forme `(2, 3)`). Le résultat a la forme `(2, 3)` — chaque ligne de `m + row`\n",
"est `(m[i, :] + row)`. C'est l'idiome pour ajouter un offset par colonne (e.g.\n",
"bonus par matière à un tableau notes[étudiant, matière]).\n",
"\n",
"**L'alignement des formes (de droite à gauche)** :\n",
"\n",
"NumPy compare les formes *de la droite vers la gauche* :\n",
"- `(2, 3)` vs `(3,)` → aligner à droite → `(2, 3)` vs `(2, 3)`. La dimension\n",
" gauche du vecteur est implicitement 1, qui se diffuse à 2. Compatible.\n",
"- `(2, 3)` vs `(2,)` → `(2, 3)` vs `(2,)` → la dimension droite est 3 vs 2,\n",
" incompatible. NumPy lève `ValueError`.\n",
"\n",
"**Sortie attendue** : deux tableaux avec un offset constant (cas 1) ou un\n",
"vecteur ligne (cas 2) ajouté.\n",
"\n",
"**Coût** : < 0.01 seconde (2 opérations de broadcasting sur de petits tableaux)."
]
},
{
"cell_type": "code",
"execution_count": 6,
Expand Down Expand Up @@ -884,39 +774,6 @@
"print(\"X[[0, 2], [1, 3]] =\", X[[0, 2], [1, 3]], \" (paires (0,1) et (2,3))\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Lecture de l'indexation** (cellule code[14]) :\n",
"\n",
"La cellule ci-dessus illustre les 3 idiomes d'indexation NumPy :\n",
"\n",
"1. **Tranches (slicing)** : `a[1:4]` retourne `[20, 30, 40]` (indices 1 à 3\n",
" exclus). Pour les matrices 2D, `X[:, 0]` retourne la colonne 0 (toutes les\n",
" lignes), `X[1:, 2:]` retourne un bloc en bas-droite.\n",
"2. **Indexation avancée** : `a[[0, 2, 4]]` retourne `[10, 30, 50]` (indices\n",
" piochés arbitrairement). Pour les matrices 2D, `X[[0, 2], [1, 3]]` retourne\n",
" un tableau 1D avec les éléments aux positions `(0, 1)` et `(2, 3)`.\n",
"3. **Masques booléens** : `a[a > 10]` retourne `[20, 30, 40, 50]` (les éléments\n",
" où `a > 10` est True). Le masque est lui-même un `ndarray` de booléens.\n",
"\n",
"**Pourquoi ces idiomes sont cruciaux** :\n",
"\n",
"- Les **tranches** sont la base de la manipulation de tableaux — `X[:, 0]` est\n",
" l'idiome pour « extraire la première colonne » (utilisé dans 80% du code de\n",
" data science).\n",
"- L'**indexation avancée** permet de piocher arbitrairement (e.g. « donne-moi\n",
" les 5 échantillons avec les indices [12, 45, 78, 123, 456] »).\n",
"- Les **masques booléens** sont l'idiome de filtrage — `df[df['age'] > 18]`\n",
" en Pandas, `arr[arr > 10]` en NumPy.\n",
"\n",
"**Sortie attendue** : 4 tableaux affichés : `a[1:4] = [20, 30, 40]`,\n",
"`X[:, 0] = [0, 4, 8]`, `X[1:, 2:] = [[6, 7], [10, 11]]`, etc.\n",
"\n",
"**Coût** : < 0.01 seconde (3 idiomes × ~3 opérations chacun)."
]
},
{
"cell_type": "code",
"execution_count": 10,
Expand Down Expand Up @@ -1159,50 +1016,6 @@
" \" <- changera au prochain run\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Lecture de la sémantique de `axis`** (cellule code[18]) :\n",
"\n",
"La cellule ci-dessus illustre la sémantique de `axis` pour les réductions\n",
"sur une matrice 2D de forme `(2, 3)` (2 lignes, 3 colonnes).\n",
"\n",
"**Le protocole** :\n",
"\n",
"1. `X = np.array([[1, 2, 3], [4, 5, 6]])` : matrice 2×3.\n",
"2. `X.sum()` : 21 (tous les éléments).\n",
"3. `X.sum(axis=0)` : `[5, 7, 9]` — écrase les *lignes*, donc le résultat a\n",
" la forme des colonnes `(3,)`. Chaque entrée est la somme d'une colonne.\n",
"4. `X.sum(axis=1)` : `[6, 15]` — écrase les *colonnes*, donc le résultat a\n",
" la forme des lignes `(2,)`. Chaque entrée est la somme d'une ligne.\n",
"\n",
"**L'intuition visuelle** : imaginez la matrice comme une grille :\n",
"\n",
"```\n",
"1 2 3\n",
"4 5 6\n",
"```\n",
"\n",
"`axis=0` veut dire « pour chaque colonne, additionne *verticalement* » :\n",
"- colonne 0 : 1 + 4 = 5\n",
"- colonne 1 : 2 + 5 = 7\n",
"- colonne 2 : 3 + 6 = 9\n",
"→ résultat : `[5, 7, 9]`\n",
"\n",
"`axis=1` veut dire « pour chaque ligne, additionne *horizontalement* » :\n",
"- ligne 0 : 1 + 2 + 3 = 6\n",
"- ligne 1 : 4 + 5 + 6 = 15\n",
"→ résultat : `[6, 15]`\n",
"\n",
"**Le piège classique** : penser `axis=0` = « lignes » et `axis=1` = « colonnes »\n",
"est *inverse*. NumPy suit la convention « axis est l'axe *écrasé* » : `axis=0`\n",
"écrase l'axe 0 (les lignes), donc les lignes sont agrégées et le résultat\n",
"garde la forme des colonnes.\n",
"\n",
"**Coût** : < 0.01 seconde (3 sommes sur 6 éléments)."
]
},
{
"cell_type": "markdown",
"id": "0cf94d3a",
Expand Down Expand Up @@ -1344,41 +1157,6 @@
" print(\"Exercice a completer : vectorisez ce calcul (carres_vectorises)\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Lecture méthodologique des exercices** (introduction à code[27]) :\n",
"\n",
"Les trois exercices fondamentaux couvrent les 3 idiomes NumPy essentiels :\n",
"\n",
"1. **Exercice A — vectorisation** : remplacer une boucle `for` par une\n",
" opération vectorisée (`** 2`). C'est la *brique élémentaire* de performance.\n",
"2. **Exercice B — masques booléens composés** : `(a > 5) & (a < 20)`. C'est\n",
" l'idiome de filtrage le plus courant en data science.\n",
"3. **Exercice C — broadcasting scalaire** : `notes + 2`. C'est l'idiome de\n",
" normalisation (ajout d'un offset à toutes les valeurs).\n",
"\n",
"**Pourquoi cette progression** : elle reflète la hiérarchie des besoins en ML.\n",
"\n",
"- **Niveau 1 — Vectorisation** : comprendre que `arr ** 2` bat `for i ...`.\n",
"- **Niveau 2 — Masques** : savoir filtrer avec des conditions composées.\n",
"- **Niveau 3 — Broadcasting** : savoir appliquer une transformation à toutes\n",
" les dimensions d'un tableau.\n",
"\n",
"**Pièges classiques** :\n",
"\n",
"- **Vectorisation** : écrire `for i ...: arr[i] = ...` au lieu de `arr ** 2`.\n",
" La boucle bat l'idiome vectorisé.\n",
"- **Masques** : oublier les parenthèses — `(a > 5) & (a < 20)` est correct ;\n",
" `a > 5 & a < 20` est un bug silencieux.\n",
"- **Broadcasting** : utiliser `notes + np.array([2, 2, 2])` au lieu de\n",
" `notes + 2` — les deux fonctionnent, mais le scalaire est plus lisible.\n",
"\n",
"**Coût total** : < 5 secondes (vectorisation + masques + broadcasting sur de\n",
"petits tableaux)."
]
},
{
"cell_type": "markdown",
"id": "82a46888",
Expand Down Expand Up @@ -2056,4 +1834,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,16 +259,6 @@
"print(\"Proportion classe 1 - test : {:.3f}\".format((y_test == 1).mean()))"
]
},
{
"cell_type": "markdown",
"id": "ebc87b17",
"metadata": {},
"source": [
"### Lecture du résultat : pourquoi ces deux nombres méritent votre attention\n",
"\n",
"Les proportions de la classe 1 — **0,483 sur le train** et **0,480 sur le test** — sont presque identiques : c'est exactement l'effet recherché de `stratify=y`. Sans stratification, un tirage au hasard peut s'écarter de quelques points ; sur un jeu de test de 150 observations, deux points de pourcentage représentent trois exemples — peu ici. Mais sur une classe rare (1 % de fraudes, par exemple), un découpage non stratifié peut produire un jeu de test où la classe d'intérêt est quasi absente, et toute métrique calculée dessus devient un non-sens. La stratification est une assurance quasi gratuite contre ce risque d'échantillonnage : elle conserve dans chaque découpage la structure que le modèle doit apprendre ET celle sur laquelle il sera jugé."
]
},
{
"cell_type": "markdown",
"id": "10000006",
Expand Down Expand Up @@ -513,18 +503,6 @@
"print(sweep.loc[sweep['accuracy_test'].idxmax()])"
]
},
{
"cell_type": "markdown",
"id": "0151e132",
"metadata": {},
"source": [
"### Lecture du graphique : les ciseaux du surapprentissage\n",
"\n",
"La courbe d'accuracy **train** monte continûment et finit par atteindre 1,0 : un arbre assez profond mémorise chaque observation, bruit compris. La courbe **test** suit la même trajectoire au début, culmine autour de la **profondeur 4 (accuracy 0,827)**, puis plafonne voire régresse. L'écart qui s'ouvre entre les deux courbes — 0,914 contre 0,827 au sommet du test — est le surapprentissage rendu visible : le modèle gagne sur des exemples déjà vus sans gagner sur des exemples nouveaux.\n",
"\n",
"Deux précautions pour la suite. D'abord, l'argmax du test (exercice 2) est une estimation fragile : sur 150 observations de test, un point d'accuracy représente 1,5 exemple, et le voisinage de l'optimum est plat — la validation croisée (notebook **2.5**) remplacera ce jugement à décision unique par une moyenne sur plusieurs découpages. Ensuite, ce balayage a utilisé le jeu de test pour *choisir* l'hyperparamètre : en pratique, ce choix se fait sur un jeu de validation dédié, et le test ne sert qu'une seule fois, à la toute fin — sinon on optimise contre le juge."
]
},
{
"cell_type": "markdown",
"id": "1000000c",
Expand Down Expand Up @@ -789,16 +767,6 @@
"print(\" aucune statistique du test ne fuite dans l'entraînement.\")"
]
},
{
"cell_type": "markdown",
"id": "39883630",
"metadata": {},
"source": [
"### Lecture : 0,827 — exactement l'accuracy de l'arbre nu à profondeur 4\n",
"\n",
"Ce n'est pas une coïncidence, et c'est l'occasion d'une observation honnête : **un arbre de décision est insensible aux changements d'échelle**. Ses seuils de coupure se déplacent avec la mise à l'échelle, mais les partitions de l'espace des caractéristiques restent les mêmes — le `StandardScaler` ne change donc rien pour ce modèle précis. La valeur du `Pipeline` n'est pas ici un gain d'accuracy : c'est la **discipline**. Dès que le modèle deviendra sensible aux échelles (k-NN, régression régularisée, SVM dans les notebooks suivants), ce même pipeline garantira que le scaler est ajusté sur le train seul ; écrit à la main, le réflexe « scaler puis découper » laisserait la moyenne et l'écart-type du test contaminer l'entraînement — une fuite silencieuse qui gonfle l'accuracy mesurée."
]
},
{
"cell_type": "markdown",
"id": "10000014",
Expand Down Expand Up @@ -900,4 +868,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Loading
Loading