Ce cours vous présente Scikit-Learn, la bibliothèque Python de référence pour le machine learning, en se concentrant sur les modèles de classification.
Vous découvrirez les fondements théoriques de l’apprentissage supervisé et comment implémenter différents modèles de classification avec Scikit-Learn.
Ce que vous allez apprendre :
- Les principes fondamentaux de l’apprentissage supervisé
- La différence entre régression et classification
- Comment préparer vos données pour un problème de classification
- Les algorithmes de classification essentiels et leurs implémentations
- Comment évaluer et améliorer les performances de vos modèles
Ce que vous devez maîtriser :
- Connaissances de base en Python (variables, fonctions, boucles)
- Notions élémentaires de mathématiques (algèbre linéaire, probabilités)
- Compréhension basique des concepts de données (tableaux, matrices)
- Installation de Python et des bibliothèques scientifiques (numpy, pandas)
Comprendre les bases de l’apprentissage supervisé

Qu’est-ce que l’apprentissage supervisé ?
L’apprentissage supervisé est une approche de machine learning où l’algorithme apprend à partir de données étiquetées.
Imaginez un professeur (les données étiquetées) qui guide un élève (l’algorithme) jusqu’à ce qu’il puisse faire des prédictions par lui-même sur de nouvelles données.
Dans ce paradigme, nous fournissons à l’algorithme :
- Des exemples d’entrée (features) : caractéristiques ou attributs décrivant chaque observation
- Des sorties attendues (labels) : réponses correctes associées à chaque exemple
L’objectif est de trouver une fonction qui relie correctement les entrées aux sorties, et qui peut généraliser à de nouvelles observations jamais vues auparavant.
# Structure typique d'un problème d'apprentissage supervisé
import numpy as np
from sklearn.model_selection import train_test_split
# Données d'entrée (X) et sorties attendues (y)
X = np.array([[0, 1], [1, 0], [1, 1], [0, 0]]) # Caractéristiques
y = np.array([0, 0, 1, 0]) # Étiquettes
# Division en ensembles d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42)
print(f"Données d'entraînement: {X_train.shape[0]} exemples")
print(f"Données de test: {X_test.shape[0]} exemples")
L’apprentissage supervisé comporte plusieurs étapes clés :
- Collecte et préparation des données : recueillir des données pertinentes et les nettoyer
- Extraction des caractéristiques : identifier les variables qui permettront de prédire la sortie
- Choix du modèle : sélectionner l’algorithme approprié à votre problème
- Entraînement : ajuster les paramètres du modèle sur les données d’entraînement
- Évaluation : mesurer la performance sur des données non utilisées pour l’entraînement
- Optimisation : affiner le modèle pour améliorer ses performances
Classification vs Régression
L’apprentissage supervisé se divise en deux grandes catégories : la classification et la régression.
Classification | Régression |
---|---|
Prédit une catégorie/classe | Prédit une valeur numérique continue |
Sortie discrète (appartenance à un groupe) | Sortie continue (nombre réel) |
Exemples : spam/non-spam, bénin/malin | Exemples : prix, température, âge |
Évaluation : précision, rappel, F1-score | Évaluation : RMSE, MAE, R² |
Pour les problèmes de classification, on distingue également :
- Classification binaire : deux classes possibles (0/1, oui/non)
- Classification multi-classe : plus de deux classes mutuellement exclusives
- Classification multi-label : possibilité d’appartenir à plusieurs classes simultanément
Voici comment ces problèmes se traduisent en pratique avec Scikit-Learn :
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.datasets import load_iris, load_diabetes
# Exemple de classification (Iris)
X_cls, y_cls = load_iris(return_X_y=True)
clf = LogisticRegression(max_iter=200)
clf.fit(X_cls, y_cls)
print(f"Classes prédites : {clf.predict(X_cls[:2])}")
print(f"Probabilités : {clf.predict_proba(X_cls[:2])[:, 1]}")
# Exemple de régression (Diabetes)
X_reg, y_reg = load_diabetes(return_X_y=True)
reg = LinearRegression()
reg.fit(X_reg, y_reg)
print(f"Valeurs prédites : {reg.predict(X_reg[:2])}")
Dans ce cours, nous nous concentrerons sur la classification, mais les concepts abordés vous aideront également à comprendre la régression.
Préparation des données pour l’apprentissage
La qualité de vos modèles dépend fortement de la qualité des données d’entrée. Une bonne préparation des données est cruciale et implique plusieurs étapes :
1. Nettoyage des données
Les données du monde réel sont rarement parfaites. Vous devrez gérer :
import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer
# Exemple de données avec valeurs manquantes
data = pd.DataFrame({
'age': [25, 30, np.nan, 40],
'income': [50000, np.nan, 70000, 60000],
'education': ['Bachelor', 'Master', np.nan, 'PhD']
})
# Imputation des valeurs manquantes numériques
num_imputer = SimpleImputer(strategy='mean')
data[['age', 'income']] = num_imputer.fit_transform(data[['age', 'income']])
# Imputation des valeurs manquantes catégorielles
cat_imputer = SimpleImputer(strategy='most_frequent')
data[['education']] = cat_imputer.fit_transform(data[['education']].values)
print("Données après imputation :")
print(data)
2. Codage des variables catégorielles
Les algorithmes de machine learning travaillent avec des nombres. Les variables catégorielles doivent être transformées :
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
# Encodage pour cible catégorielle (y)
label_encoder = LabelEncoder()
education_encoded = label_encoder.fit_transform(data['education'])
print(f"Encodage des étiquettes : {dict(zip(label_encoder.classes_, range(len(label_encoder.classes_))))}")
# Encodage One-Hot pour features catégorielles (X)
encoder = OneHotEncoder(sparse_output=False)
education_one_hot = encoder.fit_transform(data[['education']])
print("Encodage One-Hot :")
print(pd.DataFrame(education_one_hot, columns=encoder.get_feature_names_out(['education'])))
3. Normalisation et standardisation
Les algorithmes sensibles à l’échelle des variables nécessitent une normalisation :
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Standardisation (moyenne=0, écart-type=1)
scaler = StandardScaler()
age_income_scaled = scaler.fit_transform(data[['age', 'income']])
print("Données standardisées :")
print(pd.DataFrame(age_income_scaled, columns=['age', 'income']))
# Normalisation (valeurs entre 0 et 1)
normalizer = MinMaxScaler()
age_income_normalized = normalizer.fit_transform(data[['age', 'income']])
print("Données normalisées :")
print(pd.DataFrame(age_income_normalized, columns=['age', 'income']))
4. Division des données
Pour évaluer honnêtement votre modèle, vous devez séparer vos données :
from sklearn.model_selection import train_test_split
X = age_income_scaled # Caractéristiques
y = education_encoded # Cible
# Division en ensembles d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42)
print(f"Ensemble d'entraînement : {X_train.shape[0]} exemples")
print(f"Ensemble de test : {X_test.shape[0]} exemples")
Scikit-Learn facilite grandement ces tâches de préparation grâce à ses nombreuses classes de prétraitement et à son API cohérente.
Premier modèle : La Régression Logistique
Principe et théorie
Malgré son nom contenant « régression », la régression logistique est un algorithme de classification. Son fonctionnement repose sur l’estimation de la probabilité qu’une observation appartienne à une classe donnée.
Le modèle de base de la régression logistique est :
- Calculer une combinaison linéaire des features : z = w₀ + w₁x₁ + w₂x₂ + … + wₙxₙ
- Appliquer la fonction sigmoïde pour transformer z en probabilité : p = 1 / (1 + e^(-z))
- Classifier en fonction d’un seuil (généralement 0.5) : classe = 1 si p ≥ 0.5, sinon classe = 0
La fonction sigmoïde est représentée graphiquement ainsi :

Pendant l’entraînement, le modèle cherche les coefficients w qui maximisent la vraisemblance des données observées, généralement en minimisant la fonction de perte d’entropie croisée.
Implémentation avec Scikit-Learn
Scikit-Learn rend l’utilisation de la régression logistique très simple :
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
# Charger les données (cancer du sein)
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
# Description du jeu de données
print(f"Nombre d'exemples : {X.shape[0]}")
print(f"Nombre de caractéristiques : {X.shape[1]}")
print(f"Classes : {np.unique(y)}")
print(f"Noms des classes : {cancer.target_names}")
print(f"Distribution des classes : {np.bincount(y)}")
# Division en ensembles d'entraînement et de test
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42)
# Création et entraînement du modèle
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train)
# Prédictions
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1] # Proba de la classe positive
# Évaluation
accuracy = accuracy_score(y_test, y_pred)
print(f"Précision : {accuracy:.3f}")
# Rapport détaillé
print("\nRapport de classification :")
print(classification_report(y_test, y_pred, target_names=cancer.target_names))
# Matrice de confusion
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=cancer.target_names,
yticklabels=cancer.target_names)
plt.xlabel('Prédit')
plt.ylabel('Réel')
plt.title('Matrice de confusion')
plt.show()
# Coefficients du modèle
coef = pd.DataFrame(
model.coef_[0],
index=cancer.feature_names,
columns=['Coefficient']
).sort_values('Coefficient', ascending=False)
plt.figure(figsize=(10, 8))
sns.barplot(x=coef.Coefficient, y=coef.index)
plt.title('Importance des caractéristiques')
plt.axvline(x=0, color='r', linestyle='--')
plt.show()


Interprétation et limites
La régression logistique offre plusieurs avantages :
- Interprétabilité : les coefficients indiquent l’importance et la direction de l’influence de chaque feature
- Probabilités : fournit naturellement des estimations de probabilité
- Efficacité : rapide à entraîner et à faire des prédictions
- Bien adapté aux petits jeux de données
Cependant, elle présente certaines limitations :
- Linéarité : suppose une relation linéaire entre les features et le logarithme des odds (rapport de probabilité afin de mesurer le risque qu’un événement arrivant à un groupe, arrive également à un autre)
- Indépendance des features : n’intègre pas les interactions entre variables par défaut
- Sensibilité aux valeurs aberrantes
- Difficulté avec les frontières de décision complexes
Pour visualiser ces limitations, voici un exemple de frontière de décision linéaire :
from sklearn.inspection import DecisionBoundaryDisplay
# Sélectionner deux caractéristiques pour la visualisation
X_2d = X[:, :2] # Utiliser seulement les deux premières caractéristiques
X_train_2d, X_test_2d, y_train, y_test = train_test_split(
X_2d, y, test_size=0.3, random_state=42)
# Modèle
model_2d = LogisticRegression()
model_2d.fit(X_train_2d, y_train)
# Afficher la frontière de décision
plt.figure(figsize=(10, 8))
disp = DecisionBoundaryDisplay.from_estimator(
model_2d, X_2d, alpha=0.8, grid_resolution=1000)
plt.scatter(X_2d[:, 0], X_2d[:, 1], c=y,
edgecolor='k', s=20, cmap=plt.cm.Paired)
plt.xlabel(cancer.feature_names[0])
plt.ylabel(cancer.feature_names[1])
plt.title('Frontière de décision de la régression logistique')
plt.colorbar(disp.contourf_)
plt.show()

Évaluation des modèles de classification
Les métriques d’évaluation
L’évaluation correcte d’un modèle de classification est essentielle pour comprendre ses performances réelles. Voici les principales métriques à connaître :
Matrice de confusion
C’est la base de nombreuses métriques. Elle organise les prédictions en quatre catégories :
Prédiction Positive | Prédiction Négative | |
---|---|---|
Réellement Positif | Vrai Positif (VP) | Faux Négatif (FN) |
Réellement Négatif | Faux Positif (FP) | Vrai Négatif (VN) |
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression # Un exemple de classifieur
from sklearn.metrics import confusion_matrix
from sklearn.datasets import make_classification # Pour générer des données d'exemple
# --- 1. Génération de données d'exemple ---
# Créons un problème de classification binaire simple
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# X: features (caractéristiques)
# y: target (étiquettes réelles, 0 ou 1 dans ce cas)
# --- 2. Division des données en ensembles d'entraînement et de test ---
# y_test contiendra les vraies étiquettes de notre ensemble de test
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# test_size=0.3 signifie que 30% des données seront utilisées pour le test
# --- 3. Entraînement d'un modèle de classification ---
# Utilisons une Régression Logistique comme exemple
model = LogisticRegression(solver='liblinear') # 'liblinear' est un bon solveur pour les petits datasets
model.fit(X_train, y_train)
# --- 4. Faire des prédictions sur l'ensemble de test ---
# y_pred contiendra les étiquettes prédites par notre modèle pour l'ensemble de test
y_pred = model.predict(X_test)
# --- 5. Calculer et visualiser la matrice de confusion ---
cm = confusion_matrix(y_test, y_pred)
# Visualiser la matrice de confusion
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['Prédit Négatif (0)', 'Prédit Positif (1)'], # Étiquettes pour l'axe X
yticklabels=['Réel Négatif (0)', 'Réel Positif (1)']) # Étiquettes pour l'axe Y
plt.xlabel('Valeurs Prédites par le Modèle')
plt.ylabel('Valeurs Réelles (Vérité Terrain)')
plt.title('Matrice de Confusion')
plt.show()

Précision, Rappel, F1-Score
- Précision = VP / (VP + FP) : proportion de prédictions positives correctes
- Rappel = VP / (VP + FN) : proportion de positifs réels correctement identifiés
- F1-Score : moyenne harmonique de la précision et du rappel
from sklearn.metrics import precision_score, recall_score, f1_score
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
print(f"Précision : {precision:.3f}")
print(f"Rappel : {recall:.3f}")
print(f"F1-Score : {f1:.3f}")
Courbe ROC et AUC
La courbe ROC (Receiver Operating Characteristic) montre la performance à différents seuils de décision. L’AUC (Area Under Curve) mesure la capacité du modèle à distinguer les classes.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns # Bien que non utilisé directement pour ROC, c'est bon de l'avoir si on fait aussi la matrice de confusion
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression # Un exemple de classifieur
from sklearn.datasets import make_classification # Pour générer des données d'exemple
from sklearn.metrics import roc_curve, roc_auc_score # Pour la courbe ROC et l'AUC
# --- 1. Génération de données d'exemple ---
# Créons un problème de classification binaire simple
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# X: features (caractéristiques)
# y: target (étiquettes réelles, 0 ou 1 dans ce cas)
# --- 2. Division des données en ensembles d'entraînement et de test ---
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# y_test contiendra les vraies étiquettes de notre ensemble de test
# --- 3. Entraînement d'un modèle de classification ---
# Utilisons une Régression Logistique comme exemple.
# Important : le modèle doit pouvoir prédire des probabilités (avoir une méthode predict_proba)
model = LogisticRegression(solver='liblinear')
model.fit(X_train, y_train)
# --- 4. Calculer les probabilités pour la courbe ROC ---
# La courbe ROC nécessite les probabilités de la classe positive.
# model.predict_proba(X_test) retourne un array de shape (n_samples, n_classes)
# On s'intéresse à la probabilité de la classe positive (généralement la 2ème colonne, index 1)
y_prob = model.predict_proba(X_test)[:, 1]
# --- 5. Calculer les points de la courbe ROC et l'AUC ---
# fpr: Taux de Faux Positifs (False Positive Rate)
# tpr: Taux de Vrais Positifs (True Positive Rate) / Rappel (Recall)
# thresholds: Seuils de décision utilisés pour calculer fpr et tpr
fpr, tpr, thresholds = roc_curve(y_test, y_prob)
auc = roc_auc_score(y_test, y_prob)
# --- 6. Tracer la courbe ROC ---
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, color='blue', lw=2, label=f'Courbe ROC (AUC = {auc:.3f})') # lw = linewidth
plt.plot([0, 1], [0, 1], color='grey', lw=2, linestyle='--', label='Classifieur Aléatoire (AUC = 0.5)')
plt.xlim([0.0, 1.0]) # Limites de l'axe X
plt.ylim([0.0, 1.05]) # Limites de l'axe Y (un peu de marge en haut)
plt.xlabel('Taux de Faux Positifs (1 - Spécificité)')
plt.ylabel('Taux de Vrais Positifs (Sensibilité / Rappel)')
plt.title('Receiver Operating Characteristic (ROC)')
plt.legend(loc="lower right") # Position de la légende
plt.grid(True) # Ajoute une grille pour une meilleure lisibilité
plt.show()
print(f"Aire sous la courbe ROC (AUC): {auc:.3f}")

Validation croisée
La validation croisée est une technique puissante pour obtenir une estimation plus fiable des performances d’un modèle. Elle permet d’utiliser efficacement toutes les données disponibles.
K-Fold Cross-Validation
La méthode la plus courante est la validation croisée k-fold, qui divise les données en k sous-ensembles et effectue k itérations d’entraînement/évaluation.
import numpy as np
from sklearn.model_selection import cross_val_score, KFold
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
# --- 1. Génération de données d'exemple ---
# Créons un problème de classification binaire simple
# Il est important d'utiliser l'ensemble des données (X, y) ici,
# car la validation croisée gère elle-même les divisions internes.
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# X: features (caractéristiques)
# y: target (étiquettes réelles, 0 ou 1 dans ce cas)
# --- 2. Configuration du modèle ---
# Vous pouvez utiliser n'importe quel modèle de classification de scikit-learn
model = LogisticRegression(solver='liblinear', random_state=42) # Ajout de random_state pour la reproductibilité du modèle
# --- 3. Configuration de la validation croisée (K-Fold) ---
# n_splits=5 signifie que les données seront divisées en 5 "plis" (folds)
# shuffle=True mélange les données avant de les diviser, ce qui est généralement une bonne pratique.
# random_state=42 assure que le mélange est le même à chaque exécution (reproductibilité).
cv = KFold(n_splits=5, shuffle=True, random_state=42)
# --- 4. Exécution de la validation croisée et calcul des scores ---
# 'scoring' peut être ajusté selon la métrique qui vous intéresse
# ('accuracy', 'roc_auc', 'f1', 'precision', 'recall', etc.)
# Ici, nous utilisons 'accuracy' (exactitude).
scores = cross_val_score(model, X, y, cv=cv, scoring='accuracy')
# --- 5. Affichage et interprétation des résultats ---
print(f"Scores d'exactitude pour chaque pli (fold) : {scores}")
print(f"Exactitude moyenne : {scores.mean():.3f}")
print(f"Écart-type de l'exactitude : {scores.std():.3f}")
print(f"Performance du modèle (exactitude) : {scores.mean():.3f} ± {scores.std():.3f}")
# (Optionnel) Visualisation simple des scores par pli
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 5))
plt.bar(range(1, len(scores) + 1), scores, tick_label=[f'Pli {i}' for i in range(1, len(scores) + 1)])
plt.ylim([min(scores) - 0.05, max(scores) + 0.05]) # Ajuster les limites pour une meilleure visualisation
plt.xlabel('Pli de Validation Croisée')
plt.ylabel('Exactitude (Accuracy)')
plt.title('Scores d\'Exactitude par Pli de Validation Croisée')
for index, value in enumerate(scores):
plt.text(index + 1, value + 0.005, f"{value:.3f}", ha='center') # Affiche la valeur sur chaque barre
plt.show()

Exactitude moyenne : 0.863
Écart-type de l’exactitude : 0.007
Performance du modèle (exactitude) : 0.863 ± 0.007
Validation croisée stratifiée
Pour les problèmes de classification avec des classes déséquilibrées, il est préférable d’utiliser une validation croisée stratifiée qui maintient la proportion des classes dans chaque fold.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
import pandas as pd # Pour une manipulation plus facile des résultats pour le graphique
# --- 1. Génération de données d'exemple ---
# Créons un problème de classification binaire.
# Pour illustrer l'utilité de StratifiedKFold, on pourrait créer des classes déséquilibrées,
# mais pour la simplicité de cet exemple, nous utiliserons des classes équilibrées par défaut.
# make_classification(weights=[0.9, 0.1]) créerait des classes déséquilibrées.
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# X: features (caractéristiques)
# y: target (étiquettes réelles, 0 ou 1)
# --- 2. Configuration du modèle ---
model = LogisticRegression(solver='liblinear', random_state=42)
# --- 3. Configuration de la validation croisée stratifiée ---
# n_splits=5 signifie 5 plis.
# shuffle=True mélange les données avant de les diviser.
# random_state=42 assure la reproductibilité.
stratified_cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# --- 4. Définition des métriques et calcul des scores ---
metrics = ['accuracy', 'precision', 'recall', 'f1']
results_summary = {} # Pour stocker les moyennes et écarts-types pour le graphique
print("Résultats de la validation croisée stratifiée (5 plis) :\n")
for metric in metrics:
# cross_val_score retourne un array de scores, un pour chaque pli
scores = cross_val_score(model, X, y, cv=stratified_cv, scoring=metric)
mean_score = scores.mean()
std_score = scores.std()
results_summary[metric.capitalize()] = {'mean': mean_score, 'std': std_score}
print(f"{metric.capitalize()} : {mean_score:.3f} ± {std_score:.3f}")
# --- 5. Visualisation des performances moyennes des métriques ---
# Préparation des données pour le graphique
metric_names = list(results_summary.keys())
mean_values = [results_summary[m]['mean'] for m in metric_names]
std_values = [results_summary[m]['std'] for m in metric_names]
plt.figure(figsize=(10, 6))
bars = plt.bar(metric_names, mean_values, yerr=std_values, capsize=5, color=['skyblue', 'lightcoral', 'lightgreen', 'gold'])
plt.ylabel('Score Moyen')
plt.title('Performance Moyenne du Modèle par Métrique (avec Validation Croisée Stratifiée)')
plt.ylim([0, 1.05]) # Les scores sont généralement entre 0 et 1
# Ajouter les valeurs exactes sur les barres pour une lecture facile
for bar in bars:
yval = bar.get_height()
plt.text(bar.get_x() + bar.get_width()/2.0, yval + 0.02, f'{yval:.3f}', ha='center', va='bottom')
plt.figtext(0.5, -0.05,
"Les barres d'erreur représentent ±1 écart-type de la moyenne des scores sur les 5 plis.\n"
"Une barre d'erreur plus petite indique une performance plus stable pour cette métrique à travers les plis.",
ha="center", fontsize=9, style='italic')
plt.tight_layout(rect=[0, 0.05, 1, 1]) # Ajuste pour éviter que le figtext ne soit coupé
plt.show()

Accuracy : 0.869 ± 0.009
Precision : 0.878 ± 0.020
Recall : 0.858 ± 0.016
F1 : 0.868 ± 0.008
Optimisation des hyperparamètres
Les hyperparamètres sont des paramètres qui ne sont pas appris pendant l’entraînement mais qui doivent être définis à l’avance. Leur optimisation est cruciale pour obtenir les meilleures performances.
Grid Search
La recherche par grille teste toutes les combinaisons d’hyperparamètres spécifiées.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import GridSearchCV, StratifiedKFold # Utiliser StratifiedKFold pour CV
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
# --- 1. Génération de données d'exemple ---
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# --- 2. Définition de la grille de paramètres ---
# 'C': Inverse du coefficient de régularisation. Des valeurs plus petites spécifient une régularisation plus forte.
# 'penalty': Type de norme utilisée dans la pénalisation (L1 ou L2).
# 'solver': Algorithme à utiliser dans le problème d'optimisation.
# 'liblinear' est bon pour les petits datasets et supporte L1 et L2.
# 'saga' est bon pour les grands datasets, supporte L1, L2 et elasticnet, et est plus rapide.
param_grid = {
'C': [0.001, 0.01, 0.1, 1, 10, 100],
'penalty': ['l1', 'l2'],
'solver': ['liblinear', 'saga'] # Assurez-vous que les solveurs supportent les pénalités listées
}
# --- 3. Configuration et exécution de la recherche par grille ---
# Utilisation de StratifiedKFold pour la validation croisée (cv) pour une meilleure robustesse,
# surtout si les classes sont déséquilibrées (même si make_classification par défaut les équilibre).
cv_strategy = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
grid_search = GridSearchCV(
estimator=LogisticRegression(max_iter=10000, random_state=42), # max_iter augmenté pour assurer la convergence des solveurs comme 'saga'
param_grid=param_grid,
cv=cv_strategy, # Stratégie de validation croisée
scoring='f1', # Métrique pour évaluer les combinaisons. F1 est un bon choix pour les problèmes de classification.
n_jobs=-1, # Utiliser tous les cœurs CPU disponibles pour accélérer la recherche
verbose=1 # Affiche des messages sur la progression
)
print("Début de la recherche par grille (GridSearchCV)...")
grid_search.fit(X, y)
print("Recherche par grille terminée.")
# --- 4. Affichage des meilleurs paramètres et score ---
print(f"\nMeilleurs paramètres trouvés : {grid_search.best_params_}")
print(f"Meilleur score F1 (moyenne sur CV) : {grid_search.best_score_:.3f}")
# --- 5. Affichage des résultats détaillés pour les meilleures combinaisons ---
results_df = pd.DataFrame(grid_search.cv_results_)
# Trier par le score de test (la métrique de 'scoring') et afficher les colonnes pertinentes
# 'rank_test_score' classe les combinaisons, 1 étant la meilleure.
top_results = results_df.sort_values(by='rank_test_score').head(10) # Afficher les 10 meilleures
print("\nTop 10 des combinaisons de paramètres (basé sur le score F1 moyen) :")
print(top_results[['params', 'mean_test_score', 'std_test_score', 'rank_test_score']])
# --- 6. Visualisation des résultats de GridSearchCV ---
# Visualisation de l'impact du paramètre 'C' sur le score F1, en distinguant par 'penalty' et 'solver'
# On va simplifier en ne visualisant que pour le meilleur 'solver' trouvé, ou en les séparant.
# Préparer les données pour la visualisation
plot_data = []
for i in range(len(results_df)):
params = results_df.loc[i, 'params']
plot_data.append({
'C': params['C'],
'penalty': params['penalty'],
'solver': params['solver'],
'mean_f1_score': results_df.loc[i, 'mean_test_score'],
'std_f1_score': results_df.loc[i, 'std_test_score']
})
plot_df = pd.DataFrame(plot_data)
# Créer un graphique pour chaque solveur
unique_solvers = plot_df['solver'].unique()
num_solvers = len(unique_solvers)
fig, axes = plt.subplots(num_solvers, 1, figsize=(12, 6 * num_solvers), sharex=True)
if num_solvers == 1: # Si un seul solveur, axes n'est pas un array
axes = [axes]
for i, solver_name in enumerate(unique_solvers):
ax = axes[i]
solver_subset = plot_df[plot_df['solver'] == solver_name]
sns.lineplot(data=solver_subset, x='C', y='mean_f1_score', hue='penalty', marker='o', ax=ax, errorbar='sd')
# 'errorbar="sd"' affiche l'écart-type (std_test_score) comme bande d'erreur
ax.set_xscale('log') # C est souvent exploré sur une échelle logarithmique
ax.set_title(f'Performance (Score F1) vs. C pour le solveur "{solver_name}"')
ax.set_ylabel('Score F1 Moyen')
ax.grid(True, which="both", ls="--")
ax.legend(title='Penalty')
axes[-1].set_xlabel('Paramètre C (Échelle Logarithmique)')
plt.tight_layout()
plt.figtext(0.5, -0.02, # Ajustez la position verticale si nécessaire
"Les bandes d'erreur représentent ±1 écart-type du score F1 moyen sur les plis de validation croisée.\n"
"Un score F1 plus élevé est meilleur. Les points indiquent les combinaisons testées.",
ha="center", fontsize=9, style='italic')
plt.subplots_adjust(bottom=0.15) # Ajuster l'espacement pour le figtext
plt.show()

Random Search
Pour les espaces de paramètres plus grands, la recherche aléatoire est souvent plus efficace.
RandomizedSearchCV
explore un nombre fixe de combinaisons d’hyperparamètres (n_iter
) en les échantillonnant à partir de distributions que vous spécifiez. C’est souvent plus efficace que GridSearchCV
lorsque l’espace des hyperparamètres est grand ou lorsque certains hyperparamètres sont continus.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import RandomizedSearchCV, StratifiedKFold
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification
from scipy.stats import loguniform # Pour échantillonner 'C' sur une échelle logarithmique
# --- 1. Génération de données d'exemple ---
X, y = make_classification(n_samples=1000, n_features=20, n_classes=2, random_state=42)
# --- 2. Définition des distributions des paramètres ---
# 'C': Échantillonné à partir d'une distribution log-uniforme.
# loguniform(1e-3, 1e3) signifie que C sera échantillonné entre 0.001 et 1000,
# avec une probabilité égale pour chaque ordre de grandeur.
# 'penalty': Choisi aléatoirement entre 'l1' et 'l2'.
# 'solver': Choisi aléatoirement entre 'liblinear' et 'saga'.
param_distributions = {
'C': loguniform(1e-3, 1e3),
'penalty': ['l1', 'l2'],
'solver': ['liblinear', 'saga'] # Assurez-vous que les solveurs supportent les pénalités
}
# --- 3. Configuration et exécution de la recherche aléatoire ---
cv_strategy = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
random_search = RandomizedSearchCV(
estimator=LogisticRegression(max_iter=10000, random_state=42), # max_iter augmenté
param_distributions=param_distributions,
n_iter=20, # Nombre de combinaisons de paramètres à échantillonner
cv=cv_strategy, # Stratégie de validation croisée
scoring='f1', # Métrique d'évaluation
n_jobs=-1, # Utiliser tous les cœurs CPU
verbose=1, # Afficher des messages sur la progression
random_state=42 # Pour la reproductibilité de l'échantillonnage
)
print("Début de la recherche aléatoire (RandomizedSearchCV)...")
random_search.fit(X, y)
print("Recherche aléatoire terminée.")
# --- 4. Affichage des meilleurs paramètres et score ---
print(f"\nMeilleurs paramètres trouvés : {random_search.best_params_}")
print(f"Meilleur score F1 (moyenne sur CV) : {random_search.best_score_:.3f}")
# --- 5. Affichage des résultats détaillés pour quelques combinaisons testées ---
results_df = pd.DataFrame(random_search.cv_results_)
# Trier par le score de test et afficher les colonnes pertinentes
# 'rank_test_score' classe les combinaisons, 1 étant la meilleure.
top_results_random = results_df.sort_values(by='rank_test_score').head(10) # Afficher les 10 meilleures
print("\nTop 10 des combinaisons de paramètres échantillonnées (basé sur le score F1 moyen) :")
print(top_results_random[['params', 'mean_test_score', 'std_test_score', 'rank_test_score']])
# --- 6. Visualisation des résultats de RandomizedSearchCV ---
# On va créer un scatter plot pour voir comment les scores F1 varient
# en fonction des valeurs de C échantillonnées, en distinguant par penalty et solver.
# Extraire les valeurs de C, penalty, solver et les scores pour le graphique
# Note: 'param_C' est le nom que scikit-learn donne à la colonne pour le paramètre 'C' dans cv_results_
# Pareil pour 'param_penalty' et 'param_solver'.
plot_df = pd.DataFrame({
'C_sampled': results_df['param_C'].astype(float), # Assurer que C est numérique pour l'échelle log
'penalty': results_df['param_penalty'],
'solver': results_df['param_solver'],
'mean_f1_score': results_df['mean_test_score'],
'std_f1_score': results_df['std_test_score']
})
plt.figure(figsize=(14, 8))
# Utiliser scatterplot de seaborn pour différencier par 'hue' (penalty) et 'style' (solver)
scatter_plot = sns.scatterplot(
data=plot_df,
x='C_sampled',
y='mean_f1_score',
hue='penalty',
style='solver',
size='mean_f1_score', # Optionnel: varier la taille du point par score
sizes=(50, 250), # Optionnel: plage de tailles
palette='viridis',
legend='full'
)
# Mettre en évidence le meilleur point
best_idx = random_search.best_index_
plt.scatter(
plot_df.loc[best_idx, 'C_sampled'],
plot_df.loc[best_idx, 'mean_f1_score'],
s=300, # Taille plus grande pour le meilleur point
facecolors='none', # Cercle vide
edgecolors='red', # Couleur de la bordure
linewidth=2,
label=f'Meilleur Point (F1={random_search.best_score_:.3f})'
)
plt.xscale('log')
plt.xlabel('Paramètre C Échantillonné (Échelle Logarithmique)')
plt.ylabel('Score F1 Moyen (sur CV)')
plt.title(f'Résultats de RandomizedSearchCV ({random_search.n_iter} itérations)')
plt.grid(True, which="both", ls="--")
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') # Placer la légende à l'extérieur
plt.tight_layout(rect=[0, 0, 0.85, 1]) # Ajuster pour la légende externe
plt.figtext(0.5, -0.03,
"Chaque point représente une combinaison d'hyperparamètres échantillonnée et son score F1 moyen.\n"
"La couleur indique la pénalité, le style du marqueur indique le solveur. Le meilleur point est cerclé en rouge.",
ha="center", fontsize=9, style='italic')
plt.subplots_adjust(bottom=0.15)
plt.show()

Pipeline scikit-learn
Scikit-Learn offre une fonctionnalité extrêmement puissante : les pipelines. Ils permettent d’enchaîner plusieurs étapes de prétraitement et de modélisation de manière cohérente.
Construction d’un pipeline complet
Voici un exemple de pipeline qui intègre toutes les étapes nécessaires :
import numpy as np # Pour np.nan
import pandas as pd
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn import set_config # Pour visualiser le pipeline
# Essayer d'importer 'display' pour les environnements IPython, sinon utiliser print.
try:
from IPython.display import display
except ImportError:
# Définir une fonction display factice qui utilise print si IPython n'est pas disponible
# Cela évite une NameError si le code est exécuté hors d'un environnement IPython.
print("IPython.display n'est pas disponible. Utilisation de print() pour l'affichage du pipeline.")
def display(obj):
print(obj)
# --- 1. Créer un jeu de données mixte avec valeurs manquantes ---
data = pd.DataFrame({
'age': [25, 30, np.nan, 40, 35],
'income': [50000, np.nan, 70000, 60000, 65000],
'gender': ['M', 'F', 'M', 'F', np.nan],
'education': ['Bachelor', 'Master', np.nan, 'PhD', 'Bachelor']
})
X = data.copy() # Nos features
# --- 2. Définir les colonnes numériques et catégorielles ---
numeric_features = ['age', 'income']
categorical_features = ['gender', 'education']
# --- 3. Prétraitement pour les colonnes numériques ---
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='mean')),
('scaler', StandardScaler())
])
# --- 4. Prétraitement pour les colonnes catégorielles ---
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])
# --- 5. Préprocesseur combinant les transformateurs ---
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
# --- 6. Pipeline final ---
pipe = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', LogisticRegression(solver='liblinear', random_state=42))
])
# --- 7. Créer des variables cible et entraîner ---
y = np.array([0, 1, 0, 1, 0])
print("Entraînement du pipeline...")
pipe.fit(X, y)
print("Pipeline entraîné.")
# --- 8. Prédire sur de nouvelles données ---
new_data = pd.DataFrame({
'age': [22, 32, np.nan],
'income': [45000, 62000, 55000],
'gender': ['F', np.nan, 'M'],
'education': ['Bachelor', 'PhD', 'Master']
})
print("\nPrédictions sur de nouvelles données:")
prediction = pipe.predict(new_data)
probability = pipe.predict_proba(new_data)
for i in range(len(new_data)):
print(f"\nDonnée {i+1}:")
print(f" Prédiction de classe : {prediction[i]}")
print(f" Probabilités par classe : {probability[i]} (Classe 0: {probability[i][0]:.3f}, Classe 1: {probability[i][1]:.3f})")
# --- 9. Visualisation de la Structure du Pipeline ---
print("\n--- Visualisation de la Structure du Pipeline ---")
set_config(display='diagram') # Active l'affichage en diagramme pour scikit-learn
# Affichage du pipeline.
# Dans Jupyter/IPython, cela rendra un diagramme HTML.
# Dans un script Python standard, cela imprimera une représentation textuelle structurée.
display(pipe) # Maintenant, 'display' est défini soit par IPython, soit par notre fonction factice.
print("\nExplication de l'affichage du pipeline:")
print("Si vous êtes dans un notebook Jupyter ou un environnement similaire, vous devriez voir un diagramme HTML interactif.")
print("Si vous exécutez ce script dans une console Python standard, vous verrez une représentation textuelle de la structure du pipeline.")
print("Les deux formats vous aident à comprendre comment les étapes sont organisées.")
Prédictions sur de nouvelles données:
Donnée 1:
Prédiction de classe : 0
Probabilités par classe : [0.68850722 0.31149278] (Classe 0: 0.689, Classe 1: 0.311)
Donnée 2:
Prédiction de classe : 1
Probabilités par classe : [0.39708338 0.60291662] (Classe 0: 0.397, Classe 1: 0.603)
Donnée 3:
Prédiction de classe : 1
Probabilités par classe : [0.47847465 0.52152535] (Classe 0: 0.478, Classe 1: 0.522)
--- Visualisation de la Structure du Pipeline ---
Pipeline(steps=[('preprocessor',
ColumnTransformer(transformers=[('num',
Pipeline(steps=[('imputer',
SimpleImputer()),
('scaler',
StandardScaler())]),
['age', 'income']),
('cat',
Pipeline(steps=[('imputer',
SimpleImputer(strategy='most_frequent')),
('onehot',
OneHotEncoder(handle_unknown='ignore',
sparse_output=False))]),
['gender', 'education'])])),
('classifier',
LogisticRegression(random_state=42, solver='liblinear'))])
Avantages des pipelines
Les pipelines offrent de nombreux avantages :
- Encapsulation du workflow : toutes les étapes sont regroupées dans un seul objet
- Prévention des fuites de données : les transformations sont appliquées correctement sans contamination entre ensembles d’entraînement et de test
- Simplification de l’optimisation : la validation croisée et la recherche d’hyperparamètres peuvent être appliquées à l’ensemble du pipeline
- Facilité de déploiement : le pipeline entier peut être sauvegardé et réutilisé
Optimisation d’un pipeline
Vous pouvez optimiser tous les composants d’un pipeline simultanément :
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, StratifiedKFold # StratifiedKFold pour une CV robuste
from sklearn import set_config # Pour visualiser le pipeline
# Essayer d'importer 'display' pour les environnements IPython, sinon utiliser print.
try:
from IPython.display import display
except ImportError:
print("IPython.display n'est pas disponible. Utilisation de print() pour l'affichage des pipelines.")
def display(obj):
print(obj)
# --- 1. Créer un jeu de données mixte avec valeurs manquantes ---
data = pd.DataFrame({
'age': [25, 30, np.nan, 40, 35, 28, np.nan, 42, 33, 38],
'income': [50000, np.nan, 70000, 60000, 65000, 52000, 75000, np.nan, 68000, 61000],
'gender': ['M', 'F', 'M', 'F', np.nan, 'M', 'F', 'M', 'F', 'M'],
'education': ['Bachelor', 'Master', np.nan, 'PhD', 'Bachelor', 'Master', 'PhD', 'Bachelor', np.nan, 'PhD']
})
X_features = data.copy() # Nos features
# Créer des variables cible factices pour l'exemple (plus d'échantillons pour CV)
y_target = np.array([0, 1, 0, 1, 0, 1, 0, 1, 0, 1])
# --- 2. Définir les colonnes numériques et catégorielles ---
numeric_features = ['age', 'income']
categorical_features = ['gender', 'education']
# --- 3. Définir les transformateurs de prétraitement ---
# Prétraitement pour les colonnes numériques
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='mean')), # Stratégie sera optimisée
('scaler', StandardScaler())
])
# Prétraitement pour les colonnes catégorielles
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')), # Stratégie sera optimisée
('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])
# Préprocesseur combinant les deux transformateurs
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
# --- 4. Pipeline final avec prétraitement et modèle ---
# Le solveur 'liblinear' supporte les pénalités 'l1' et 'l2'
# Les paramètres C et penalty seront optimisés.
pipe = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', LogisticRegression(solver='liblinear', max_iter=1000, random_state=42))
])
# --- 5. Définir la grille de paramètres à optimiser pour GridSearchCV ---
# La notation 'nom_etape__nom_sous_etape__parametre' est utilisée pour accéder aux paramètres imbriqués.
param_grid = {
'preprocessor__num__imputer__strategy': ['mean', 'median'],
'preprocessor__cat__imputer__strategy': ['most_frequent', 'constant'], # 'constant' nécessite fill_value, mais SimpleImputer le gère
'preprocessor__cat__imputer__fill_value': ['Unknown'], # Ajouté pour 'constant' strategy
'classifier__C': [0.1, 1, 10],
'classifier__penalty': ['l1', 'l2']
}
# --- 6. Configuration et exécution de la recherche par grille ---
# Utilisation de StratifiedKFold pour la validation croisée
cv_strategy = StratifiedKFold(n_splits=3, shuffle=True, random_state=42) # n_splits réduit à 3 pour l'exemple rapide
grid_search_pipeline = GridSearchCV(
pipe,
param_grid,
cv=cv_strategy,
scoring='f1', # Métrique d'évaluation (ex: 'accuracy', 'f1', 'roc_auc')
n_jobs=-1, # Utiliser tous les cœurs CPU disponibles
verbose=1 # Afficher des messages sur la progression
)
print("Début de l'optimisation du pipeline avec GridSearchCV...")
grid_search_pipeline.fit(X_features, y_target)
print("Optimisation terminée.")
# --- 7. Affichage des meilleurs paramètres et du meilleur score ---
print(f"\nMeilleurs paramètres trouvés pour le pipeline : {grid_search_pipeline.best_params_}")
print(f"Meilleur score F1 (moyenne sur CV) : {grid_search_pipeline.best_score_:.3f}")
# --- 8. Affichage de la structure du meilleur pipeline trouvé ---
print("\n--- Structure du Meilleur Pipeline Estimé ---")
set_config(display='diagram')
best_pipeline_estimator = grid_search_pipeline.best_estimator_
display(best_pipeline_estimator)
# --- 9. Visualisation des résultats de GridSearchCV pour le pipeline ---
results_df = pd.DataFrame(grid_search_pipeline.cv_results_)
# Préparer les données pour la visualisation : afficher les N meilleures combinaisons
N_top_combinations = 15 # Nombre de meilleures combinaisons à afficher
top_n_results = results_df.sort_values(by='rank_test_score').head(N_top_combinations)
# Créer des étiquettes lisibles pour les paramètres
def params_to_string(params_dict):
# Raccourcir les clés pour la lisibilité
short_params = {
'num_im_strat': params_dict.get('preprocessor__num__imputer__strategy', 'N/A'),
'cat_im_strat': params_dict.get('preprocessor__cat__imputer__strategy', 'N/A'),
# 'cat_fill': params_dict.get('preprocessor__cat__imputer__fill_value', 'N/A'), # Peut rendre l'étiquette trop longue
'clf_C': params_dict.get('classifier__C', 'N/A'),
'clf_pen': params_dict.get('classifier__penalty', 'N/A')
}
return '\n'.join([f"{k.split('_')[-1] if '_' in k else k}:{v}" for k, v in short_params.items()])
top_n_results['param_str'] = top_n_results['params'].apply(params_to_string)
plt.figure(figsize=(12, 8)) # Ajuster la taille pour la lisibilité
bar_plot = sns.barplot(
x='mean_test_score',
y='param_str',
data=top_n_results,
orient='h', # Barres horizontales pour mieux lire les étiquettes des paramètres
palette='viridis',
hue='param_str', # Pour que chaque barre ait une couleur distincte si palette le permet
dodge=False, # Nécessaire si hue est utilisé avec y
legend=False # La légende n'est pas utile ici car les étiquettes sont sur l'axe y
)
# Ajouter les barres d'erreur (std_test_score)
# Pour barplot horizontal, xerr est utilisé. Les positions y sont les indices.
y_coords = np.arange(len(top_n_results))
plt.errorbar(
x=top_n_results['mean_test_score'],
y=y_coords,
xerr=top_n_results['std_test_score'],
fmt='none', # Pas de marqueur de point
capsize=5,
color='black'
)
plt.xlabel('Score F1 Moyen (sur CV)')
plt.ylabel('Combinaisons d\'Hyperparamètres')
plt.title(f'Top {N_top_combinations} des Performances des Combinaisons d\'Hyperparamètres du Pipeline')
plt.xlim([min(0, top_n_results['mean_test_score'].min() - 0.1), max(1, top_n_results['mean_test_score'].max() + 0.1)]) # Ajuster les limites x
plt.tight_layout()
plt.figtext(0.5, -0.03,
"Chaque barre représente une combinaison d'hyperparamètres et son score F1 moyen.\n"
"Les barres d'erreur noires indiquent ±1 écart-type du score sur les plis de validation croisée.",
ha="center", fontsize=9, style='italic')
plt.subplots_adjust(bottom=0.1) # Ajuster pour le figtext
plt.show()

Vers d’autres modèles de classification
Scikit-Learn propose de nombreux autres algorithmes de classification, chacun avec ses forces et faiblesses. Voici un aperçu des plus importants :
Algorithme | Forces | Faiblesses | Utilisations typiques |
---|---|---|---|
K plus proches voisins (KNN) | Simple, intuitif, non-paramétrique | Lent sur grands jeux de données, sensible à la dimensionnalité | Recommandation, classification d’images simples |
Machines à vecteurs de support (SVM) | Efficace en haute dimension, versatile (différents noyaux) | Lent à entraîner sur grands jeux, difficile à interpréter | Reconnaissance d’images, classification de textes |
Arbres de décision | Très interprétable, gère données mixtes, non-linéaire | Tendance au surapprentissage, instable | Systèmes de décision, scoring crédit |
Forêts aléatoires | Robuste, haute performance, peu de paramètres | Moins interprétable, plus lourd | Applications générales, biologie, finance |
Gradient Boosting | Souvent le plus performant | Sensible aux hyperparamètres, peut surapprendre | Compétitions ML, prédictions précises |
Naïve Bayes | Rapide, efficace avec peu de données, probabiliste | Hypothèse d’indépendance souvent irréaliste | Classification de textes, filtrage spam |
Pour vous aider à choisir, voici une comparaison rapide sur un jeu de données :
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import time
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
# Import des classifieurs
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler # Pour améliorer les performances de certains modèles
# --- 1. Génération de données d'exemple ---
# X: features, y: target
# Augmentation du nombre d'échantillons pour mieux voir les différences de temps
X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=42)
# --- 2. Division des données en ensembles d'entraînement et de test ---
# Et mise à l'échelle des features, ce qui est souvent bénéfique
X_train_orig, X_test_orig, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
# Mise à l'échelle des données
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train_orig)
X_test = scaler.transform(X_test_orig)
# --- 3. Liste des classifieurs à comparer ---
# Ajout de random_state pour la reproductibilité là où c'est pertinent
classifiers = [
("Régression Logistique", LogisticRegression(solver='liblinear', random_state=42)),
("K plus proches voisins", KNeighborsClassifier()), # n_neighbors=5 par défaut
("SVM", SVC(probability=True, random_state=42)), # probability=True peut être lent, nécessaire pour predict_proba
("Arbre de décision", DecisionTreeClassifier(random_state=42)),
("Forêt aléatoire", RandomForestClassifier(random_state=42)),
("Gradient Boosting", GradientBoostingClassifier(random_state=42)),
("Naïve Bayes (Gaussian)", GaussianNB())
]
# --- 4. Évaluation des modèles ---
results_list = [] # Renommé pour éviter conflit avec variable 'results' plus loin
print("Début de l'évaluation des modèles...\n")
for name, clf in classifiers:
print(f"Évaluation du modèle : {name}")
# Temps d'entraînement
start_time_train = time.time()
clf.fit(X_train, y_train)
train_time = time.time() - start_time_train
# Temps de prédiction
start_time_predict = time.time()
y_pred = clf.predict(X_test)
predict_time = time.time() - start_time_predict
# Calcul de la précision
accuracy = accuracy_score(y_test, y_pred)
results_list.append({
"Modèle": name,
"Précision": accuracy,
"Temps d'entraînement (s)": train_time,
"Temps de prédiction (s)": predict_time
})
print(f" Précision : {accuracy:.4f}")
print(f" Temps d'entraînement : {train_time:.4f} s")
print(f" Temps de prédiction : {predict_time:.4f} s\n")
# --- 5. Afficher les résultats sous forme de DataFrame ---
results_df = pd.DataFrame(results_list).sort_values("Précision", ascending=False)
print("\n--- Tableau Récapitulatif des Performances ---")
print(results_df)
# --- 6. Visualisation des résultats ---
# Graphique 1: Précision des modèles
plt.figure(figsize=(12, 8))
sns.barplot(x="Précision", y="Modèle", data=results_df, palette="viridis", hue="Modèle", dodge=False, legend=False)
plt.title("Comparaison de la Précision des Modèles de Classification", fontsize=16)
plt.xlabel("Précision (Accuracy)", fontsize=14)
plt.ylabel("Modèle", fontsize=14)
plt.xlim(results_df["Précision"].min() * 0.9, results_df["Précision"].max() * 1.05) # Ajuster les limites pour la lisibilité
for index, row in results_df.iterrows():
plt.text(row["Précision"] + 0.005, index, f"{row['Précision']:.3f}", color='black', ha="left", va="center")
plt.tight_layout()
plt.show()
# Graphique 2: Temps d'entraînement et de prédiction
# Préparation des données pour le graphique des temps
times_df = results_df.melt(id_vars="Modèle",
value_vars=["Temps d'entraînement (s)", "Temps de prédiction (s)"],
var_name="Type de Temps",
value_name="Temps (s)")
plt.figure(figsize=(14, 9))
sns.barplot(x="Modèle", y="Temps (s)", hue="Type de Temps", data=times_df, palette="muted")
plt.title("Comparaison des Temps d'Entraînement et de Prédiction", fontsize=16)
plt.xlabel("Modèle", fontsize=14)
plt.ylabel("Temps (secondes)", fontsize=14)
plt.xticks(rotation=45, ha="right")
plt.legend(title="Type de Temps")
# Optionnel: échelle logarithmique si les temps varient beaucoup
# plt.yscale('log')
# plt.ylabel("Temps (secondes) - Échelle Logarithmique", fontsize=14)
plt.tight_layout()
plt.show()
print("\n--- Fin de la comparaison ---")


Conclusion et prochaines étapes pour devenir un expert
Vous avez maintenant une solide introduction aux modèles de classification avec Scikit-Learn. Pour approfondir vos connaissances, voici quelques pistes :
- Expérimentez avec différents jeux de données et algorithmes
- Explorez l’ingénierie des caractéristiques (feature engineering)
- Approfondissez votre compréhension des algorithmes spécifiques
- Participez à des compétitions Kaggle pour appliquer vos connaissances
- Étudiez les méthodes de traitement des données déséquilibrées
N’oubliez pas que le choix d’un modèle dépend toujours du contexte spécifique de votre problème : il n’existe pas de solution universelle en machine learning.
Scikit-Learn est une bibliothèque extrêmement riche – ce cours n’a fait qu’effleurer la surface de ses capacités. Continuez à explorer sa documentation détaillée et ses nombreux exemples pour devenir un expert en classification.
Leave a Comment