Entraînez votre premier modèle de machine learning
Le machine learning semble intimidant, mais l’idée centrale est simple : montrer à un ordinateur des exemples de paires entrée/sortie, et il apprend un motif qu’il peut appliquer à de nouvelles données inédites. Dans ce projet, vous allez faire exactement ça, charger un jeu de données classique, entraîner un classificateur d’arbre de décision, et évaluer sa qualité de prédiction. Aucune formation en mathématiques n’est requise.
🎯 Ce que vous allez faire
- Charger et explorer un jeu de données réel.
- Prétraiter des données pour le machine learning.
- Diviser les données en ensembles d’entraînement et de test.
- Entraîner un classificateur d’arbre de décision.
- Évaluer la précision du modèle et créer une matrice de confusion.
- Visualiser les résultats sous forme de matrice de confusion.
Où exécuter ceci
En local avec uv est le chemin principal recommandé pour celui-ci, tout comme les autres Projets concrets de cette série. Les étapes 1 à 6 ci-dessous supposent ce chemin.
GitHub Codespaces fonctionne bien aussi : ouvrez tout le dépôt du cours dans un Codespace gratuit (Node, Python, et uv sont déjà installés, selon le .devcontainer/devcontainer.json du dépôt), et les mêmes commandes uv fonctionnent depuis un onglet de navigateur, sans installation locale.
Google Colab et les notebooks Kaggle sont d’excellents choix ici, entraîner un arbre de décision sur un jeu de données aussi petit ne nécessite aucun GPU, donc un environnement de notebook gratuit est amplement suffisant. Exécutez !pip install scikit-learn pandas matplotlib dans une cellule, puis collez et adaptez le code des étapes ci-dessous.
- Exécutez-le dans le navigateur. Un compagnon notebook interactif est prêt, ouvrez-le dans Colab, Kaggle ou Binder et suivez les étapes dans l’ordre.
Setup
uv init ml-classifier
cd ml-classifier
uv add scikit-learn pandas matplotlib
Étape 1 : Charger et explorer les données
Le jeu de données Iris est l’un des plus célèbres du machine learning. Il contient des mesures (longueur du sépale, largeur du sépale, longueur du pétale, largeur du pétale) pour 150 fleurs d’iris de trois espèces différentes. Votre mission : apprendre à un modèle à prédire l’espèce à partir des mesures.
import pandas as pd
from sklearn.datasets import load_iris
# Load the dataset
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df["species"] = iris.target
df["species_name"] = df["species"].map({0: "setosa", 1: "versicolor", 2: "virginica"})
df.head()
Explorez les données pour comprendre ce avec quoi vous travaillez :
# How many samples per species?
print(df["species_name"].value_counts())
# Basic statistics for each feature
df.describe()
🎯 Résultat attendu : Vous verrez 50 échantillons par espèce (classes équilibrées), et des statistiques montrant des plages comme la longueur du sépale d’environ 4,3 à 7,9 cm.
🩹 Si ça ne marche pas : Si load_iris() échoue, assurez-vous d’avoir exécuté uv add scikit-learn dans l’étape de configuration. Le jeu de données est inclus dans scikit-learn, aucune connexion Internet n’est requise.
1.1 Vérifie qu’il s’importe et se charge proprement
✅ Liste de vérification
- ✅
dfcontient 150 lignes et 6 colonnes (4 mesures +species+species_name). - ✅
df["species_name"].value_counts()affiche 50 pour chaque espèce. - ✅
df.describe()montre des statistiques raisonnables pour chaque mesure, pas de NaN, pas de valeurs absurdes.
🤔 Question(s) socratique(s)
- Pourquoi avons-nous ajouté
species_namealors quespecies(0, 1, 2) suffit pour le modèle ? Dans quel cas l’un ou l’autre serait préférable ? - Que se passerait-il si une espèce avait 150 échantillons et les deux autres seulement 10 ? Comment cela affecterait-il l’entraînement ?
Étape 2 : Prétraiter les caractéristiques
Séparez les caractéristiques d’entrée (les mesures) de la cible (l’étiquette d’espèce). Chaque colonne entrant dans le modèle doit être numérique, heureusement, les caractéristiques de l’Iris le sont déjà, donc aucun encodage n’est nécessaire.
X = df.drop(columns=["species", "species_name"])
y = df["species"]
print(f"Features shape: {X.shape}")
print(f"Target shape: {y.shape}")
🎯 Résultat attendu : Features shape: (150, 4) et Target shape: (150,), 150 lignes, 4 colonnes de caractéristiques.
🩹 Si ça ne marche pas : Si vous voyez des colonnes de type object dans X.dtypes, vous avez accidentellement inclus des colonnes de chaînes. Supprimez tout ce qui n’est pas une mesure numérique.
2.1 Vérifie la séparation
✅ Liste de vérification
- ✅
X.shapeest(150, 4). - ✅
y.shapeest(150,). - ✅
X.dtypesmontre uniquement des colonnesfloat64. - ✅
Xne contient passpeciesnispecies_name.
🤔 Question(s) socratique(s)
- Pourquoi ne garde-t-on pas
species_namedansX, ne pourrait-il pas aider le modèle à prédire l’espèce ? - Que se passerait-il si
Xcontenait une colonne de typeobjectnon numérique ? Comment le modèle réagirait-il ?
Étape 3 : Division entraînement/test
La précision d’un modèle sur les données sur lesquelles il a été entraîné ne vous dit presque rien. Vous devez mettre de côté des données que le modèle ne verra jamais pendant l’entraînement, puis évaluer sur cette portion mise de côté. C’est l’habitude la plus importante en machine learning.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"Training set: {X_train.shape[0]} samples")
print(f"Test set: {X_test.shape[0]} samples")
test_size=0.2 met de côté 20 % des lignes pour le test (30 échantillons). random_state=42 rend la division reproductible, vous obtiendrez les mêmes lignes à chaque fois.
🎯 Résultat attendu : Training set: 120 samples, Test set: 30 samples.
🩹 Si ça ne marche pas : Si les chiffres n’additionnent pas 150, vérifiez votre test_size. Si y_test ne contient qu’une seule espèce, votre déséquilibrée, essayez un random_state différent ou vérifiez que y contient bien les trois classes.
Fuite de données
Toujours diviser après avoir chargé les données mais avant toute transformation qui résume le jeu de données (comme la mise à l’échelle ou l’encodage). Ici les caractéristiques de l’Iris sont déjà numériques et sur des échelles similaires, donc pas de risque de fuite, mais cette discipline compte pour des jeux de données plus sales.
3.1 Vérifie la division
✅ Liste de vérification
- ✅
X_train.shape[0]+X_test.shape[0]égale 150. - ✅ Relancer la division avec le même
random_statereproduit exactement les mêmes lignes dansX_test. - ✅
y_trainety_testcontiennent tous les deux des 0, des 1, et des 2, pas une seule espèce.
🤔 Question(s) socratique(s)
- Si vous entraîniez un modèle et l’évaluiez sur
X_train/y_trainau lieu deX_test/y_testpar erreur, vous attendriez-vous à une précision meilleure ou pire que le vrai chiffre, et pourquoi ? - Que donnerait
test_size=0.5? Avantages et inconvénients ?
Étape 4 : Entraîner un classificateur
Un arbre de décision pose une série de questions oui/non sur les caractéristiques (par ex. « la longueur du pétale est-elle > 2,5 ? ») et arrive à une prédiction. C’est intuitif, rapide, et fonctionne bien comme premier modèle.
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier(random_state=42)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
.fit(X_train, y_train) est là où l’apprentissage a lieu, le modèle ne voit jamais X_test pendant cette étape. Ensuite .predict(X_test) applique ce qu’il a appris aux données mises de côté.
🎯 Résultat attendu : predictions est un tableau de longueur 30 ne contenant que des 0, 1, ou 2 (les étiquettes d’espèce).
🩹 Si ça ne marche pas : Si vous voyez un avertissement sur les noms de caractéristiques, vous avez peut-être passé un DataFrame avec des colonnes supplémentaires. Assurez-vous que X_train et X_test ne contiennent que les quatre colonnes numériques de caractéristiques.
4.1 Vérifie l’entraînement
✅ Liste de vérification
- ✅
model.fit(...)s’exécute sans erreur. - ✅
predictionscontient 30 valeurs, chacune étant 0, 1, ou 2. - ✅
predictionsest de la même longueur quey_test.
🤔 Question(s) socratique(s)
model.fit(X_train, y_train)voit les vraies réponses (y_train) pendant l’entraînement, est-ce de la triche ? Pourquoi est-ce acceptable ici mais pas lors de l’évaluation ?- Que se passerait-il si vous appeliez
model.predict(X_train)au lieu demodel.predict(X_test)?
Étape 5 : Évaluer le modèle
Commencez par la précision (accuracy), la fraction des prédictions correctes, puis creusez plus profond avec la précision (precision), le rappel (recall), et une matrice de confusion.
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix
accuracy = accuracy_score(y_test, predictions)
precision = precision_score(y_test, predictions, average="weighted")
recall = recall_score(y_test, predictions, average="weighted")
print(f"Accuracy: {accuracy:.1%}")
print(f"Precision: {precision:.1%}")
print(f"Recall: {recall:.1%}")
La précision (accuracy) indique le taux de réussite global. La précision (precision) indique, pour chaque fois que le modèle a prédit une espèce, combien de fois il avait raison. Le rappel (recall) indique, pour chaque espèce réelle, combien le modèle en a trouvé. Le paramètre average="weighted" gère le cas multi-classes en moyennant sur les trois espèces.
🎯 Résultat attendu : Les trois métriques devraient être d’environ 90 à 100 % sur ce jeu de données, l’Iris est suffisamment séparé pour qu’un arbre de décision fonctionne très bien.
🩹 Si ça ne marche pas : Si la précision est exactement de 33 %, le modèle devine aléatoirement (niveau chance pour 3 classes). Vérifiez que X_train et y_train n’ont pas été mélangés indépendamment, ils doivent rester alignés.
5.1 Vérifie les métriques
✅ Liste de vérification
- ✅ Les trois scores (accuracy, precision, recall) sont tous supérieurs à 90 %.
- ✅ Vous comprenez la différence entre accuracy, precision, et recall.
- ✅ Vous pouvez expliquer pourquoi
average="weighted"est nécessaire ici.
🤔 Question(s) socratique(s)
- Si la accuracy est de 95 % mais que le jeu de données est déséquilibré (95 % d’une seule classe), que vous dit vraiment ce chiffre ?
- Dans quel cas le rappel serait-il plus important que la précision (precision), par ex. dans un contexte médical ?
Étape 6 : Visualiser les résultats
Une matrice de confusion montre exactement quelles espèces le modèle a confondues. La visualiser rend le motif évident en un coup d’œil.
import matplotlib.pyplot as plt
import numpy as np
cm = confusion_matrix(y_test, predictions)
fig, ax = plt.subplots(figsize=(6, 5))
im = ax.imshow(cm, cmap="Blues")
ax.set_xticks(range(3))
ax.set_yticks(range(3))
ax.set_xticklabels(iris.target_names)
ax.set_yticklabels(iris.target_names)
ax.set_xlabel("Predicted")
ax.set_ylabel("Actual")
ax.set_title("Confusion Matrix")
# Add count labels in each cell
for i in range(3):
for j in range(3):
ax.text(j, i, str(cm[i, j]), ha="center", va="center",
color="white" if cm[i, j] > cm.max() / 2 else "black")
plt.colorbar(im)
plt.tight_layout()
plt.show()
Les cellules diagonales (de haut en gauche à droite en bas) montrent les prédictions correctes. Les cellules hors diagonale montrent les erreurs, par exemple, si versicolor et virginica sont parfois confondues, cette cellule s’allumera.
🎯 Résultat attendu : Une grille 3×3 avec des nombres élevés sur la diagonale et des zéros (ou presque) en dehors. Un modèle parfait n’aurait que des entrées diagonales.
🩹 Si ça ne marche pas : Si le graphique n’apparaît pas, assurez-vous de courir dans un environnement avec affichage (Jupyter, VS Code, ou un script local). Dans un terminal sans affichage, remplacez plt.show() par plt.savefig("confusion_matrix.png") pour sauvegarder la figure dans un fichier.
6.1 Vérifie la visualisation
✅ Liste de vérification
- ✅ Le graphique affiche une grille 3×3 avec les noms d’espèce sur les deux axes.
- ✅ La diagonale contient des nombres élevés (proches de 30) ; les autres cellules sont proches de zéro.
- ✅ La légende de couleur montre une échelle de blanc (zéro) à bleu foncé (maximum).
🤔 Question(s) socratique(s)
- Si versicolor et virginica étaient souvent confondues, quelle cellule de la matrice s’allumerait, et que dirait cela sur les caractéristiques de ces deux espèces ?
- La matrice de confusion montre les erreurs, mais pas leur * gravité *, dans quel contexte serait-il plus grave de confondre versicolor avec virginica que l’inverse ?
🧩 Défis
- Essayez un classificateur différent. Remplacez
DecisionTreeClassifierparRandomForestClassifier(ajoutezfrom sklearn.ensemble import RandomForestClassifier). Comment la précision change-t-elle ? - Réglez l’arbre. Définissez
max_depth=2lors de la création duDecisionTreeClassifier. Que devient la précision ? Et avecmax_depth=10? - Importance des caractéristiques. Après l’entraînement, affichez
model.feature_importances_à côté deiris.feature_names. Quelle caractéristique compte le plus pour prédire l’espèce ? - Testez une autre division. Changez
test_sizeà 0,3 ou 0,1. Comment le chiffre de précision évolue-t-il ? Lancez la division 10 fois avec différentes valeurs derandom_stateet rapportez la fourchette des scores de précision.
Ce que vous avez appris
Vous avez construit un flux de travail complet de machine learning : charger des données, préparer les caractéristiques, diviser en ensembles d’entraînement et de test, entraîner un classificateur, évaluer avec plusieurs métriques, et visualiser les résultats. Le flux de travail, préparer → diviser → entraîner → évaluer, est la même forme utilisée pour chaque tâche d’apprentissage supervisé, qu’il s’agisse d’un jeu de données de 150 lignes ou d’un système de production de millions de lignes. Les arbres de décision ne sont qu’une famille de modèles ; les mêmes étapes fonctionnent avec la régression logistique, les forêts aléatoires, les réseaux de neurones, et au-delà.
Vérifiez la documentation actuelle de scikit-learn
scikit-learn est stable, mais son API évolue entre les versions majeures, les valeurs de paramètres par défaut changent, et des fonctions sont dépréciées. Avant de vous fier à ce code au-delà d’un projet de cours, parcourez la documentation actuelle de scikit-learn pour la version que vous avez réellement installée (uv pip show scikit-learn).
Où aller à partir d’ici
- La validation croisée. Une seule division train/test donne un chiffre de précision qui dépend en partie de la chance.
sklearn.model_selection.cross_val_scorerépète le cycle division-entraînement-évaluation plusieurs fois et moyenne le résultat, une façon plus fiable de comparer deux modèles. - L’ingénierie de caractéristiques. Essayez de créer de nouvelles colonnes à partir des mesures existantes (par ex. le ratio longueur/largeur du pétale) et regardez si cela améliore la précision.
- Un jeu de données différent. Kaggle héberge des centaines de petits jeux de données bien documentés pour débutants, une bonne prochaine étape une fois que ce flux de travail devient routinier.
Partagez votre projet avec la classe
Vous avez construit quelque chose dont vous êtes fier ? examples/student-projects/ est une galerie de projets que d’autres étudiants ont soumis, et son README a un guide complet et accessible aux débutants pour ajouter le vôtre via une pull request, même si vous n’avez jamais utilisé git auparavant : forker le dépôt, créer une branche, valider vos fichiers, et ouvrir la PR, une étape à la fois. Aucune expérience préalable de git n’est présumée.
Bienvenue dans l’écriture de Python en dehors du navigateur. 🎓
Terminez chaque étape puis marquez ce projet comme terminé pour gagner ses points.