Découvrez ce que signifie « apprendre » pour une machine
Imaginez un cabinet médical rennais qui perd chaque semaine une dizaine de créneaux parce que des patients ne viennent pas à leur rendez-vous. Personne n’a écrit de règle du type « si le rendez-vous est pris trois semaines à l’avance, le patient risque d’oublier », et pourtant l’historique du cabinet contient peut-être déjà cette règle, cachée dans des milliers de lignes. L’apprentissage automatique, c’est précisément l’art de faire émerger ce genre de règle à partir d’exemples, plutôt que de la programmer à la main.
Dans ce chapitre, vous allez poser le vocabulaire et les grandes familles de problèmes. Nous suivrons ce cabinet médical tout au long du chapitre (et nous le retrouverons, avec de vraies données, au dernier chapitre du cours).
Programmer une règle ou l’apprendre ?
Section intitulée « Programmer une règle ou l’apprendre ? »En programmation classique, vous écrivez la règle et la machine l’applique : des données entrent, une réponse sort. En apprentissage automatique (machine learning), vous fournissez des données et les réponses attendues, et c’est la machine qui produit la règle. Cette règle apprise s’appelle un modèle. Une fois construit, le modèle s’applique à de nouveaux cas, exactement comme un programme ordinaire.
Une définition classique, due à Tom Mitchell, le dit de façon plus précise : un programme apprend d’une expérience E, pour une tâche T et une mesure de performance P, si sa performance sur T, mesurée par P, s’améliore avec E.
Pour notre cabinet :
- la tâche T consiste à prédire, pour chaque rendez-vous à venir, si le patient viendra ;
- l’expérience E est l’historique des rendez-vous passés, dont on sait s’ils ont été honorés ;
- la performance P pourrait être la proportion d’absences correctement anticipées.
Cette définition a un mérite : elle vous oblige à dire ce que vous mesurez. Vous verrez au chapitre 3 que ce choix de P n’a rien d’anodin.
Le vocabulaire de base : exemples, caractéristiques, étiquettes
Section intitulée « Le vocabulaire de base : exemples, caractéristiques, étiquettes »Avant d’aller plus loin, fixons les mots. Voici un extrait (inventé) de l’historique du cabinet :
| Patient | Âge | Délai de prise (jours) | SMS de rappel | Venu ? |
|---|---|---|---|---|
| A | 34 | 2 | oui | oui |
| B | 71 | 25 | non | non |
| C | 8 | 0 | non | oui |
| D | 45 | 40 | oui | non |
- Chaque ligne est un exemple (on dit aussi instance, individu ou observation). Ici, un exemple est un rendez-vous.
- Chaque colonne décrivant l’exemple est une caractéristique (feature), aussi appelée variable ou attribut : l’âge, le délai, le SMS. On note souvent le vecteur des caractéristiques d’un exemple, et leur nombre.
- La colonne à prédire est l’étiquette (label) ou variable cible, notée . Ici, « Venu ? ».
- L’ensemble des exemples dont on connaît l’étiquette forme le jeu d’apprentissage (training set), noté , où est le nombre d’exemples.
Les caractéristiques n’ont pas toutes la même nature. L’âge est numérique (on peut calculer une moyenne), le SMS est binaire, et une variable comme le quartier du patient serait nominale (des catégories sans ordre). Cette nature compte : certains modèles ne savent manipuler que des nombres, d’autres acceptent directement des catégories. Nous y reviendrons à chaque modèle.
Les grandes familles d’apprentissage
Section intitulée « Les grandes familles d’apprentissage »Toutes les situations ne se ressemblent pas. Ce qui les distingue, c’est surtout ce que vous savez sur vos exemples.
L’apprentissage supervisé
Section intitulée « L’apprentissage supervisé »Vous disposez d’exemples étiquetés : pour chaque rendez-vous passé, vous savez si le patient est venu. Le modèle apprend la relation entre et , puis prédit pour de nouveaux . C’est le cas le plus courant en entreprise, et celui qui occupe l’essentiel de ce cours.
L’apprentissage non supervisé
Section intitulée « L’apprentissage non supervisé »Vous n’avez que les , sans étiquette. Le but n’est plus de prédire, mais de découvrir une structure : regrouper les patients en profils semblables (partitionnement ou clustering), réduire le nombre de dimensions pour visualiser les données, détecter des cas atypiques. Le cabinet pourrait ainsi découvrir, sans rien lui dire, qu’il existe un groupe de jeunes actifs qui prennent rendez-vous au dernier moment.
L’apprentissage semi-supervisé et faiblement supervisé
Section intitulée « L’apprentissage semi-supervisé et faiblement supervisé »Entre les deux, vous avez quelques exemples étiquetés et beaucoup qui ne le sont pas. Étiqueter coûte cher (il faut parfois un médecin pour annoter une radiographie), alors on essaie de tirer parti des exemples non annotés. On parle d’apprentissage faiblement supervisé quand les étiquettes existent mais sont partielles, bruitées ou approximatives (par exemple, produites par des règles automatiques).
L’apprentissage par renforcement
Section intitulée « L’apprentissage par renforcement »Ici, pas de jeu d’exemples tout prêt. Un agent agit dans un environnement et reçoit une récompense (reward) après ses actions. Il apprend, par essais et erreurs, une stratégie qui maximise la récompense cumulée. Pour le cabinet, on pourrait imaginer un agent qui décide chaque jour combien de rendez-vous surréserver, et qui est récompensé quand la salle d’attente est pleine sans être débordée. C’est l’approche des jeux (échecs, go) et d’une partie de la robotique.
| Famille | Ce que vous avez | Ce que vous cherchez | Exemple au cabinet |
|---|---|---|---|
| Supervisé | des couples | prédire | le patient viendra-t-il ? |
| Non supervisé | des seuls | une structure | des profils de patients |
| Semi-supervisé | peu de , beaucoup de | prédire à moindre coût | quelques dossiers annotés à la main |
| Par renforcement | un environnement et des récompenses | une stratégie d’action | une politique de surréservation |
Classification ou régression ?
Section intitulée « Classification ou régression ? »Dans l’apprentissage supervisé, la nature de l’étiquette décide du type de problème.
- Si est une catégorie, on parle de classification. « Viendra / ne viendra pas » est une classification binaire. Reconnaître l’espèce d’une fleur d’iris parmi trois est une classification multiclasse : une seule classe par exemple, parmi possibles. Si un exemple peut recevoir plusieurs étiquettes à la fois (un article de presse à la fois « santé » et « économie »), on parle de classification multi-étiquette (multi-label).
- Si est un nombre réel, on parle de régression : prédire le retard du patient en minutes, ou le prix d’un appartement.
Mais alors, prédire une probabilité d’absence, c’est de la régression ? Bonne question ! Non : le modèle produit bien un nombre entre 0 et 1, mais ce nombre sert à choisir une classe. On reste en classification, avec un modèle qui fournit un score en plus de sa décision.
Quatre façons de ranger les modèles
Section intitulée « Quatre façons de ranger les modèles »Vous rencontrerez beaucoup de modèles dans ce cours. Pour ne pas vous perdre, il est utile de les ranger selon deux axes.
Paramétrique ou non paramétrique
Section intitulée « Paramétrique ou non paramétrique »Un modèle paramétrique a un nombre de paramètres fixé à l’avance, quelle que soit la quantité de données. Une régression linéaire sur trois caractéristiques a toujours quatre paramètres (trois poids et une constante), que vous ayez cent ou un million de rendez-vous. Le modèle résume les données dans ces paramètres, puis on peut oublier les données.
Un modèle non paramétrique voit sa taille croître avec les données. Le cas extrême est celui des k plus proches voisins, qui garde tous les exemples en mémoire et s’en sert au moment de prédire (on le dit aussi paresseux, lazy). Un arbre de décision qu’on laisse pousser sans limite est aussi non paramétrique : plus il y a de données, plus il a de nœuds.
Discriminant ou génératif
Section intitulée « Discriminant ou génératif »Un modèle discriminant apprend directement ce qui sert à décider : la probabilité (lisez « probabilité de sachant »), ou simplement une frontière entre les classes. La régression logistique, les SVM et les arbres sont discriminants.
Un modèle génératif apprend comment les données sont produites dans chaque classe, c’est-à-dire , ainsi que la fréquence de chaque classe . Il obtient ensuite grâce à la formule de Bayes (chapitre 5). Comme il modélise la distribution des , il pourrait en principe générer de nouveaux exemples plausibles, d’où son nom. Le classifieur bayésien naïf est génératif.
| Modèle | Paramétrique ? | Discriminant ou génératif |
|---|---|---|
| Régression logistique | oui | discriminant |
| Bayes naïf | oui | génératif |
| k plus proches voisins | non | ni l’un ni l’autre au sens strict (vote local) |
| Arbre de décision non borné | non | discriminant |
À quoi ressemble l’apprentissage en Python
Section intitulée « À quoi ressemble l’apprentissage en Python »Pour finir, voyons la forme que prend tout cela en pratique. La bibliothèque scikit-learn impose la même interface à tous ses modèles : on crée le modèle, on l’entraîne avec fit, on prédit avec predict. Voici le tableau du cabinet, traduit en code :
import pandas as pdfrom sklearn.tree import DecisionTreeClassifier
# Historique (inventé) : une ligne par rendez-voushistorique = pd.DataFrame({ "age": [34, 71, 8, 45, 52, 23], "delai": [2, 25, 0, 40, 3, 30], "sms": [1, 0, 0, 1, 1, 0], "venu": [1, 0, 1, 0, 1, 0],})
X = historique[["age", "delai", "sms"]] # caractéristiquesy = historique["venu"] # étiquette
modele = DecisionTreeClassifier(max_depth=2)modele.fit(X, y) # apprentissage
nouveau = pd.DataFrame({"age": [60], "delai": [35], "sms": [0]})print(modele.predict(nouveau)) # prédiction pour un rendez-vous à venirSix exemples, c’est évidemment beaucoup trop peu pour apprendre quoi que ce soit de fiable. Ce code montre seulement la mécanique. Toute la difficulté, que vous découvrirez au chapitre suivant, est de savoir si le modèle obtenu fonctionnera sur des rendez-vous qu’il n’a jamais vus.
En résumé
Section intitulée « En résumé »- L’apprentissage automatique produit une règle (le modèle) à partir d’exemples, au lieu de la programmer à la main.
- Un jeu de données se compose d’exemples (lignes) décrits par des caractéristiques (colonnes) ; en supervisé, chaque exemple porte une étiquette .
- Supervisé : on connaît ; non supervisé : on cherche une structure dans les seuls ; semi-supervisé : peu d’étiquettes ; par renforcement : un agent apprend par récompenses.
- Classification si est une catégorie (binaire, multiclasse, multi-étiquette), régression si est un nombre réel.
- Un modèle paramétrique a une taille fixe ; un modèle non paramétrique grandit avec les données.
- Un modèle discriminant apprend ou une frontière ; un modèle génératif apprend et .
Exercices
Section intitulée « Exercices »Exercice · Nommer le type de problème
facilePour chaque situation, dites s’il s’agit d’apprentissage supervisé, non supervisé ou par renforcement et, s’il est supervisé, s’il s’agit de classification ou de régression.
- Prédire la durée (en minutes) d’une consultation à partir du motif et de l’âge du patient.
- Regrouper les 3 000 patients du cabinet en profils, sans idée préalable des groupes.
- Décider si un courriel est un spam, à partir de 10 000 courriels déjà triés.
- Apprendre à un robot à marcher en le récompensant quand il avance sans tomber.
Voir la solution
- Supervisé, régression : l’étiquette (une durée) est un nombre réel, et on dispose de consultations passées dont on connaît la durée.
- Non supervisé : aucune étiquette n’est fournie, on cherche une structure (c’est du partitionnement).
- Supervisé, classification binaire : spam ou non, avec des exemples déjà triés.
- Par renforcement : pas d’exemples étiquetés, mais un agent qui agit et reçoit une récompense.
Exercice · Paramétrique et génératif
moyenLe cabinet hésite entre deux modèles pour prédire les absences : un classifieur bayésien naïf et un classifieur des k plus proches voisins.
- Lequel est paramétrique ? Que se passe-t-il pour la taille de chaque modèle si le cabinet passe de 10 000 à 100 000 rendez-vous d’historique ?
- Lequel est génératif ? Qu’est-ce que cela signifie concrètement ?
Voir la solution
- Le Bayes naïf est paramétrique : il résume chaque classe par un nombre fixe de probabilités (une par valeur de chaque caractéristique et par classe). Passer à 100 000 rendez-vous change la valeur de ces probabilités, pas leur nombre. Les k plus proches voisins sont non paramétriques : le modèle, c’est l’historique lui-même, donc sa taille est multipliée par dix, tout comme le temps de calcul d’une prédiction (il faut comparer le nouveau rendez-vous à tous les anciens).
- Le Bayes naïf est génératif : il apprend, pour chaque classe (« venu », « absent »), comment se répartissent les caractéristiques, , ainsi que la proportion de chaque classe, . Il en déduit ensuite par la formule de Bayes. Les k plus proches voisins, eux, ne modélisent pas la distribution des données : ils décident directement par un vote local.
En code
import numpy as npfrom sklearn.naive_bayes import CategoricalNBfrom sklearn.neighbors import KNeighborsClassifier
rng = np.random.default_rng(0)for m in [10_000, 100_000]: # m rendez-vous inventés : 3 caractéristiques à 4 modalités, étiquette venu/absent X = rng.integers(0, 4, size=(m, 3)) y = rng.integers(0, 2, size=m) nb = CategoricalNB().fit(X, y) # Bayes naïf : une probabilité par (classe, caractéristique, valeur), plus les priors n_nb = sum(t.size for t in nb.feature_log_prob_) + nb.class_count_.size knn = KNeighborsClassifier().fit(X, y) # k-NN : le modèle garde toutes les valeurs de l'historique n_knn = knn.n_samples_fit_ * X.shape[1] print(f"{m} rendez-vous : Bayes naïf {n_nb} nombres, k-NN {n_knn} nombres")# 10000 rendez-vous : Bayes naïf 26 nombres, k-NN 30000 nombres# 100000 rendez-vous : Bayes naïf 26 nombres, k-NN 300000 nombresset.seed(0)for (m in c(10000L, 100000L)) { # m rendez-vous inventés : 3 caractéristiques à 4 modalités, étiquette venu/absent X <- as.data.frame(matrix(sample(0:3, 3 * m, replace = TRUE), ncol = 3)) y <- sample(c("absent", "venu"), m, replace = TRUE) # Bayes naïf : une table P(valeur | classe) par caractéristique, plus les priors tables <- lapply(X, function(col) prop.table(table(y, col), 1)) n_nb <- sum(sapply(tables, length)) + length(unique(y)) # k-NN : le modèle, c'est l'historique complet n_knn <- nrow(X) * ncol(X) cat(m, "rendez-vous : Bayes naïf", n_nb, "nombres, k-NN", n_knn, "nombres\n")}# 10000 rendez-vous : Bayes naïf 26 nombres, k-NN 30000 nombres# 100000 rendez-vous : Bayes naïf 26 nombres, k-NN 300000 nombresExercice · Repérer une fausse variable numérique
moyenDans l’historique du cabinet, le quartier du patient est codé par un entier de 0 à 80 (0 pour « Centre », 1 pour « Beaulieu », etc.). Un collègue propose de donner cette colonne telle quelle à un modèle qui calcule des distances entre patients. Qu’en pensez-vous ?
Voir la solution
C’est une mauvaise idée. Le quartier est une variable nominale : les entiers ne sont que des codes, sans ordre ni écart significatif. Pour un modèle à base de distance, les quartiers 1 et 2 seraient « proches » et les quartiers 1 et 80 « éloignés », ce qui n’a aucun sens géographique ni médical. Il vaut mieux utiliser un encodage one-hot (une colonne binaire par quartier), ou regrouper les quartiers selon une information pertinente (distance au cabinet, par exemple). Nous rencontrerons exactement ce défaut dans l’étude de cas du chapitre 10.
En code
import numpy as np
# Trois patients : quartiers codés 1 (Beaulieu), 2 et 80codes = np.array([1, 2, 80])
# Codes utilisés comme un nombre : 1 et 2 paraissent voisins, 1 et 80 très éloignésprint(abs(codes[0] - codes[1]), abs(codes[0] - codes[2])) # 1 79
# Encodage one-hot : une colonne binaire par quartier (81 quartiers)onehot = np.eye(81)[codes]d = lambda a, b: np.linalg.norm(a - b)print(round(d(onehot[0], onehot[1]), 3), round(d(onehot[0], onehot[2]), 3)) # 1.414 1.414 : tous à égale distance# Trois patients : quartiers codés 1 (Beaulieu), 2 et 80codes <- c(1, 2, 80)
# Codes utilisés comme un nombre : 1 et 2 paraissent voisins, 1 et 80 très éloignésabs(codes[1] - codes[2]); abs(codes[1] - codes[3]) # 1 79
# Encodage one-hot : une colonne binaire par quartier (81 quartiers, codes 0 à 80)onehot <- diag(81)[codes + 1, ]d <- function(a, b) sqrt(sum((a - b)^2))d(onehot[1, ], onehot[2, ]); d(onehot[1, ], onehot[3, ]) # 1.414 1.414 : tous à égale distance