LYCÉE → PRÉPA · L13

Module L13 · Partie H · Intelligence artificielle

Apprentissage supervisé : apprendre une fonction à partir d’exemples.

On a des exemples (x, y) — une image et son étiquette, des mesures de capteurs et une panne, une position et une commande — et on veut une fonction f telle que f(x) ≈ y sur des x jamais vus. Tout l’apprentissage automatique tient dans ce « jamais vus » : la généralisation. Ce module construit les algorithmes fondamentaux à la main, avec NumPy, et surtout la méthode pour savoir si ça marche.

Durée : 3 séances · Prérequis : L10-L12. Objectifs : régression linéaire et logistique par gradient, k plus proches voisins, arbres de décision et forêts, fonctions de coût, sur-apprentissage et régularisation, validation croisée, métriques (précision, rappel, matrice de confusion, ROC), pipeline complet sur un jeu de données de capteurs.

Ce que vous saurez faire à la fin
  • Implémenter régression linéaire, régression logistique, kNN et arbre de décision from scratch.
  • Séparer proprement entraînement / validation / test et expliquer pourquoi.
  • Diagnostiquer sous- et sur-apprentissage et y remédier.
  • Choisir la bonne métrique pour un problème déséquilibré (détection de panne).

Références : An Introduction to Statistical Learning (gratuit), cours CS229 (Stanford), freeCodeCamp « Machine Learning with Python », Hands-On Machine Learning (Géron).

Fiche de cours · Définitions

Apprentissage supervisé : définitions

Définition (problème supervisé). Données (xi, yi) i.i.d. selon une loi inconnue P. On cherche une fonction h dans une classe d’hypothèses H minimisant le risque R(h) = EP[ℓ(h(x), y)] pour une perte ℓ. On ne connaît que le risque empirique R̂(h) = (1/n)Σ ℓ(h(xi), yi). Régression : y ∈ ℝ ; classification : y ∈ {classes}.
Définition (pertes usuelles). Quadratique ℓ = (h(x) − y)² ; absolue |h(x) − y| ; 0-1 : 1h(x)≠y ; entropie croisée (log-vraisemblance négative) : −log ph(y | x) ; charnière (SVM) : max(0, 1 − y·h(x)).
Définition (sur-apprentissage, sous-apprentissage, généralisation). Sur-apprentissage : R̂ petit mais R grand (le modèle mémorise le bruit) ; sous-apprentissage : R̂ et R grands (modèle trop simple). L’écart de généralisation est R − R̂. Il croît avec la complexité de H et décroît avec n.
Définition (train / validation / test, validation croisée). Entraînement : ajuster les paramètres. Validation : choisir les hyperparamètres (k, λ, profondeur). Test : estimer R une seule fois. Validation croisée à K plis : K entraînements, chacun testé sur le pli laissé de côté ; moyenne des scores.
Définition (régularisation). Ajouter à R̂ une pénalité sur la complexité : λ‖w‖² (ridge, L2), λ‖w‖₁ (lasso, L1, produit des poids exactement nuls). λ contrôle le compromis biais-variance.
Définition (métriques de classification). Matrice de confusion (VP, FP, FN, VN). Exactitude = (VP + VN)/n ; précision = VP/(VP + FP) ; rappel = VP/(VP + FN) ; F1 = 2·P·R/(P + R) ; courbe ROC (rappel vs taux de faux positifs) et son aire AUC.

Fiche de cours · Formules

Modèles et formules à connaître

ModèlePrédictionApprentissageHyperparamètres
Régression linéaireh(x) = wᵀx + bMoindres carrés : w = (XᵀX + λI)⁻¹Xᵀy (ridge)λ
Régression logistiquep(y=1|x) = σ(wᵀx + b), σ(z) = 1/(1 + e−z)Descente de gradient sur l’entropie croiséeλ, pas α
k plus proches voisinsVote (ou moyenne) des k plus prochesAucun (mémoriser)k, métrique
Arbre de décisionSuite de tests sur une variableGlouton : diviser sur le gain d’impureté (Gini, entropie)Profondeur, feuilles min
Forêt aléatoireMoyenne de B arbresBootstrap + variables aléatoires par nœudB, variables par nœud
SVM linéairesigne(wᵀx + b)Maximiser la marge 2/‖w‖ sous yi(wᵀxi + b) ≥ 1C
Naïf bayésienargmaxy P(y)Π P(xj | y)ComptagesLissage
Gradient de la régression logistique (entropie croisée) : ∇w = (1/n) Σi (σ(wᵀxi) − yi)·xi même forme que pour la régression linéaire : (prédiction − cible) × entrée
Décomposition biais-variance : E[(h(x) − y)²] = (E[h(x)] − f(x))² + Var(h(x)) + σ²bruit
Entropie H(p) = −Σ pk log pk ; Gini = 1 − Σ pk² ; gain d’une division = impureté(parent) − Σ (nenfant/n)·impureté(enfant)
Standardisation : x′ = (x − μ)/σ calculée sur le train seulement, appliquée au test

Fiche de cours · Théorèmes et démonstrations

Démonstrations à savoir refaire (1/2)

Théorème 1 (décomposition biais-variance). Soit y = f(x) + ε avec E[ε] = 0, Var(ε) = σ². Pour un prédicteur h appris sur un échantillon aléatoire D (h dépend de D), en un point x fixé : ED,ε[(h(x) − y)²] = (ED[h(x)] − f(x))² + VarD(h(x)) + σ².
Notons h̄ = ED[h(x)]. (h − y)² = (h − h̄ + h̄ − f − ε)². Développons en trois termes : (h − h̄)², (h̄ − f)², ε², et trois doubles produits. E[(h − h̄)²] = Var(h). (h̄ − f)² est constant. E[ε²] = σ². Doubles produits : E[(h − h̄)(h̄ − f)] = (h̄ − f)E[h − h̄] = 0 ; E[(h − h̄)ε] = 0 et E[(h̄ − f)ε] = 0 car ε est indépendant de D et centré. D’où la formule. Lecture : un modèle rigide (k grand, arbre court, λ grand) a un biais élevé et une variance faible ; un modèle souple l’inverse ; σ² est irréductible. Le sur-apprentissage est le régime « variance ».
Théorème 2 (gradient de l’entropie croisée pour la régression logistique). Avec pi = σ(wᵀxi) et L(w) = −(1/n)Σ [yi log pi + (1 − yi) log(1 − pi)], on a ∇L = (1/n)Σ (pi − yi)xi, et L est convexe.
σ′(z) = σ(z)(1 − σ(z)). Pour un exemple, ∂/∂w [y log p + (1 − y) log(1 − p)] = y·p(1 − p)x/p − (1 − y)·p(1 − p)x/(1 − p) = [y(1 − p) − (1 − y)p]x = (y − p)x. Le signe moins de L donne (p − y)x. Convexité : la hessienne est (1/n)Σ pi(1 − pi)xixiᵀ, somme de matrices semi-définies positives (p(1 − p) ≥ 0). Donc la descente de gradient converge vers le minimum global — contrairement aux réseaux de neurones profonds.

Fiche de cours · Théorèmes et démonstrations

Démonstrations à savoir refaire (2/2)

Théorème 3 (le 1-NN a une erreur asymptotique ≤ 2× l’erreur de Bayes). (Cover-Hart, cas binaire.) Quand n → ∞, l’erreur du plus proche voisin tend vers E[2η(x)(1 − η(x))] ≤ 2R*, où η(x) = P(y = 1 | x) et R* = E[min(η, 1 − η)] est l’erreur du meilleur classifieur possible.
Quand n → ∞, le plus proche voisin x′ de x tend vers x, donc η(x′) → η(x). Le 1-NN se trompe si les étiquettes de x et x′, tirées indépendamment avec probabilité η, diffèrent : P(erreur | x) → η(1 − η) + (1 − η)η = 2η(1 − η). Or 2η(1 − η) ≤ 2 min(η, 1 − η) car max(η, 1 − η) ≤ 1. En intégrant sur x : R1NN ≤ 2R*. Leçon : un modèle sans paramètre et sans hypothèse fait au plus deux fois pire que l’optimal — avec assez de données ; mais « assez » croît exponentiellement avec la dimension (fléau de la dimension).
Théorème 4 (marge maximale du SVM). Pour des données linéairement séparables, l’hyperplan wᵀx + b = 0 qui maximise la distance au point le plus proche est la solution de min ½‖w‖² sous yi(wᵀxi + b) ≥ 1.
La distance d’un point xi à l’hyperplan est |wᵀxi + b|/‖w‖. Multiplier (w, b) par un scalaire ne change pas l’hyperplan ; on normalise pour que le point le plus proche vérifie |wᵀxi + b| = 1. La marge vaut alors 1/‖w‖ de chaque côté, et la maximiser revient à minimiser ‖w‖ (ou ½‖w‖², différentiable) sous les contraintes de bonne classification avec marge 1. C’est un programme quadratique convexe : solution unique. Les points où la contrainte est active sont les vecteurs de support ; la version « marge souple » ajoute C·Σ ξi pour tolérer des erreurs.
Théorème 5 (pourquoi le bagging réduit la variance). La moyenne de B prédicteurs de même variance v et de corrélation ρ deux à deux a une variance ρv + (1 − ρ)v/B.
Var((1/B)Σ hb) = (1/B²)[Σ Var(hb) + Σb≠b′ Cov(hb, hb′)] = (1/B²)[Bv + B(B − 1)ρv] = v/B + ρv(B − 1)/B → ρv quand B → ∞. Le bagging (bootstrap) réduit le terme (1 − ρ)v/B ; la forêt aléatoire réduit en plus ρ en tirant des sous-ensembles de variables à chaque nœud — d’où sa supériorité sur le simple bagging d’arbres.

Fiche de cours · Méthodes

Méthodes et pièges

Méthode — un projet supervisé dans l’ordre. (1) Définir la cible et la métrique métier avant de modéliser. (2) Séparer le test et ne plus y toucher. (3) Explorer : distributions, valeurs manquantes, fuites (une variable qui « connaît » la réponse). (4) Baseline triviale (classe majoritaire, moyenne) puis modèle simple (linéaire). (5) Validation croisée pour les hyperparamètres. (6) Diagnostiquer avec les courbes d’apprentissage : écart train/val grand → variance (plus de données, régularisation, modèle plus simple) ; les deux mauvais → biais (modèle plus riche, meilleures variables). (7) Test final, une fois, avec intervalle de confiance.
Méthode — choisir la métrique. Classes déséquilibrées : jamais l’exactitude seule (99 % de « pas d’obstacle » donne 99 % en prédisant toujours non). Coût des faux négatifs élevé (obstacle raté) → rappel ; coût des faux positifs élevé (freinages inutiles) → précision ; les deux → F1 ou courbe précision-rappel, et choisir le seuil selon les coûts.
Méthode — éviter les fuites. Toute statistique (moyenne, écart-type, encodage) est calculée sur le train uniquement, dans un Pipeline. Données temporelles : séparer par date, jamais au hasard. Données groupées (plusieurs mesures d’un même robot) : séparer par groupe.

Pièges : régler les hyperparamètres sur le test ; comparer des modèles sur une seule séparation ; oublier de standardiser pour k-NN, SVM, régression régularisée ; interpréter un coefficient comme une causalité ; corrélation train/test due à des doublons ; un AUC de 0,99 sur un problème « trop facile » qui cache une fuite.

Fiche de cours · Exercices corrigés

Exercices corrigés

Exercice 1. Un détecteur d’obstacles donne sur 10 000 images : 95 obstacles détectés (VP), 5 ratés (FN), 400 fausses alertes (FP), 9 500 vrais négatifs. Calculer exactitude, précision, rappel, F1. Un second détecteur a VP = 80, FN = 20, FP = 40. Lequel choisir pour un robot rapide ? pour un aspirateur ?
Correction. Détecteur 1 : exactitude (95 + 9500)/10 000 = 95,95 % ; précision 95/495 = 19,2 % ; rappel 95/100 = 95 % ; F1 = 2·0,192·0,95/(0,192 + 0,95) = 0,319. Détecteur 2 : exactitude (80 + 9860)/10 000 = 99,4 % ; précision 80/120 = 66,7 % ; rappel 80 % ; F1 = 0,727. L’exactitude est trompeuse (classe « pas d’obstacle » à 99 %). Robot rapide : rater un obstacle est grave → rappel → détecteur 1 (quitte à freiner 400 fois pour rien) ; aspirateur : une fausse alerte fait juste changer de direction, un ratage n’est pas dangereux → F1/précision → détecteur 2. Le bon choix vient des coûts, pas de la métrique « par défaut ».
Exercice 2. Régression ridge : montrer que w = (XᵀX + λI)⁻¹Xᵀy minimise ‖Xw − y‖² + λ‖w‖² et que la solution existe toujours pour λ > 0, même si XᵀX est singulière.
Correction. J(w) = wᵀXᵀXw − 2yᵀXw + yᵀy + λwᵀw ; ∇J = 2XᵀXw − 2Xᵀy + 2λw = 0 ⇔ (XᵀX + λI)w = Xᵀy. XᵀX est symétrique semi-définie positive (valeurs propres ≥ 0) ; XᵀX + λI a pour valeurs propres λi + λ ≥ λ > 0 : définie positive, donc inversible, et J est strictement convexe : minimum unique. Interprétation : λ « rétrécit » les composantes de w selon les directions de faible variance des données (facteur λi/(λi + λ)), celles que le bruit domine — réduction de variance au prix d’un biais.
Exercice 3. Les courbes d’apprentissage d’un modèle montrent : erreur train 2 %, erreur validation 15 %, et l’erreur de validation ne baisse plus depuis 2 000 exemples. Diagnostic et trois actions, par ordre de priorité.
Correction. Écart train/validation de 13 points : variance (sur-apprentissage). L’erreur de validation qui stagne avec n suggère aussi que le problème n’est pas seulement le manque de données mais que le modèle exploite quelque chose d’absent en validation (variables bruitées, fuite partielle) ou est trop complexe. Actions : (1) régulariser / simplifier (λ plus grand, arbre moins profond, dropout) et sélectionner les variables ; (2) augmenter/varier les données (augmentation, collecte dans de nouvelles conditions) ; (3) vérifier la séparation train/val (doublons, groupes) — si un même robot ou une même scène apparaît des deux côtés, l’erreur de validation est sous-estimée et le vrai écart est encore plus grand.

01 / Régression

Régression linéaire par descente de gradient

Vocabulaire

Paramètres (w, b) : ce que l’algorithme apprend. Hyperparamètres (lr, epochs) : ce que vous choisissez. Epoch : un passage sur toutes les données. Fonction de coût (perte, loss) : ce qu’on minimise. La régression linéaire a une solution exacte ; on la fait par gradient ici parce que c’est le même code qui servira pour la régression logistique et les réseaux de neurones, où il n’y a plus de solution exacte.

01 / Régression

Sur-apprentissage : la courbe qui apprend le bruit

Degré 1 : sous-apprentissage (biais) — le modèle est trop simple. Degré 14 : sur-apprentissage (variance) — il passe par tous les points d’entraînement, y compris le bruit, et se trompe ailleurs. L’erreur d’entraînement descend toujours avec la complexité ; seule l’erreur sur des données jamais vues dit la vérité. La régularisation (pénaliser les grands coefficients) permet un modèle riche qui ne mémorise pas.

02 / Classification

Régression logistique : une probabilité, pas une droite

Pourquoi l’entropie croisée et pas l’erreur quadratique

La sortie est une probabilité p = σ(w·x + b). L’entropie croisée est −log p pour la bonne classe : elle punit très fort une prédiction confiante et fausse (p → 0 pour y = 1 donne un coût → ∞), et son gradient (p − y)·x n’a pas de plateau, contrairement au carré composé avec une sigmoïde. C’est la log-vraisemblance négative d’un modèle de Bernoulli (module L11) : minimiser l’entropie croisée = maximiser la vraisemblance. Pour K classes, la sigmoïde devient le softmax et le même principe s’applique.

02 / Classification

k plus proches voisins et arbre de décision : deux autres familles

Trois façons d’apprendre

Paramétrique (régression logistique) : une forme fixée, des paramètres appris ; rapide, interprétable, limité par la forme. Par mémoire (kNN) : pas d’apprentissage, tout à la prédiction ; k petit = sur-apprentissage, k grand = sous-apprentissage ; coût O(n) par prédiction. Par partition (arbre) : découpe récursive de l’espace par des seuils ; interprétable (« si température > 80 et vibration > 2 alors panne »), mais instable. Une forêt aléatoire moyenne 100 arbres entraînés sur des échantillons bootstrap avec des sous-ensembles de variables : c’est l’algorithme « par défaut » sur données tabulaires, avec le gradient boosting (XGBoost).

03 / Méthode

Le protocole : train / validation / test, et validation croisée

Règle absolue : le jeu de test ne sert qu’une fois, à la fin. Tout choix (modèle, hyperparamètre, prétraitement) fait en regardant le test est une fuite qui rend le score mensonger. Les compétitions Kaggle et les articles utilisent un test caché pour cette raison. Sur des séries temporelles (capteurs), on coupe dans le temps : entraîner sur le passé, tester sur le futur — jamais de mélange aléatoire.

03 / Méthode

Métriques : l’exactitude ment sur les classes rares

Choisir le seuil, c’est choisir un compromis

Précision : parmi les alertes, combien sont vraies. Rappel (sensibilité) : parmi les pannes, combien sont détectées. Baisser le seuil augmente le rappel et baisse la précision. Une panne moteur ratée coûte un robot ; une fausse alerte coûte une vérification : on choisit le seuil selon ces coûts, pas selon l’exactitude. La courbe ROC résume le détecteur indépendamment du seuil ; AUC = 0,5 pour le hasard, 1 pour un détecteur parfait. Pour les classes très déséquilibrées, la courbe précision-rappel est plus parlante.

04 / Pipeline

Un projet complet : prédire la panne à partir des capteurs

Les étapes 2 et 3 utilisent uniquement les statistiques du train : imputer avec la médiane de tout le jeu ferait fuir de l’information du test. Les poids des 4 capteurs de bruit sont proches de 0 : le modèle a « compris » qu’ils ne servent à rien — c’est ce que la régularisation L1 (lasso) pousse exactement à zéro, pour sélectionner les variables.

Cours

Cours 1 — Le cadre : risque, minimisation du risque empirique, et pourquoi ça généralise

Définitions. Les données (x, y) sont tirées d’une loi inconnue D. Un modèle h a un risque R(h) = E(x,y)∼D[perte(h(x), y)] — ce qu’on voudrait minimiser — et un risque empirique R̂(h) = moyenne de la perte sur les n exemples — ce qu’on minimise en pratique. L’erreur de généralisation est R(h) − R̂(h).

Résultat central (esquisse). Pour une classe de modèles H « pas trop riche », avec grande probabilité, R(h) ≤ R̂(h) + O(√(complexité(H)/n)) pour tout h ∈ H. La complexité se mesure par la dimension VC, la complexité de Rademacher, ou simplement le nombre de paramètres. Conséquences : plus de données réduisent l’écart ; un modèle plus riche l’augmente ; la régularisation réduit la classe effective. C’est la justification théorique du compromis biais-variance et de la validation croisée.

PerteFormuleUsageCe qu’elle estime
Quadratique(ŷ − y)²RégressionLa moyenne conditionnelle E[y | x]
Absolue|ŷ − y|Régression robusteLa médiane conditionnelle
Entropie croisée−log p(y)ClassificationLa probabilité conditionnelle P(y | x)
Charnière (hinge)max(0, 1 − y·s)SVMUne marge
0-11[ŷ ≠ y]Évaluation seulementNon dérivable : on optimise un substitut

Cours

Cours 2 — Exemple travaillé : régression logistique, de la vraisemblance au gradient

Modèle. P(y = 1 | x) = σ(w·x + b), σ(z) = 1/(1 + e−z). Vraisemblance des n exemples indépendants : Π p_iy_i(1 − p_i)1−y_i. Log-vraisemblance négative moyenne (= entropie croisée) : J = −(1/n) Σ [y_i log p_i + (1 − y_i) log(1 − p_i)].

Dérivation du gradient. σ′(z) = σ(z)(1 − σ(z)). Pour un exemple : ∂/∂z [−y log p − (1−y) log(1−p)] = −y(1−p) + (1−y)p = p − y. Par la règle de la chaîne, ∂J/∂w = (1/n) Σ (p_i − y_i) x_i = Xᵀ(p − y)/n et ∂J/∂b = moyenne(p − y). Convexité : la hessienne Xᵀ diag(p_i(1−p_i)) X / n est semi-définie positive ⇒ la descente de gradient (ou Newton, très efficace ici) trouve l’optimum global. Avec régularisation L2 (+ λ‖w‖²/2), l’optimum est unique et fini même si les données sont séparables (sinon ‖w‖ → ∞).

Cours

Cours 3 — Prétraitement et fuites : ce qui se passe avant le modèle

ÉtapePourquoiRègle anti-fuite
Séparation train/val/testMesurer la généralisationFaite en premier, avant tout calcul ; par temps pour les séries ; par groupe (un même robot dans un seul jeu) si les exemples sont corrélés
Imputation des manquantsLes modèles n’acceptent pas NaNStatistique calculée sur le train, appliquée au test ; ajouter une colonne « était manquant »
Normalisation / standardisationConditionnement (gradient, kNN, SVM)μ et σ du train ; les arbres n’en ont pas besoin
Encodage des catégoriesTextes → nombresOne-hot pour peu de modalités ; encodage par cible = fuite classique si fait sur tout le jeu
Caractéristiques (features)Rendre le problème linéaire ou localDérivées, moyennes glissantes, produits ; jamais une caractéristique qui contient l’étiquette du futur
Équilibrage des classesClasse rare ignoréePondération de la perte ou sur-échantillonnage du train seulement
PipelineTout enchaîner de façon reproductiblesklearn.pipeline.Pipeline : fit sur train, transform sur test, automatiquement

TP guidé

TP — Un projet d’apprentissage supervisé complet avec scikit-learn (sur PC, 3 h)

Exercices

Exercices auto-corrigés — modèles from scratch

Exercice 1 — Régression linéaire régularisée par gradient, avec arrêt anticipé

ridge_gd(X, y, X_val, y_val, lam, lr, max_epochs) : descente de gradient sur MSE + λ‖w‖², qui s’arrête quand l’erreur de validation n’a pas baissé depuis 20 epochs et renvoie les meilleurs poids (ceux du minimum de validation), plus le nombre d’epochs effectuées.

Correction
def ridge_gd(X, y, Xv, yv, lam=0.0, lr=0.05, max_epochs=3000):
    w, b = np.zeros(X.shape[1]), 0.0; best = (np.inf, None, None, 0); patience = 0
    for e in range(1, max_epochs + 1):
        err = X @ w + b - y
        w -= lr * (2 * X.T @ err / len(y) + 2 * lam * w); b -= lr * 2 * err.mean()
        val = np.mean((Xv @ w + b - yv)**2)
        if val < best[0]: best = (val, w.copy(), b, e); patience = 0
        else:
            patience += 1
            if patience >= 20: break
    return best[1], best[2], e

Exercice 2 — kNN pondéré et validation croisée maison

a) knn_predire(Xtr, ytr, X, k) avec vote pondéré par 1/(distance + ε). b) cv_score(Xtr, ytr, k, plis=5) : exactitude moyenne en validation croisée stratifiée ou non. c) meilleur_k(Xtr, ytr, ks).

Correction
def knn_predire(Xtr, ytr, X, k):
    d = np.sqrt(((X[:, None, :] - Xtr[None, :, :])**2).sum(-1)); idx = np.argsort(d, 1)[:, :k]
    w = 1 / (np.take_along_axis(d, idx, 1) + 1e-6); votes = (w * ytr[idx]).sum(1) / w.sum(1)
    return (votes > 0.5).astype(int)
def cv_score(Xtr, ytr, k, plis=5):
    idx = np.arange(len(ytr)); parts = np.array_split(idx, plis); s = []
    for i in range(plis):
        te = parts[i]; tr = np.concatenate([p for j, p in enumerate(parts) if j != i])
        s.append(np.mean(knn_predire(Xtr[tr], ytr[tr], Xtr[te], k) == ytr[te]))
    return float(np.mean(s))
def meilleur_k(Xtr, ytr, ks): return max(ks, key=lambda k: cv_score(Xtr, ytr, k))

Exercices

Exercices auto-corrigés — évaluation

Exercice 3 — Courbe précision-rappel et seuil optimal

precision_rappel(y, scores) renvoie les listes (précision, rappel) pour chaque seuil possible (tri décroissant des scores). seuil_optimal(y, scores, cout_fn, cout_fp) renvoie le seuil qui minimise cout_fn × (faux négatifs) + cout_fp × (faux positifs).

Correction
def precision_rappel(y, scores):
    o = np.argsort(-scores); ys = y[o]; vp = np.cumsum(ys); n_pos = ys.sum()
    return vp / np.arange(1, len(y) + 1), vp / n_pos
def seuil_optimal(y, scores, cout_fn, cout_fp):
    seuils = np.unique(scores); couts = [cout_fn * ((scores < t) & (y == 1)).sum() + cout_fp * ((scores >= t) & (y == 0)).sum() for t in seuils]
    return seuils[int(np.argmin(couts))]

Exercice 4 — Détecter une fuite

Le jeu ci-dessous contient une colonne « fuite » construite à partir de l’étiquette. Écrivez colonnes_suspectes(X, y, noms) qui renvoie les colonnes dont l’exactitude d’un seuil simple (validation croisée 5 plis, meilleur seuil sur le train) dépasse 0,95 — des candidates à examiner avant tout entraînement.

Correction
def colonnes_suspectes(X, y, noms):
    out = []; idx = np.arange(len(y)); parts = np.array_split(idx, 5)
    for j, nom in enumerate(noms):
        acc = []
        for i in range(5):
            te = parts[i]; tr = np.concatenate([p for k, p in enumerate(parts) if k != i])
            seuils = np.unique(X[tr, j]); best = max(seuils, key=lambda t: max(np.mean((X[tr, j] > t) == y[tr]), np.mean((X[tr, j] <= t) == y[tr])))
            acc.append(max(np.mean((X[te, j] > best) == y[te]), np.mean((X[te, j] <= best) == y[te])))
        if np.mean(acc) > 0.95: out.append(nom)
    return out

05 / Défis

Défi ★ — Softmax : classer en 3 catégories

Consigne

Générez 3 nuages de points 2D (3 types de terrain d’après deux capteurs). Implémentez la régression softmax : scores z = X W + b (n × 3), probabilités p = exp(z)/Σexp(z) (stabilisé en soustrayant le max), coût = −mean(log p[y]), gradient = Xᵀ(p − Y_onehot)/n. Entraînez, mesurez l’exactitude test, tracez les régions de décision, affichez la matrice de confusion 3×3.

Correction (extrait)
def softmax(z): z = z - z.max(1, keepdims=True); e = np.exp(z); return e / e.sum(1, keepdims=True)
Y = np.eye(3)[y]
W, b = np.zeros((2, 3)), np.zeros(3)
for _ in range(500):
    P = softmax(X @ W + b)
    W -= 0.1 * X.T @ (P - Y) / len(y); b -= 0.1 * (P - Y).mean(0)
pred = softmax(X @ W + b).argmax(1)
print("exactitude :", np.mean(pred == y))
conf = np.zeros((3, 3), int)
for a, p in zip(y, pred): conf[a, p] += 1
print(conf)

05 / Défis

Défi ★★ — Forêt aléatoire et importance des variables

Consigne

À partir de la fonction arbre du module : 1) implémentez une forêt de 50 arbres, chacun entraîné sur un échantillon bootstrap (tirage avec remise) et ne considérant à chaque nœud qu’un sous-ensemble aléatoire de √d variables ; prédiction par vote. 2) Comparez arbre seul / forêt sur le jeu « pannes » (exactitude et F1). 3) Importance par permutation : pour chaque variable, mélangez sa colonne dans le test et mesurez la baisse de score. Les 4 capteurs de bruit doivent ressortir à ≈ 0.

Correction (extrait)
def foret(X, y, n_arbres=50, max_prof=6):
    d = X.shape[1]; m = max(1, int(np.sqrt(d))); arbres = []
    for _ in range(n_arbres):
        idx = rng.integers(0, len(y), len(y)); cols = rng.choice(d, m, replace=False)
        arbres.append((cols, arbre(X[idx][:, cols], y[idx], max_prof=max_prof)))
    return arbres
def predire_foret(F, X):
    votes = np.array([[predire_arbre(a, x[cols]) for x in X] for cols, a in F])
    return (votes.mean(0) > 0.5).astype(int)
F = foret(Xi[tr], y[tr]); pred = predire_foret(F, Xi[te])
print("forêt : F1", round(metriques(y[te], pred)["F1"], 3))
base = np.mean(pred == y[te])
for j, nom in enumerate(noms):
    Xp = Xi[te].copy(); rng.shuffle(Xp[:, j])
    print(f"{nom:8s} importance {base - np.mean(predire_foret(F, Xp) == y[te]):+.3f}")

Le bootstrap et le sous-ensemble de variables décorrèlent les arbres : la moyenne de 50 modèles instables mais peu biaisés est stable (la variance d’une moyenne de variables peu corrélées diminue — module L11). C’est le principe du bagging.

05 / Défis

Défi ★★★ — Esprit prépa : biais-variance et le théorème « pas de repas gratuit »

Consigne

1) Décomposition biais-variance : pour le problème polynomial du module, répétez 200 fois (nouveau bruit d’entraînement à chaque fois) l’ajustement de degré d, et calculez en 50 points x : le biais² (écart entre la prédiction moyenne et f_vrai), la variance (dispersion des prédictions) et le bruit. Tracez biais², variance et erreur totale en fonction de d ; vérifiez erreur = biais² + variance + σ². 2) Montrez expérimentalement le compromis : la régularisation λ réduit la variance et augmente le biais. 3) Question ouverte : le théorème « no free lunch » dit qu’aucun algorithme n’est meilleur qu’un autre en moyenne sur tous les problèmes possibles. Pourquoi l’apprentissage marche-t-il quand même dans le monde réel ? (Réfléchissez aux hypothèses implicites : régularité, structure, priors.)

Correction (extrait)
rng = np.random.default_rng(7); xs = np.linspace(0.05, 0.95, 50); sigma = 0.25
print(" d | biais²  | variance | total  | biais²+var+σ²")
for d in (1, 2, 3, 5, 8, 12):
    preds = []
    for _ in range(200):
        xt = rng.uniform(0, 1, 20); yt = f_vrai(xt) + rng.normal(0, sigma, 20)
        preds.append(predire(ajuster(xt, yt, d, 1e-6), xs))
    preds = np.array(preds)
    biais2 = np.mean((preds.mean(0) - f_vrai(xs))**2); var = preds.var(0).mean()
    total = np.mean((preds - (f_vrai(xs) + rng.normal(0, sigma, preds.shape)))**2)
    print(f"{d:2d} | {biais2:.4f} | {var:.4f}   | {total:.4f} | {biais2 + var + sigma**2:.4f}")

Pour la question 3 : les problèmes réels ne sont pas « tous les problèmes possibles ». Les fonctions à apprendre sont lisses, compositionnelles, de basse dimension effective ; les algorithmes encodent ces hypothèses (biais inductif) — la régularité pour les noyaux, la localité et l’invariance par translation pour les convolutions, la compositionnalité pour la profondeur. Comprendre quels biais inductifs rendent les réseaux profonds si efficaces est une question de recherche ouverte (module L24).

06 / Vérification

Vous essayez 20 valeurs de k et gardez celle qui donne la meilleure exactitude sur le jeu de test. Le score obtenu est :

Deux questions supplémentaires

1. Un modèle a 0,1 % d’erreur en entraînement et 15 % en test. Diagnostic ? Sur-apprentissage : plus de données, régularisation, modèle plus simple.

2. Pourquoi normaliser les entrées avant une descente de gradient ? Des échelles différentes donnent une fonction de coût très allongée (mal conditionnée) : la descente zigzague. Les arbres, eux, s’en moquent.

Référence

Les mots à retenir

MotDéfinition
SuperviséApprendre f à partir de couples (x, y).
Fonction de coûtCe qu’on minimise : MSE, entropie croisée.
GénéralisationPerformance sur des données jamais vues.
Sous-/sur-apprentissageModèle trop simple / qui mémorise le bruit.
RégularisationPénaliser la complexité (L2 ridge, L1 lasso).
Validation croiséeChoisir les hyperparamètres sans toucher au test.
Fuite de donnéesInformation du test utilisée à l’entraînement.
Précision / rappel / F1Métriques pour classes déséquilibrées.
ROC / AUCPerformance indépendante du seuil.
kNN / arbre / forêtPar mémoire / par partition / par vote d’arbres.
Biais-varianceErreur = biais² + variance + bruit.

Pour continuer

Vous savez apprendre à partir de données

Module suivant : les réseaux de neurones from scratch — du neurone au perceptron multicouche, rétropropagation en NumPy, et un classifieur de chiffres.

À faire chez soi

← L12SommaireL14 : Réseaux de neurones →