Formation ML / Apprentissage supervisé - Classification

K-Nearest Neighbors (KNN)

Débutant 30 min 13 sections

Aucun entraînement : la classe se décide au vote des k voisins, à condition d'avoir normalisé les échelles.

Objectifs d'apprentissage

  • Comprendre le principe de classification par voisinage
  • Choisir le bon nombre k de voisins
  • Connaître l'importance de la normalisation
  • Implémenter KNN avec scikit-learn

Prérequis

Notions de base en Python

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 que KNN?

K-Nearest Neighbors (KNN) est un algorithme de classification base sur une idée simple:

"Dis-moi qui sont tes voisins, je te dirai qui tu es"

Fonctionnement:

  1. Pour un nouveau point, trouver les k points les plus proches
  2. Regarder les classes de ces k voisins
  3. Prédire la classe majoritaire

Exemple: Si k=5 et les 5 voisins sont [A, A, B, A, B], on prédit A (3 contre 2).

Avantages:

  • Très intuitif et facile à comprendre
  • Pas d'entraînement (lazy learning)
  • Fonctionne pour classification et régression
  • Pas d'hypothèse sur la distribution des données

Inconvénients:

  • Lent en prédiction (doit calculer toutes les distances)
  • Sensible à l'échelle des features
  • Sensible au "fléau de la dimension"
  • Le choix de k est important
Théorie

Schéma: Comment KNN décide

Classification d'un nouveau point:

flowchart TD NEW["Nouveau Point
(à classifier)"] subgraph Voisins ["Recherche des k=5 voisins"] V1["Voisin 1: Setosa
Distance: 0.2"] V2["Voisin 2: Setosa
Distance: 0.3"] V3["Voisin 3: Versicolor
Distance: 0.5"] V4["Voisin 4: Setosa
Distance: 0.6"] V5["Voisin 5: Versicolor
Distance: 0.7"] end NEW --> Voisins VOTE["Vote Majoritaire
Setosa: 3 | Versicolor: 2"] Voisins --> VOTE PRED["Prédiction: SETOSA"] VOTE --> PRED class PRED ml-node-accent

Impact du choix de k:

flowchart LR subgraph K1 ["k=1"] R1["Très sensible
au bruit"] end subgraph K5 ["k=5"] R5["Equilibre
(souvent optimal)"] end subgraph K50 ["k=50"] R50["Trop lisse
(sous-apprentissage)"] end class R5 ml-node-accent

Conseil: Choisir k impair pour éviter les égalités!

Exemple concret - Classification d'une fleur (k=3):

Nouvelle fleur: $\textcolor{#3498db}{petal\_length = 3.5}$ cm, $\textcolor{#e67e22}{petal\_width = 1.2}$ cm

Étape 1 - Calcul des distances (Euclidienne):

$$d = \sqrt{(\textcolor{#3498db}{x_1} - \textcolor{#3498db}{x_1'})^2 + (\textcolor{#e67e22}{x_2} - \textcolor{#e67e22}{x_2'})^2}$$

VoisinEspèceDistance
$\textcolor{#9B7AC4}{V_1}$Versicolor$\textcolor{#9B7AC4}{0.3}$
$\textcolor{#F7E64D}{V_2}$Versicolor$\textcolor{#F7E64D}{0.5}$
$\textcolor{#e74c3c}{V_3}$Virginica$\textcolor{#e74c3c}{0.8}$

Étape 2 - Vote majoritaire:

  • Versicolor: $\textcolor{#9B7AC4}{1}$ + $\textcolor{#F7E64D}{1}$ = 2 votes
  • Virginica: $\textcolor{#e74c3c}{1}$ = 1 vote

Résultat: $\textcolor{#27ae60}{\mathbf{VERSICOLOR}}$ (2 > 1)

Légende des couleurs:

  • $\textcolor{#3498db}{Bleu}$ : longueur du pétale ($\textcolor{#3498db}{3.5}$ cm)
  • $\textcolor{#e67e22}{Orange}$ : largeur du pétale ($\textcolor{#e67e22}{1.2}$ cm)
  • $\textcolor{#9B7AC4}{Violet}$ : voisin 1 (le plus proche)
  • $\textcolor{#F7E64D}{Jaune}$ : voisin 2
  • $\textcolor{#e74c3c}{Rouge}$ : voisin 3 (le plus éloigné)
  • $\textcolor{#27ae60}{Vert}$ : prédiction finale
Avancé Exercice manuel: À vous de calculer!

Objectif: Appliquer K-Nearest Neighbors à la main.

CONTEXTE

Classification de fruits (Pomme/Orange) basée sur 2 features : poids et couleur (score).

Points d'entraînement :

FruitPoids (g)CouleurClasse
A1500.8Pomme
B1700.9Pomme
C1400.3Orange
D1600.4Orange
E1800.7Pomme

Nouveau fruit à classifier : Poids = 155g, Couleur = 0.5

Distance euclidienne : $d = \sqrt{(x_1-y_1)^2 + (x_2-y_2)^2}$

Note: Normalisez le poids en divisant par 100.

PARTIE 1 : Calcul des distances

1.1) Calculez la distance du nouveau fruit à chaque point d'entraînement 1.2) Classez les points par distance croissante

PARTIE 2 : Classification avec K=3

2.1) Quels sont les 3 plus proches voisins ? 2.2) Quelle est la prédiction (vote majoritaire) ?

PARTIE 3 : Impact de K

3.1) Quelle serait la prédiction avec K=1 ? 3.2) Et avec K=5 ?

Avancé Solution de l'exercice manuel

SOLUTION DÉTAILLÉE

Nouveau point : (1.55, 0.5) après normalisation

PARTIE 1 : Calcul des distances

1.1) Distances (poids normalise par 100) :

  • $d(A) = \sqrt{(1.55-1.5)^2 + (0.5-0.8)^2} = \sqrt{0.0025 + 0.09} = \textcolor{#3498db}{0.304}$
  • $d(B) = \sqrt{(1.55-1.7)^2 + (0.5-0.9)^2} = \sqrt{0.0225 + 0.16} = \textcolor{#3498db}{0.427}$
  • $d(C) = \sqrt{(1.55-1.4)^2 + (0.5-0.3)^2} = \sqrt{0.0225 + 0.04} = \textcolor{#e67e22}{0.250}$
  • $d(D) = \sqrt{(1.55-1.6)^2 + (0.5-0.4)^2} = \sqrt{0.0025 + 0.01} = \textcolor{#e67e22}{0.112}$
  • $d(E) = \sqrt{(1.55-1.8)^2 + (0.5-0.7)^2} = \sqrt{0.0625 + 0.04} = \textcolor{#3498db}{0.320}$

1.2) Tri par distance :

  1. D (0.112) - Orange
  2. C (0.250) - Orange
  3. A (0.304) - Pomme
  4. E (0.320) - Pomme
  5. B (0.427) - Pomme

PARTIE 2 : Classification avec K=3

2.1) 3 plus proches : $\textcolor{#F7E64D}{D, C, A}$

2.2) Vote :

  • Orange : 2 (D, C)
  • Pomme : 1 (A)

$\boxed{\text{Prédiction : Orange}}$

PARTIE 3 : Impact de K

3.1) K=1 : Plus proche = D (Orange) → $\textcolor{#e67e22}{\text{Orange}}$

3.2) K=5 : Tous les points

  • Orange : 2 (D, C)
  • Pomme : 3 (A, E, B)

→ $\textcolor{#3498db}{\text{Pomme}}$

$\boxed{\text{K influence la décision!}}$

Légende : $\textcolor{#3498db}{Bleu}$: Pomme, $\textcolor{#e67e22}{Orange}$: Orange, $\textcolor{#F7E64D}{Jaune}$: K plus proches

Code

Explorer le dataset Iris

print("=" * 70)
print("      EXPLORATION DU DATASET IRIS POUR KNN")
print("=" * 70)

print("\n" + "=" * 70)
print("1. APERCU DU DATASET")
print("=" * 70)
print("""
Le dataset Iris est un classique du Machine Learning!
Il contient des mesures de 150 fleurs de 3 especes differentes.

KNN va apprendre a classifier une nouvelle fleur en regardant
les k fleurs les plus similaires (les plus proches).
""")
display(df.head(10), title="Dataset Iris")

print("\n" + "=" * 70)
print("2. DISTRIBUTION DES ESPECES")
print("=" * 70)
species_counts = df['species'].value_counts()
print("\nRepartition des especes:")
print("-" * 40)
for species, count in species_counts.items():
    pct = count / len(df) * 100
    bar = "█" * int(pct / 3)
    print(f"  {species:12}: {count:3d} ({pct:.0f}%)  {bar}")

print(f"""
Interpretation:
  → Dataset parfaitement equilibre (50 de chaque espece)
  → Ideal pour l'apprentissage supervise
  → Pas besoin de techniques de resampling
""")

print("\n" + "=" * 70)
print("3. STATISTIQUES DES CARACTERISTIQUES")
print("=" * 70)
print("""
KNN utilise les distances entre points, donc les ECHELLES
des features sont cruciales!
""")
display(df.describe().round(2), title="Statistiques descriptives")

print("\n" + "-" * 40)
print("ANALYSE DES ECHELLES (CRUCIAL POUR KNN):")
print("-" * 40)
features = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
for feature in features:
    min_val = df[feature].min()
    max_val = df[feature].max()
    range_val = max_val - min_val
    print(f"  {feature:15}: [{min_val:.1f} - {max_val:.1f}], amplitude = {range_val:.1f}")

print(f"""
PROBLEME POTENTIEL:
  → sepal_length varie de 4.3 a 7.9 (amplitude 3.6)
  → petal_width varie de 0.1 a 2.5 (amplitude 2.4)

  Sans normalisation, sepal_length dominera le calcul de distance!
  La NORMALISATION est OBLIGATOIRE pour KNN.
""")
Ctrl+Entrée

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

Code

Visualiser les données

print("=" * 70)
print("    VISUALISATION DES DONNEES IRIS")
print("=" * 70)
print("""
Visualisons les donnees pour comprendre comment KNN va les separer.
Un bon algorithme KNN fonctionne mieux quand les classes
forment des groupes distincts dans l'espace des features.
""")

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

colors = {'setosa': '#9B7AC4', 'versicolor': '#C09CF0', 'virginica': '#F7E64D'}

# Petal length vs width
for species in df['species'].unique():
    mask = df['species'] == species
    axes[0].scatter(df[mask]['petal_length'], df[mask]['petal_width'],
                   c=colors[species], label=species, alpha=0.7, s=60, edgecolors='black')
axes[0].set_xlabel('Petal Length (cm)')
axes[0].set_ylabel('Petal Width (cm)')
axes[0].set_title('Petales: Longueur vs Largeur')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Sepal length vs width
for species in df['species'].unique():
    mask = df['species'] == species
    axes[1].scatter(df[mask]['sepal_length'], df[mask]['sepal_width'],
                   c=colors[species], label=species, alpha=0.7, s=60, edgecolors='black')
axes[1].set_xlabel('Sepal Length (cm)')
axes[1].set_ylabel('Sepal Width (cm)')
axes[1].set_title('Sepales: Longueur vs Largeur')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\n" + "=" * 70)
print("INTERPRETATION DES VISUALISATIONS")
print("=" * 70)
print("""
GRAPHIQUE DE GAUCHE (Petales):
  → Setosa (violet) est clairement separee des autres
  → Versicolor et Virginica se chevauchent un peu
  → Les petales sont de bons discriminants!

GRAPHIQUE DE DROITE (Sepales):
  → Plus de chevauchement entre les especes
  → Moins discriminant que les petales
  → Utile en combinaison avec d'autres features

IMPLICATIONS POUR KNN:
  → Un nouveau point dans la zone violette sera facilement classe Setosa
  → Dans la zone de chevauchement, le resultat depend de k
  → Plus k est grand, plus la decision est "lissee"
""")
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.