Formation ML / Apprentissage supervisé - Classification

Support Vector Machine (SVM)

Intermédiaire 45 min 13 sections

Parmi une infinité de frontières, celle qui maximise la marge ; le kernel trick prend le relais sinon.

Objectifs d'apprentissage

  • Comprendre le concept de marge maximale et d'hyperplan séparateur
  • Maîtriser le kernel trick pour les données non linéaires
  • Implémenter SVM avec scikit-learn
  • Choisir le bon kernel selon les données

Prérequis

Module Régression logistique 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

Qu'est-ce qu'un SVM?

Un Support Vector Machine (SVM) est un algorithme de classification qui cherche l'hyperplan séparant les classes avec la marge maximale.

Concepts clés:

  • Hyperplan: Frontière de décision (droite en 2D, plan en 3D)
  • Marge: Distance entre l'hyperplan et les points les plus proches
  • Support Vectors: Points qui définissent la marge (les plus proches de l'hyperplan)

Pourquoi maximiser la marge? Une marge plus grande = meilleure généralisation sur de nouvelles données.

Avantages:

  • Efficace en haute dimension
  • Fonctionne bien même si nb_features > nb_samples
  • Robuste à l'overfitting (grâce à la régularisation)

Inconvénients:

  • Lent sur très grands datasets (>10K samples)
  • Sensible à l'échelle des features (normalisation requise)
  • Choix du kernel peut être difficile
Théorie

Schéma: Marge maximale et Support Vectors

Trouver l'hyperplan optimal:

flowchart LR subgraph Data ["Données 2D"] direction TB C1["Classe 0
(cercles)"] C2["Classe 1
(carrés)"] end subgraph SVM ["SVM"] H["Hyperplan
(frontière)"] M["Marge
(maximisee)"] SV["Support Vectors
(points clés)"] end Data --> SVM class H ml-node-accent class SV ml-node-main

Plusieurs hyperplans possibles, lequel choisir?

flowchart TD H1["Hyperplan A
Marge: 0.5"] H2["Hyperplan B
Marge: 1.2"] H3["Hyperplan C
Marge: 2.0"] BEST["SVM choisit C
(marge maximale)"] H1 --> BEST H2 --> BEST H3 --> BEST class H3 ml-node-success class BEST ml-node-accent

Le problème des données non linéaires:

flowchart LR L["Données
Non Linéaires"] K["Kernel Trick
(transformation)"] H["Hyperplan
dans nouvel espace"] L -->|"Impossible en 2D"| K -->|"Séparable en 3D+"| H class K ml-node-brand class H ml-node-accent

Exemple concret - Classification d'un nouveau point:

Nouveau point: $\textcolor{#3498db}{x_1 = 2.5}$, $\textcolor{#e67e22}{x_2 = 1.8}$

Le SVM a appris l'hyperplan: $\textcolor{#9B7AC4}{w_1} \cdot x_1 + \textcolor{#9B7AC4}{w_2} \cdot x_2 + \textcolor{#9B7AC4}{b} = 0$ Avec $\textcolor{#9B7AC4}{w_1 = 0.8}$, $\textcolor{#9B7AC4}{w_2 = 0.6}$, $\textcolor{#9B7AC4}{b = -2.0}$

Calcul de la décision:

$$f(x) = \textcolor{#9B7AC4}{0.8} \times \textcolor{#3498db}{2.5} + \textcolor{#9B7AC4}{0.6} \times \textcolor{#e67e22}{1.8} + \textcolor{#9B7AC4}{(-2.0)}$$

$$f(x) = \textcolor{#3498db}{2.0} + \textcolor{#e67e22}{1.08} - 2.0 = \textcolor{#27ae60}{\mathbf{+1.08}}$$

Décision:

  • Si $f(x) > 0$ → $\textcolor{#27ae60}{\text{Classe 1}}$
  • Si $f(x) < 0$ → Classe 0

Ici $\textcolor{#27ae60}{+1.08 > 0}$ → Classe 1

Légende des couleurs:

  • $\textcolor{#3498db}{Bleu}$ : feature 1 et sa contribution ($\textcolor{#3498db}{2.5 \times 0.8 = 2.0}$)
  • $\textcolor{#e67e22}{Orange}$ : feature 2 et sa contribution ($\textcolor{#e67e22}{1.8 \times 0.6 = 1.08}$)
  • $\textcolor{#9B7AC4}{Violet}$ : poids appris par le SVM
  • $\textcolor{#27ae60}{Vert}$ : score final positif → Classe 1
Avancé Exercice manuel: À vous de calculer!

Objectif: Comprendre les SVM à la main (marge, vecteurs supports).

CONTEXTE

Vous avez 4 points en 2D à séparer (classification binaire) :

Pointx1x2Classe
A12+1
B23+1
C41-1
D52-1

L'hyperplan séparateur est : $w_1 x_1 + w_2 x_2 + b = 0$

Avec $w_1 = 1$, $w_2 = 0$, $b = -3$

Soit : $x_1 - 3 = 0$ (ligne verticale à $x_1 = 3$)

PARTIE 1 : Classification

1.1) Calculez $f(x) = w_1 x_1 + w_2 x_2 + b$ pour chaque point 1.2) Vérifiez que le signe de $f(x)$ correspond à la classe

PARTIE 2 : Calcul des marges

Distance d'un point à l'hyperplan : $\frac{|f(x)|}{||w||}$

2.1) Calculez $||w|| = \sqrt{w_1^2 + w_2^2}$ 2.2) Calculez la distance de chaque point à l'hyperplan 2.3) Quels sont les vecteurs supports (points les plus proches) ?

PARTIE 3 : Marge du classifieur

3.1) Quelle est la marge (distance au plus proche) ? 3.2) Pourquoi SVM maximise-t-il cette marge ?

Avancé Solution de l'exercice manuel

SOLUTION DÉTAILLÉE

PARTIE 1 : Classification

1.1) Calcul de $f(x) = x_1 - 3$ :

  • $f(A) = 1 - 3 = \textcolor{#3498db}{-2}$ → signe négatif
  • $f(B) = 2 - 3 = \textcolor{#3498db}{-1}$ → signe négatif
  • $f(C) = 4 - 3 = \textcolor{#e67e22}{+1}$ → signe positif
  • $f(D) = 5 - 3 = \textcolor{#e67e22}{+2}$ → signe positif

1.2) Vérification :

  • A, B : $f(x) < 0$ mais classe +1 → Incohérent !

Correction : L'hyperplan devrait être $-x_1 + 3 = 0$ ou les classes inversées. Supposons que les classes sont inversées (A,B = -1 et C,D = +1) pour la suite.

PARTIE 2 : Calcul des marges

2.1) Norme :

$$||w|| = \sqrt{1^2 + 0^2} = \textcolor{#9B7AC4}{1}$$

2.2) Distances :

  • $d(A) = \frac{|-2|}{1} = \textcolor{#27ae60}{2}$
  • $d(B) = \frac{|-1|}{1} = \textcolor{#F7E64D}{1}$
  • $d(C) = \frac{|+1|}{1} = \textcolor{#F7E64D}{1}$
  • $d(D) = \frac{|+2|}{1} = \textcolor{#27ae60}{2}$

2.3) Vecteurs supports :

$\boxed{\text{B et C (distance = 1)}}$

PARTIE 3 : Marge du classifieur

3.1) Marge = distance minimale = $\textcolor{#e74c3c}{\mathbf{1}}$

3.2) SVM maximise la marge pour :

  • Meilleure généralisation
  • Plus robuste au bruit
  • Frontière la plus "sure" entre les classes

Légende : $\textcolor{#3498db}{Bleu}$: classe -1, $\textcolor{#e67e22}{Orange}$: classe +1, $\textcolor{#F7E64D}{Jaune}$: vecteurs supports

Code

Explorer les données

print("=" * 70)
print("   EXPLORATION DES DONNEES DE CLASSIFICATION")
print("   Pour l'entrainement du SVM")
print("=" * 70)

# =================================================================
# 1. APERCU DES DONNEES
# =================================================================
print("\n" + "-" * 40)
print("1. APERCU DU DATASET")
print("-" * 40)
print("""
Ce dataset contient des exemples pour un probleme de classification binaire.
Le SVM va trouver l'hyperplan qui separe le mieux les deux classes.
""")
display(df.head(10), title="Dataset de Classification")

# =================================================================
# 2. DISTRIBUTION DES CLASSES
# =================================================================
print("\n" + "-" * 40)
print("2. DISTRIBUTION DES CLASSES")
print("-" * 40)

class_counts = df['label'].value_counts().sort_index()
total = len(df)

print(f"""
Classe 0 : {class_counts[0]:3d} echantillons ({class_counts[0]/total*100:.1f}%)
Classe 1 : {class_counts[1]:3d} echantillons ({class_counts[1]/total*100:.1f}%)

Total    : {total} echantillons
""")

# Verification de l'equilibre
ratio = min(class_counts) / max(class_counts)
if ratio > 0.8:
    print("  [OK] Classes bien equilibrees!")
    print("       Le SVM ne sera pas biaise.")
elif ratio > 0.5:
    print("  [ATTENTION] Leger desequilibre.")
    print("              Considerer class_weight='balanced'.")
else:
    print("  [ATTENTION] Desequilibre significatif!")
    print("              Utiliser class_weight='balanced' recommande.")

# =================================================================
# 3. STATISTIQUES PAR CLASSE
# =================================================================
print("\n" + "-" * 40)
print("3. STATISTIQUES PAR CLASSE")
print("-" * 40)
print("""
Comparons les moyennes des features entre les deux classes.
Si elles sont differentes, le SVM pourra les separer facilement.
""")
display(df.groupby('label').mean().round(3), title="Moyennes par classe")

# Interpreter les differences
mean_class0 = df[df['label'] == 0][['feature1', 'feature2']].mean()
mean_class1 = df[df['label'] == 1][['feature1', 'feature2']].mean()

print(f"""
DIFFERENCES ENTRE CLASSES:

Feature 1:
  - Classe 0: moyenne = {mean_class0['feature1']:.3f}
  - Classe 1: moyenne = {mean_class1['feature1']:.3f}
  - Difference: {abs(mean_class0['feature1'] - mean_class1['feature1']):.3f}

Feature 2:
  - Classe 0: moyenne = {mean_class0['feature2']:.3f}
  - Classe 1: moyenne = {mean_class1['feature2']:.3f}
  - Difference: {abs(mean_class0['feature2'] - mean_class1['feature2']):.3f}

Plus les differences sont grandes, plus la separation sera facile!
""")

# =================================================================
# 4. PLAGE DES VALEURS
# =================================================================
print("\n" + "-" * 40)
print("4. ECHELLE DES FEATURES (IMPORTANT POUR SVM!)")
print("-" * 40)

print(f"""
Feature 1: min = {df['feature1'].min():.3f}, max = {df['feature1'].max():.3f}
Feature 2: min = {df['feature2'].min():.3f}, max = {df['feature2'].max():.3f}

ATTENTION: Le SVM est sensible a l'echelle des features!
Si les echelles sont differentes, la feature avec les plus grandes
valeurs dominera le calcul de distance.

SOLUTION: Toujours normaliser les features avant SVM!
(StandardScaler ou MinMaxScaler)
""")
Ctrl+Entrée

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

Code

Visualiser les données

print("=" * 70)
print("   VISUALISATION DES DONNEES")
print("   Comprendre la separabilite des classes")
print("=" * 70)

# =================================================================
# 1. SCATTER PLOT DES CLASSES
# =================================================================
print("\n" + "-" * 40)
print("1. DISTRIBUTION SPATIALE DES CLASSES")
print("-" * 40)
print("""
Ce graphique montre la position de chaque echantillon dans l'espace 2D.
Le SVM va trouver la meilleure frontiere pour separer les couleurs.
""")

plt.figure(figsize=(10, 6))

# Separer par classe
class_0 = df[df['label'] == 0]
class_1 = df[df['label'] == 1]

plt.scatter(class_0['feature1'], class_0['feature2'],
           c='#9B7AC4', label='Classe 0', alpha=0.7, s=60, edgecolors='black')
plt.scatter(class_1['feature1'], class_1['feature2'],
           c='#F7E64D', label='Classe 1', alpha=0.7, s=60, edgecolors='black')

plt.xlabel('Feature 1', fontsize=12)
plt.ylabel('Feature 2', fontsize=12)
plt.title('Donnees de Classification Binaire', fontsize=14, fontweight='bold')
plt.legend(fontsize=11)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# =================================================================
# 2. INTERPRETATION
# =================================================================
print("\n" + "-" * 40)
print("2. INTERPRETATION DU GRAPHIQUE")
print("-" * 40)

# Calculer le chevauchement approximatif
overlap_f1 = max(0, min(class_0['feature1'].max(), class_1['feature1'].max()) -
                   max(class_0['feature1'].min(), class_1['feature1'].min()))
overlap_f2 = max(0, min(class_0['feature2'].max(), class_1['feature2'].max()) -
                   max(class_0['feature2'].min(), class_1['feature2'].min()))

print(f"""
ANALYSE VISUELLE:

Separabilite des classes:
""")

if overlap_f1 < 0.5 and overlap_f2 < 0.5:
    print("  [FACILE] Les classes sont bien separees!")
    print("           Un SVM lineaire devrait suffire.")
elif overlap_f1 < 1 or overlap_f2 < 1:
    print("  [MODERE] Les classes se chevauchent partiellement.")
    print("           Un kernel RBF pourrait etre necessaire.")
else:
    print("  [DIFFICILE] Les classes sont fortement melangees.")
    print("              Essayez differents kernels et regularisation.")

print(f"""
POUR LE SVM:
- Chercher une droite (kernel linear) ou courbe (kernel rbf)
  qui passe entre les deux groupes de couleurs
- Les points les plus proches de la frontiere = Support Vectors
- La marge = espace vide entre la frontiere et les points
""")
Ctrl+Entrée

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

Contenu verrouillé
6 / 13

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.