Formation ML / Apprentissage supervisé - Classification

Régression logistique

Débutant 35 min 14 sections

La sigmoïde transforme un score en probabilité ; la matrice de confusion dit ce que coûte le seuil.

Objectifs d'apprentissage

  • Comprendre la différence entre régression et classification
  • Implémenter une régression logistique avec scikit-learn
  • Interpréter une matrice de confusion
  • Évaluer avec accuracy, precision et recall

Prérequis

Module Régression linéaire recommandé

Le code de cette page s'exécute dans votre navigateur. Le premier lancement télécharge Python et ses bibliothèques.

Théorie

Classification vs Régression

Contrairement à la régression qui prédit une valeur continue (ex: prix), la classification prédit une catégorie (ex: spam/pas spam, malade/sain).

Régression logistique:

Malgré son nom, c'est un algorithme de CLASSIFICATION. Il utilise la fonction sigmoïde pour convertir une sortie linéaire en probabilité.

Fonction sigmoïde:

$$P(y=1) = \frac{1}{1 + e^{-z}}$$

Ou $z = a \cdot x + b$ (comme la régression linéaire)

Le résultat est toujours entre 0 et 1 (une probabilité). Si $P > 0.5$, on prédit la classe 1, sinon la classe 0.

Théorie

Schéma: Classification et fonction sigmoïde

Régression vs Classification:

RÉGRESSIONCLASSIFICATION
Sortie: valeur continueSortie: catégorie
Prix: 250 000 EURSpam / Pas spam
Température: 23.5 CMalade / Sain
Âge: 35 ansFraude / Légitime
Régression LinéaireRégression Logistique
y = ax + bP(classe=1) = sigmoid(ax+b)

La fonction sigmoïde - Convertir un score en probabilité:

La sigmoïde compresse toute valeur entre 0 et 1:

  • Si z < 0 → P < 0.5 → Classe 0
  • Si z > 0 → P > 0.5 → Classe 1

Le seuil de décision est à P = 0.5 (ou z = 0).

Frontière de décision:

La régression logistique trace une ligne (ou hyperplan) qui sépare les deux classes dans l'espace des features.

Pipeline de prédiction:

flowchart LR F["Features
(x1, x2)"] S["Score
z = ax + b"] Sig["Sigmoïde
P = 1/(1+e^-z)"] D{"P > 0.5?"} C1["Classe 1"] C0["Classe 0"] F --> S --> Sig --> D D -->|Oui| C1 D -->|Non| C0 class F ml-node-secondary class C1 ml-node-accent class C0 ml-node-secondary

Exemple concret - Détection de spam:

Imaginons un classificateur de spam avec 2 features:

  • $\textcolor{#3498db}{x_1}$ = nombre de mots en majuscules (ex: $\textcolor{#3498db}{15}$)
  • $\textcolor{#e67e22}{x_2}$ = nombre de liens dans l'email (ex: $\textcolor{#e67e22}{8}$)

Le modèle a appris: $\textcolor{#3498db}{a_1 = 0.1}$, $\textcolor{#e67e22}{a_2 = 0.2}$, $\textcolor{#9B7AC4}{b = -2.5}$

Étape 1 - Calcul du score z:

$$z = \textcolor{#3498db}{0.1 \times 15} + \textcolor{#e67e22}{0.2 \times 8} + \textcolor{#9B7AC4}{(-2.5)} = \textcolor{#3498db}{1.5} + \textcolor{#e67e22}{1.6} \textcolor{#9B7AC4}{- 2.5} = \textcolor{#27ae60}{\mathbf{0.6}}$$

Étape 2 - Conversion en probabilité P:

$$P = \frac{1}{1 + e^{-\textcolor{#27ae60}{0.6}}} = \frac{1}{1.55} = \textcolor{#e74c3c}{\mathbf{0.65}}$$

Étape 3 - Décision:

$\textcolor{#e74c3c}{0.65}$ > 0.5 → Classe 1 (SPAM)

Légende des couleurs:

  • $\textcolor{#3498db}{Bleu}$ : contribution des majuscules ($\textcolor{#3498db}{+1.5}$)
  • $\textcolor{#e67e22}{Orange}$ : contribution des liens ($\textcolor{#e67e22}{+1.6}$)
  • $\textcolor{#9B7AC4}{Violet}$ : biais du modèle ($\textcolor{#9B7AC4}{-2.5}$)
  • $\textcolor{#27ae60}{Vert}$ : score final z ($\textcolor{#27ae60}{0.6}$)
  • $\textcolor{#e74c3c}{Rouge}$ : probabilité P ($\textcolor{#e74c3c}{65\%}$)
Avancé Exercice manuel: À vous de calculer!

Objectif: Maîtriser les calculs de la régression logistique à la main (sigmoïde, probabilité, décision).

Prenez une feuille et un stylo. Résolvez chaque partie AVANT de regarder la solution !

CONTEXTE

Vous travaillez sur un détecteur de spam. Le modèle a appris les paramètres suivants :

  • Coefficient pour les mots en majuscules : $a_1 = 0.15$
  • Coefficient pour les liens dans l'email : $a_2 = 0.25$
  • Biais (intercept) : $b = -3.0$

Équation du modèle :

$$z = a_1 \cdot x_1 + a_2 \cdot x_2 + b$$

$$P(\text{spam}) = \frac{1}{1 + e^{-z}}$$

Voici 4 emails à classifier :

EmailMots majuscules (x1)Liens (x2)
A108
B54
C2012
D22

PARTIE 1 : Calcul des scores z

Pour chaque email, calculez le score linéaire z.

1.1) Calculez $z_A$ pour l'email A 1.2) Calculez $z_B$ pour l'email B 1.3) Calculez $z_C$ pour l'email C 1.4) Calculez $z_D$ pour l'email D

PARTIE 2 : Conversion en probabilités

Utilisez la fonction sigmoïde pour convertir z en probabilité P(spam).

Rappel des valeurs utiles :

  • $e^0 = 1$
  • $e^{-1} \approx 0.368$
  • $e^{-2} \approx 0.135$
  • $e^1 \approx 2.718$
  • $e^2 \approx 7.389$

2.1) Calculez $P_A$ = P(spam) pour l'email A 2.2) Calculez $P_B$ = P(spam) pour l'email B 2.3) Calculez $P_C$ = P(spam) pour l'email C 2.4) Calculez $P_D$ = P(spam) pour l'email D

PARTIE 3 : Décisions de classification

Seuil de décision : si $P \geq 0.5$, l'email est classé SPAM (classe 1), sinon HAM (classe 0).

3.1) Quelle est la décision pour chaque email ? 3.2) Quels emails sont classés SPAM ? 3.3) Quels emails sont classés HAM (non-spam) ?

PARTIE 4 : Métriques de performance

Les vraies étiquettes sont :

EmailVraie classe
ASPAM (1)
BHAM (0)
CSPAM (1)
DHAM (0)

4.1) Construisez la matrice de confusion (TP, TN, FP, FN) 4.2) Calculez l'Accuracy 4.3) Calculez la Précision (parmi les prédictions SPAM, combien sont correctes ?) 4.4) Calculez le Recall (parmi les vrais SPAM, combien sont détectés ?)

PARTIE 5 : Interprétation

5.1) Quel coefficient a le plus d'impact sur la classification ? Pourquoi ? 5.2) Si un email a beaucoup de liens mais peu de majuscules, sera-t-il plus ou moins susceptible d'être spam ? 5.3) Pour quel score z a-t-on exactement P = 50% ?

Avancé Solution de l'exercice manuel

SOLUTION DÉTAILLÉE

Prenez le temps de comparer avec vos réponses. Vérifiez chaque étape !

RAPPEL DU MODÈLE

$$z = \textcolor{#3498db}{0.15} \cdot x_1 + \textcolor{#e67e22}{0.25} \cdot x_2 + \textcolor{#9B7AC4}{(-3.0)}$$

$$P(\text{spam}) = \frac{1}{1 + e^{-z}}$$

PARTIE 1 : Calcul des scores z

1.1) Email A ($x_1 = 10$, $x_2 = 8$) :

$$z_A = \textcolor{#3498db}{0.15 \times 10} + \textcolor{#e67e22}{0.25 \times 8} + \textcolor{#9B7AC4}{(-3.0)}$$

$$z_A = \textcolor{#3498db}{1.5} + \textcolor{#e67e22}{2.0} \textcolor{#9B7AC4}{- 3.0} = \textcolor{#27ae60}{\mathbf{0.5}}$$

1.2) Email B ($x_1 = 5$, $x_2 = 4$) :

$$z_B = \textcolor{#3498db}{0.15 \times 5} + \textcolor{#e67e22}{0.25 \times 4} + \textcolor{#9B7AC4}{(-3.0)}$$

$$z_B = \textcolor{#3498db}{0.75} + \textcolor{#e67e22}{1.0} \textcolor{#9B7AC4}{- 3.0} = \textcolor{#27ae60}{\mathbf{-1.25}}$$

1.3) Email C ($x_1 = 20$, $x_2 = 12$) :

$$z_C = \textcolor{#3498db}{0.15 \times 20} + \textcolor{#e67e22}{0.25 \times 12} + \textcolor{#9B7AC4}{(-3.0)}$$

$$z_C = \textcolor{#3498db}{3.0} + \textcolor{#e67e22}{3.0} \textcolor{#9B7AC4}{- 3.0} = \textcolor{#27ae60}{\mathbf{3.0}}$$

1.4) Email D ($x_1 = 2$, $x_2 = 2$) :

$$z_D = \textcolor{#3498db}{0.15 \times 2} + \textcolor{#e67e22}{0.25 \times 2} + \textcolor{#9B7AC4}{(-3.0)}$$

$$z_D = \textcolor{#3498db}{0.3} + \textcolor{#e67e22}{0.5} \textcolor{#9B7AC4}{- 3.0} = \textcolor{#27ae60}{\mathbf{-2.2}}$$

$\boxed{z_A = 0.5 \quad z_B = -1.25 \quad z_C = 3.0 \quad z_D = -2.2}$

PARTIE 2 : Conversion en probabilités

2.1) Email A ($z_A = 0.5$) :

$$P_A = \frac{1}{1 + e^{-0.5}} = \frac{1}{1 + 0.607} = \frac{1}{1.607} = \textcolor{#F7E64D}{\mathbf{0.622}}$$

2.2) Email B ($z_B = -1.25$) :

$$P_B = \frac{1}{1 + e^{1.25}} = \frac{1}{1 + 3.49} = \frac{1}{4.49} = \textcolor{#F7E64D}{\mathbf{0.223}}$$

2.3) Email C ($z_C = 3.0$) :

$$P_C = \frac{1}{1 + e^{-3}} = \frac{1}{1 + 0.05} = \frac{1}{1.05} = \textcolor{#F7E64D}{\mathbf{0.953}}$$

2.4) Email D ($z_D = -2.2$) :

$$P_D = \frac{1}{1 + e^{2.2}} = \frac{1}{1 + 9.03} = \frac{1}{10.03} = \textcolor{#F7E64D}{\mathbf{0.100}}$$

$\boxed{P_A = 62.2\% \quad P_B = 22.3\% \quad P_C = 95.3\% \quad P_D = 10.0\%}$

PARTIE 3 : Décisions de classification

3.1) Décisions (seuil = 0.5) :

  • Email A : $P_A = 62.2\% \geq 50\%$ → $\textcolor{#e74c3c}{\text{SPAM}}$
  • Email B : $P_B = 22.3\% < 50\%$ → $\textcolor{#27ae60}{\text{HAM}}$
  • Email C : $P_C = 95.3\% \geq 50\%$ → $\textcolor{#e74c3c}{\text{SPAM}}$
  • Email D : $P_D = 10.0\% < 50\%$ → $\textcolor{#27ae60}{\text{HAM}}$

3.2) Emails classes SPAM : A et C

3.3) Emails classes HAM : B et D

PARTIE 4 : Métriques de performance

4.1) Matrice de confusion :

Comparons prédictions vs réalité :

  • Email A : Prédit SPAM, Réel SPAM → $\textcolor{#27ae60}{TP}$ (True Positive)
  • Email B : Prédit HAM, Réel HAM → $\textcolor{#27ae60}{TN}$ (True Négative)
  • Email C : Prédit SPAM, Réel SPAM → $\textcolor{#27ae60}{TP}$ (True Positive)
  • Email D : Prédit HAM, Réel HAM → $\textcolor{#27ae60}{TN}$ (True Négative)

$\boxed{TP = 2 \quad TN = 2 \quad FP = 0 \quad FN = 0}$

4.2) Accuracy :

$$\text{Accuracy} = \frac{TP + TN}{TP + TN + FP + FN} = \frac{2 + 2}{2 + 2 + 0 + 0} = \frac{4}{4}$$

$\boxed{\text{Accuracy} = \textcolor{#27ae60}{100\%}}$

4.3) Précision :

$$\text{Précision} = \frac{TP}{TP + FP} = \frac{2}{2 + 0} = \frac{2}{2}$$

$\boxed{\text{Précision} = \textcolor{#27ae60}{100\%}}$

4.4) Recall :

$$\text{Recall} = \frac{TP}{TP + FN} = \frac{2}{2 + 0} = \frac{2}{2}$$

$\boxed{\text{Recall} = \textcolor{#27ae60}{100\%}}$

PARTIE 5 : Interprétation

5.1) Coefficient le plus impactant :

  • $a_1 = 0.15$ (majuscules)
  • $a_2 = 0.25$ (liens)

$\textcolor{#e67e22}{a_2}$ a le plus d'impact car $0.25 > 0.15$. Chaque lien supplémentaire augmente davantage le score z que chaque mot en majuscule.

5.2) Beaucoup de liens, peu de majuscules :

L'email sera plus susceptible d'être spam car $a_2 = 0.25$ a un impact plus fort. Par exemple, 0 majuscules + 12 liens donne :

$$z = 0 + 0.25 \times 12 - 3.0 = 0$$

$$P = 50\%$$

Alors que 12 majuscules + 0 liens donne :

$$z = 0.15 \times 12 + 0 - 3.0 = -1.2$$

$$P \approx 23\%$$

5.3) Score z pour P = 50% :

$$P = 0.5 = \frac{1}{1 + e^{-z}}$$

$$1 + e^{-z} = 2$$

$$e^{-z} = 1$$

$$-z = 0$$

$\boxed{z = 0}$

Quand $z = 0$, on a exactement $P = 50\%$ (point d'indécision).

RÉSUMÉ DES RÉSULTATS

EmailzP(spam)DécisionRéelRésultat
A+0.562.2%SPAMSPAMCorrect (TP)
B-1.2522.3%HAMHAMCorrect (TN)
C+3.095.3%SPAMSPAMCorrect (TP)
D-2.210.0%HAMHAMCorrect (TN)

Légende des couleurs :

  • $\textcolor{#3498db}{Bleu}$ : contribution des majuscules ($a_1 \cdot x_1$)
  • $\textcolor{#e67e22}{Orange}$ : contribution des liens ($a_2 \cdot x_2$)
  • $\textcolor{#9B7AC4}{Violet}$ : biais du modèle ($b = -3.0$)
  • $\textcolor{#27ae60}{Vert}$ : score z et résultats corrects
  • $\textcolor{#F7E64D}{Jaune}$ : probabilités calculées
  • $\textcolor{#e74c3c}{Rouge}$ : classification SPAM
Code

Explorer les données

# =============================================================================
# ETAPE 1 : EXPLORATION DU DATASET DE CLASSIFICATION
# =============================================================================
# En classification, l'exploration des donnees est cruciale pour comprendre :
# 1) La distribution des classes (equilibre ou desequilibre ?)
# 2) La separabilite des classes (les features permettent-elles de distinguer ?)
# 3) Les caracteristiques de chaque classe

print("=" * 70)
print("EXPLORATION DU DATASET DE CLASSIFICATION BINAIRE")
print("=" * 70)
print()

# --- 1.1 Apercu des donnees ---
print("1. APERCU DES DONNEES")
print("-" * 40)
print("Chaque ligne represente un echantillon avec 2 caracteristiques (features)")
print("et une etiquette (label) indiquant sa classe (0 ou 1).")
print()
display(df.head(10), title="Apercu du dataset de classification")

# --- 1.2 Dimensions ---
n_samples, n_cols = df.shape
print()
print("2. DIMENSIONS DU DATASET")
print("-" * 40)
print(f"   Nombre d'echantillons : {n_samples}")
print(f"   Nombre de colonnes    : {n_cols} (2 features + 1 label)")
print()

# --- 1.3 Distribution des classes ---
print("3. DISTRIBUTION DES CLASSES")
print("-" * 40)
class_counts = df['label'].value_counts().sort_index()
total = len(df)

print("   Repartition des echantillons par classe :")
print()
for classe, count in class_counts.items():
    pct = count / total * 100
    bar = "█" * int(pct / 2)
    print(f"   Classe {classe} : {count:4d} echantillons ({pct:5.1f}%) {bar}")

print()

# Analyse de l'equilibre
ratio = class_counts.min() / class_counts.max()
if ratio > 0.8:
    equilibre = "EQUILIBRE"
    emoji = "✓"
    conseil = "Excellent ! Les metriques standard (accuracy) seront fiables."
elif ratio > 0.5:
    equilibre = "LEGEREMENT DESEQUILIBRE"
    emoji = "~"
    conseil = "Acceptable, mais surveillez precision et recall par classe."
else:
    equilibre = "DESEQUILIBRE"
    emoji = "⚠"
    conseil = "Attention ! L'accuracy peut etre trompeuse. Utilisez F1-score."

print(f"   DIAGNOSTIC : {equilibre} {emoji}")
print(f"   Ratio minoritaire/majoritaire : {ratio:.2f}")
print(f"   → {conseil}")
print()

# --- 1.4 Statistiques descriptives ---
print("4. STATISTIQUES DESCRIPTIVES")
print("-" * 40)
print("Voici les statistiques cles de chaque colonne :")
print()
display(df.describe().round(3), title="Statistiques descriptives")

# --- 1.5 Analyse de separabilite ---
print()
print("5. ANALYSE DE SEPARABILITE")
print("-" * 40)
print("   La separabilite mesure si les classes sont distinguables par les features.")
print()

for feature in ['feature1', 'feature2']:
    mean_0 = df[df['label'] == 0][feature].mean()
    mean_1 = df[df['label'] == 1][feature].mean()
    std_0 = df[df['label'] == 0][feature].std()
    std_1 = df[df['label'] == 1][feature].std()

    # Calcul d'une metrique de separabilite simplifiee
    pooled_std = np.sqrt((std_0**2 + std_1**2) / 2)
    if pooled_std > 0:
        separabilite = abs(mean_1 - mean_0) / pooled_std
    else:
        separabilite = 0

    print(f"   {feature}:")
    print(f"      Classe 0 : moyenne = {mean_0:.3f}, std = {std_0:.3f}")
    print(f"      Classe 1 : moyenne = {mean_1:.3f}, std = {std_1:.3f}")
    print(f"      Difference des moyennes : {abs(mean_1 - mean_0):.3f}")

    if separabilite > 1.5:
        verdict = "BONNE separabilite"
    elif separabilite > 0.8:
        verdict = "Separabilite MODEREE"
    else:
        verdict = "Separabilite FAIBLE"

    print(f"      → {verdict} (score: {separabilite:.2f})")
    print()

# --- 1.6 Conclusion ---
print("=" * 70)
print("CONCLUSION DE L'EXPLORATION")
print("=" * 70)
print()
print("   Ce que nous avons appris :")
print("   → Les deux classes sont-elles equilibrees ?")
print("   → Les features permettent-elles de distinguer les classes ?")
print("   → Y a-t-il des patterns visibles dans les statistiques ?")
print()
print("   Prochaine etape : Visualiser les donnees pour confirmer ces observations.")
Ctrl+Entrée

Cliquez sur « Exécuter » pour voir le résultat.

Code

Visualiser les classes

# =============================================================================
# ETAPE 2 : VISUALISATION DES CLASSES
# =============================================================================
# La visualisation est essentielle en classification pour :
# 1) Voir si les classes sont naturellement separables
# 2) Identifier la forme de la frontiere de decision necessaire
# 3) Detecter des outliers ou des chevauchements problematiques

print("=" * 70)
print("VISUALISATION DES DEUX CLASSES")
print("=" * 70)
print()
print("Question cle : Les deux classes sont-elles visuellement separables ?")
print("→ Si oui, la regression logistique (frontiere lineaire) conviendra.")
print("→ Si les classes sont melangees, un modele plus complexe sera necessaire.")
print()

# Separation des classes
class_0 = df[df['label'] == 0]
class_1 = df[df['label'] == 1]

# Creation du graphique
plt.figure(figsize=(10, 7))

plt.scatter(class_0['feature1'], class_0['feature2'],
            alpha=0.6, color='#9B7AC4', label='Classe 0', s=60,
            edgecolors='white', linewidth=0.5)
plt.scatter(class_1['feature1'], class_1['feature2'],
            alpha=0.6, color='#F7E64D', label='Classe 1', s=60,
            edgecolors='white', linewidth=0.5)

plt.xlabel('Feature 1', fontsize=12)
plt.ylabel('Feature 2', fontsize=12)
plt.title('Distribution des deux classes dans l\'espace des features', fontsize=14)
plt.legend(loc='best', fontsize=11)
plt.grid(True, alpha=0.3)

# Ajouter les centroïdes
c0_center = (class_0['feature1'].mean(), class_0['feature2'].mean())
c1_center = (class_1['feature1'].mean(), class_1['feature2'].mean())
plt.plot(*c0_center, 'o', color='#9B7AC4', markersize=15, markeredgecolor='black', markeredgewidth=2)
plt.plot(*c1_center, 'o', color='#F7E64D', markersize=15, markeredgecolor='black', markeredgewidth=2)

plt.tight_layout()
plt.show()

# --- Analyse visuelle ---
print()
print("ANALYSE VISUELLE")
print("-" * 40)
print()
print("   LEGENDE :")
print("   → Points violets : Classe 0")
print("   → Points jaunes  : Classe 1")
print("   → Grands cercles : Centres (moyennes) de chaque classe")
print()

# Calcul de la distance entre centroïdes
distance = np.sqrt((c1_center[0] - c0_center[0])**2 + (c1_center[1] - c0_center[1])**2)
print(f"   DISTANCE ENTRE LES CENTRES : {distance:.2f}")
print()

if distance > 2:
    print("   OBSERVATION : Les classes sont BIEN SEPAREES")
    print("   → Une frontiere lineaire devrait bien fonctionner.")
    print("   → La regression logistique est appropriee.")
elif distance > 1:
    print("   OBSERVATION : Les classes ont un CHEVAUCHEMENT PARTIEL")
    print("   → Quelques erreurs de classification sont attendues.")
    print("   → La regression logistique reste un bon choix de base.")
else:
    print("   OBSERVATION : Les classes sont FORTEMENT MELANGEES")
    print("   → La classification sera difficile.")
    print("   → Envisagez des modeles non-lineaires (SVM, arbres).")

print()
print("   QUESTION A SE POSER :")
print("   'Puis-je tracer une droite qui separe globalement les deux couleurs ?'")
print("   → Si oui, la regression logistique conviendra !")
Ctrl+Entrée

Cliquez sur « Exécuter » pour voir le résultat.

Contenu verrouillé
6 / 14

Continuez votre apprentissage

Vous avez exploré 6 sections de ce module. Connectez-vous pour débloquer le reste du cours, incluant les exercices pratiques et les solutions.

Ctrl ou Cmd plus Entrée exécute le code. Tab indente ; Échap puis Tab quitte l'éditeur.