« Tell me who your neighbors are, and I'll tell you who you are. »
— Principe fondateur de l'apprentissage par analogie
- Vue d'ensemble
- Fondements mathématiques
- Complexité algorithmique
- Le choix de k — Biais-Variance
- Problématiques dimensionnelles — La Malédiction de la Dimensionnalité
- Structures de données et optimisation
- 6.1 KD-Tree
- 6.2 Ball-Tree
- 6.3 Locality-Sensitive Hashing (LSH)
- Variantes et extensions
- Implémentation Python
- Hyperparamètres & Tuning
- Forces, limites et cas d'usage
- Comparaison avec d'autres algorithmes
- Références scientifiques
L'algorithme des K plus proches voisins (K-Nearest Neighbors, KNN) est un estimateur non paramétrique, à apprentissage paresseux (lazy learning), proposé dans sa forme initiale par Fix & Hodges en 1951. Il appartient à la famille des méthodes d'apprentissage par instance (instance-based learning) : aucun modèle explicite n'est construit lors de la phase d'entraînement ; la totalité du dataset est mémorisée et la généralisation s'opère à la prédiction.
Il supporte deux régimes d'apprentissage supervisé :
| Régime | Sortie | Stratégie d'agrégation |
|---|---|---|
| Classification | Classe discrète | Vote majoritaire (éventuellement pondéré) |
| Régression | Valeur continue | Moyenne (éventuellement pondérée) |
Données d'entraînement Nouveau point x*
● ● ?
● ● ◆ ◆
● ● → ◆ ← k=3 voisins → classe ◆
● ●
Le cœur de KNN réside dans le choix d'une métrique de similarité
-
Séparation :
$d(x, y) = 0 \iff x = y$ -
Symétrie :
$d(x, y) = d(y, x)$ -
Inégalité triangulaire :
$d(x, z) \leq d(x, y) + d(y, z)$
Les métriques les plus utilisées appartiennent à la famille Minkowski :
| Nom | Paramètre |
Formule explicite | Remarques |
|---|---|---|---|
| Manhattan (L1) | Robuste aux outliers | ||
| Euclidienne (L2) | La plus courante | ||
| Chebyshev (L∞) | Sensible à la dimension dominante |
Autres métriques notables :
-
Mahalanobis :
$d_M(x, y) = \sqrt{(x-y)^\top \Sigma^{-1} (x-y)}$ — neutralise les corrélations et les disparités d'échelle. -
Cosinus :
$d_\cos(x, y) = 1 - \frac{x \cdot y}{|x||y|}$ — invariante à la norme, privilégiée en NLP. - Hamming : adapée aux données binaires ou catégorielles.
⚠️ Invariance d'échelle : KNN est sensible aux unités. Une normalisation (Min-Max, Z-score) ou une standardisation préalable est obligatoire lorsque les features sont hétérogènes.
Soit un ensemble d'entraînement
Étape 1 — Identification des k voisins :
Étape 2a — Classification (vote majoritaire non pondéré) :
Étape 2b — Classification pondérée par la distance :
Étape 2c — Régression :
Théorème (Cover & Hart, 1967) : Soit
$R^*$ le taux d'erreur de Bayes (optimal). Le taux d'erreur asymptotique du classifieur 1-NN$R$ vérifie :
$$R^* \leq R \leq R^* \left(2 - \frac{c \cdot R^_}{c - 1}\right) \leq 2R^_$$ où
$c$ est le nombre de classes.
Ce résultat majeur garantit que le 1-NN ne peut jamais avoir un taux d'erreur supérieur au double de l'erreur de Bayes, et ce sans aucune hypothèse paramétrique sur la distribution sous-jacente. Lorsque
| Phase | Approche naïve | Avec KD-Tree | Avec LSH |
|---|---|---|---|
| Entraînement | |||
| Prédiction (1 requête) |
|
|
|
| Prédiction (Q requêtes) |
|
||
| Stockage |
Avec
Le KD-Tree devient inefficace pour
$d \gtrsim 20$ en raison de la malédiction de la dimensionnalité.
Le paramètre
k faible (ex: k=1) k élevé (ex: k=N)
↓ ↓
Faible biais Fort biais
Forte variance Faible variance
Sur-apprentissage Sous-apprentissage
Frontière de décision Frontière de décision
très irrégulière très lisse
Décomposition formelle de l'erreur quadratique moyenne :
Stratégies de sélection de k :
-
Validation croisée k-fold (recommandée) : balayage sur une grille
$k \in {1, 3, 5, \ldots, \sqrt{N}}$ -
Règle empirique :
$k \approx \sqrt{N}$ , avec préférence pour les valeurs impaires (évite les égalités en classification binaire) -
Courbe d'erreur : tracer l'erreur de validation en fonction de
$k$ et sélectionner le coude
En grande dimension, toutes les distances tendent à devenir équivalentes. Formellement, pour des points uniformément distribués sur
Ce phénomène, décrit par Bellman (1961) et formalisé par Beyer et al. (1999), rend le concept de « voisin » statistiquement vide en haute dimension.
Conséquence pratique : la fraction du volume de l'hypercube nécessaire pour capturer une fraction
Remèdes :
| Technique | Description |
|---|---|
| PCA / t-SNE / UMAP | Réduction de dimensionnalité avant KNN |
| Sélection de features | Élimination des features non informatives |
| Métriques adaptatives | Distance de Mahalanobis, LMNN (Large Margin Nearest Neighbor) |
| ANN (Approximate NN) | LSH, HNSW, FAISS — sacrifier l'exactitude pour la scalabilité |
Un KD-Tree est un arbre binaire partitionnant
Espace 2D : KD-Tree :
[médiane x]
────────────── ┌──────┴──────┐
| | | [med y] [med y]
| | ● ● | ┌──┴──┐ ┌──┴──┐
| | | ● ● ● ●
|────|────────|
| ● | ● |
| | |
──────────────
-
Construction :
$O(Nd\log N)$ -
Requête exacte :
$O(\log N)$ en faible dimension,$O(N)$ au pire cas -
Optimal pour :
$d \leq 20$
Partitionne les données en hypersphères imbriquées plutôt qu'en hyperplans. Meilleure performance que le KD-Tree pour les données non uniformes et les grandes dimensions modérées (
- L'inégalité triangulaire est exploitée pour élaguer des branches entières lors de la recherche.
Famille de fonctions de hachage
où
Implémentations modernes : FAISS (Meta AI), ScaNN (Google), HNSW (Malkov & Yashunin, 2018).
| Variante | Description | Usage typique |
|---|---|---|
| KNN pondéré | Améliore la précision aux frontières | |
| Radius-NN | Voisins dans un rayon |
Densité variable |
| LMNN | Apprend la métrique pour maximiser la marge | Classification |
| Parzen-Rosenblatt | Estimation de densité par fenêtre de Parzen | Estimation de PDF |
| Condensed KNN | Réduit le dataset aux exemples frontières | Compression mémoire |
| Edited KNN | Supprime les exemples mal classés | Débruitage |
| KNN pour anomalies | Score = |
Détection d'anomalies |
| KNORA | KNN pour la sélection dynamique d'ensembles | Ensemble learning |
pip install scikit-learn numpy pandas matplotlibimport numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
# Pipeline complet : normalisation + KNN
pipeline = Pipeline([
('scaler', StandardScaler()),
('knn', KNeighborsClassifier(
n_neighbors=5,
metric='minkowski',
p=2, # L2 euclidienne
weights='distance',
algorithm='auto', # sklearn choisit KD-Tree ou Ball-Tree
n_jobs=-1
))
])
# Recherche d'hyperparamètres
param_grid = {
'knn__n_neighbors': range(1, 31, 2),
'knn__weights': ['uniform', 'distance'],
'knn__metric': ['euclidean', 'manhattan', 'minkowski']
}
grid_search = GridSearchCV(
pipeline,
param_grid,
cv=10,
scoring='f1_weighted',
n_jobs=-1,
verbose=1
)
grid_search.fit(X_train, y_train)
print(f"Meilleurs params : {grid_search.best_params_}")
print(f"Score CV : {grid_search.best_score_:.4f}")from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error, r2_score
knn_reg = Pipeline([
('scaler', StandardScaler()),
('knn', KNeighborsRegressor(
n_neighbors=7,
weights='distance',
algorithm='ball_tree',
leaf_size=30,
n_jobs=-1
))
])
knn_reg.fit(X_train, y_train)
y_pred = knn_reg.predict(X_test)
print(f"RMSE : {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")
print(f"R² : {r2_score(y_test, y_pred):.4f}")import matplotlib.pyplot as plt
k_range = range(1, 51, 2)
cv_scores = []
for k in k_range:
knn = Pipeline([
('scaler', StandardScaler()),
('knn', KNeighborsClassifier(n_neighbors=k, n_jobs=-1))
])
scores = cross_val_score(knn, X, y, cv=10, scoring='accuracy')
cv_scores.append(scores.mean())
optimal_k = k_range[np.argmax(cv_scores)]
plt.figure(figsize=(10, 5))
plt.plot(k_range, cv_scores, marker='o', linewidth=1.5, color='steelblue')
plt.axvline(optimal_k, color='red', linestyle='--', label=f'k optimal = {optimal_k}')
plt.xlabel('Valeur de k')
plt.ylabel('Accuracy (CV 10-fold)')
plt.title('Sélection de k par validation croisée')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()import faiss
import numpy as np
# Construction de l'index (distance L2)
d = X_train.shape[1]
index = faiss.IndexFlatL2(d)
# Ajout des vecteurs d'entraînement
X_train_f32 = X_train.astype(np.float32)
index.add(X_train_f32)
# Requête : trouver les k=5 plus proches voisins
k = 5
distances, indices = index.search(X_test.astype(np.float32), k)
# indices : (N_test, k) — indices dans X_train
# distances : (N_test, k) — distances L2 au carré| Hyperparamètre | Valeurs typiques | Impact |
|---|---|---|
n_neighbors ( |
|
Biais-Variance |
weights |
uniform, distance
|
Robustesse aux outliers |
metric |
euclidean, manhattan, mahalanobis
|
Géométrie de l'espace |
algorithm |
auto, kd_tree, ball_tree, brute
|
Performance à la prédiction |
leaf_size |
20–50 | Mémoire vs vitesse de la structure |
p (Minkowski) |
1, 2 | L1 vs L2 |
Recommandations pratiques :
- Toujours normaliser les features avant KNN.
- Préférer
weights='distance'en présence de classes déséquilibrées. - Utiliser
algorithm='ball_tree'pour$d > 15$ etalgorithm='kd_tree'pour$d \leq 15$ . - Pour
$N > 10^5$ ou$d > 50$ , basculer vers FAISS ou HNSW.
- Aucune hypothèse paramétrique sur la distribution des données
-
Apprentissage trivial (
$O(1)$ à l'entraînement) - Adaptabilité naturelle aux distributions multimodales
- Interprétabilité : la prédiction est explicable par les voisins
- Extensible à des tâches non conventionnelles (recommandation, anomalies)
- Coût de prédiction élevé pour de grands datasets sans indexation
- Sensibilité à la dimensionnalité (malédiction)
- Sensibilité aux features non pertinentes et aux échelles
- Mémoire : le dataset entier doit être accessible à l'inférence
- Pas de modèle exportable : pas de paramètres appris
| Domaine | Application |
|---|---|
| Systèmes de recommandation | Collaborative filtering (utilisateurs similaires) |
| Finance | Détection d'anomalies (fraude) |
| Bioinformatique | Classification de gènes, similarité de protéines |
| Vision | Classification d'images (baseline), image retrieval |
| NLP | Recherche sémantique avec embeddings |
| Médecine | Diagnostic assisté par analogie clinique |
| Critère | KNN | SVM | Random Forest | Réseau de neurones |
|---|---|---|---|---|
| Entraînement |
|
|||
| Prédiction |
|
|||
| Interprétabilité | ✅ Haute | ❌ Faible | ||
| Grande dimension | ❌ | ✅ | ✅ | ✅ |
| Données non linéaires | ✅ | ✅ (kernel) | ✅ | ✅ |
| Faible volume de données | ✅ | ✅ | ❌ | |
| Données manquantes | ❌ | ❌ | ✅ |
| # | Référence |
|---|---|
| [1] | Fix, E. & Hodges, J. L. (1951). Discriminatory analysis: nonparametric discrimination. USAF School of Aviation Medicine. |
| [2] | Cover, T. & Hart, P. (1967). Nearest neighbor pattern classification. IEEE Transactions on Information Theory, 13(1), 21–27. |
| [3] | Bellman, R. (1961). Adaptive Control Processes: A Guided Tour. Princeton University Press. |
| [4] | Beyer, K. et al. (1999). When is "nearest neighbor" meaningful? ICDT. |
| [5] | Hastie, T., Tibshirani, R. & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.). Springer. |
| [6] | Weinberger, K. Q. & Saul, L. K. (2009). Distance metric learning for large margin nearest neighbor classification. JMLR, 10, 207–244. |
| [7] | Johnson, J. et al. (2019). Billion-scale similarity search with GPUs (FAISS). IEEE TPAMI. |
| [8] | Malkov, Y. & Yashunin, D. (2018). Efficient and robust approximate nearest neighbor search using HNSW. IEEE TPAMI, 42(4). |
| [9] | Bentley, J. L. (1975). Multidimensional binary search trees used for associative searching. CACM, 18(9), 509–517. |
| [10] | Zhang, M. L. & Zhou, Z. H. (2007). ML-KNN: A lazy learning approach to multi-label learning. Pattern Recognition, 40(7). |
