Objectifs¶
À l'issue du TP, vous devrez être capable de :
- expliquer les objectifs de l'apprentissage non supervisé ;
- distinguer une méthode linéaire (ACP/PCA) de méthodes non linéaires (t-SNE, UMAP) ;
- implémenter une ACP « à la main » avec
pandasetnumpy; - comparer une implémentation manuelle à celle de
scikit-learn; - appliquer k-means et évaluer un clustering avec des métriques internes et externes ;
- analyser l'influence de la standardisation, des hyperparamètres et de l'initialisation;
- être capable d'utiliser la documentation
scikit-learn.
1. Rappels de cours — apprentissage non supervisé¶
L'apprentissage non supervisé cherche à extraire une structure à partir des seules variables d'entrée, sans cible à prédire. Il permet notamment de répondre aux questions suivantes :
- quelles observations se ressemblent ?
- quelles variables sont redondantes ?
- peut-on représenter les données dans un espace plus petit ?
- certains points sont-ils atypiques ?
Dans ce TP, nous étudions deux familles de méthodes :
- Réduction de dimension : ACP/PCA, t-SNE et UMAP ;
- Clustering : k-means.
Une réduction de dimension peut servir à visualiser, compresser, débruiter ou préparer les données avant un autre algorithme. Attention : une projection visuellement convaincante ne garantit pas qu'elle préserve toutes les propriétés géométriques du jeu de données.
# Décommentez uniquement si les bibliothèques ne sont pas installées.
%pip install -q scikit-learn umap-learn
[notice] A new release of pip is available: 24.0 -> 26.1.2 [notice] To update, run: pip install --upgrade pip Note: you may need to restart the kernel to use updated packages.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_wine, load_digits
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, adjusted_rand_score, normalized_mutual_info_score
RANDOM_STATE = 42
np.random.seed(RANDOM_STATE)
Partie I — Réduction de dimension¶
2. Analyse en composantes principales (ACP/PCA)¶
Soit une matrice de données $X \in \mathbb{R}^{n \times d}$, où les lignes sont les observations et les colonnes les variables.
L'ACP recherche des directions orthogonales maximisant successivement la variance projetée. Pour des données centrées $X_c$, la matrice de covariance peut s'écrire :
$$ \Sigma = \frac{1}{n-1}X_c^\top X_c. $$
Les vecteurs propres de $\Sigma$ définissent les axes principaux. Les valeurs propres associées mesurent la variance portée par ces axes. La projection sur les $k$ premiers axes est :
$$ Z = X_c W_k, $$
où $W_k$ contient les $k$ vecteurs propres associés aux plus grandes valeurs propres.
Centrer ou standardiser ?¶
L'ACP de scikit-learn centre automatiquement les variables, mais ne les réduit pas. La standardisation est généralement nécessaire lorsque les variables sont exprimées dans des unités différentes ou ont des ordres de grandeur très différents. Elle n'est toutefois pas neutre : elle donne la même variance initiale à chaque variable, y compris à une variable éventuellement très bruitée.
Exercice 1 — Chargement et exploration du jeu de données Wine¶
- Chargez le jeu de données
wineavecload_wine(as_frame=True). - Créez un
DataFramecontenant les variables et une série contenant les classes. - Affichez les dimensions, les types, les statistiques descriptives et les éventuelles valeurs manquantes.
- Comparez les ordres de grandeur et les variances des variables.
- Expliquez pourquoi une ACP standardisée semble pertinente (ou non pertinente) ici.
# À compléter
wine = load_wine(as_frame=True)
X = ...
y = ...
# Exploration
...
2.1 ACP « à la main » avec pandas et numpy¶
Dans cet exercice, l'utilisation de sklearn.decomposition.PCA et de StandardScaler est interdite jusqu'à l'étape de validation.
Exercice 2 — Implémentation manuelle¶
À partir du DataFrame X :
- calculez la moyenne et l'écart-type de chaque variable avec
pandas; - standardisez les données manuellement ;
- vérifiez numériquement que les moyennes sont proches de 0 et les écarts-types proches de 1 ;
- calculez la matrice de covariance avec
numpy; - calculez ses valeurs propres et vecteurs propres avec
np.linalg.eigh; - triez les valeurs propres par ordre décroissant et réordonnez les vecteurs propres ;
- calculez la proportion de variance expliquée et sa somme cumulée ;
- projetez les données sur les deux premières composantes ;
- représentez les individus dans le plan principal, colorés selon leur classe réelle.
Points d'attention :
- utilisez
ddof=1de manière cohérente pour l'écart-type et la covariance ; np.linalg.eighest préférable ànp.linalg.eigpour une matrice symétrique ;- le signe d'un vecteur propre est arbitraire : deux projections opposées selon un axe peuvent être mathématiquement équivalentes.
# Étape 1 — Standardisation manuelle
means = ...
stds = ...
X_std_df = ...
print("Moyennes après standardisation :")
print(...)
print("\nÉcarts-types après standardisation :")
print(...)
# Étape 2 — Covariance, décomposition spectrale et tri
cov_matrix = ...
eigenvalues, eigenvectors = ...
order = ...
eigenvalues = ...
eigenvectors = ...
explained_variance_ratio = ...
cumulative_explained_variance = ...
pca_summary = pd.DataFrame({
"valeur_propre": eigenvalues,
"variance_expliquee": explained_variance_ratio,
"variance_cumulee": cumulative_explained_variance,
})
pca_summary.head()
# Étape 3 — Projection sur les deux premiers axes
W2 = ...
Z_manual = ...
projection_manual = pd.DataFrame(Z_manual, columns=["PC1", "PC2"], index=X.index)
projection_manual["classe"] = y.to_numpy()
fig, ax = plt.subplots(figsize=(8, 6))
scatter = ax.scatter(...)
ax.set_xlabel(...)
ax.set_ylabel(...)
ax.set_title("ACP manuelle — jeu de données Wine")
ax.legend(*scatter.legend_elements(), title="Classe")
ax.grid(alpha=0.3)
plt.show()
Exercice 3 — Validation avec scikit-learn¶
- Reproduisez la standardisation avec
StandardScalerpuis l'ACP avecPCA(n_components=2). - Comparez les variances expliquées à celles de votre implémentation.
- Comparez les projections. Ne concluez pas à une erreur si un axe est inversé.
- Calculez l'erreur absolue maximale entre les ratios de variance expliquée.
- Expliquez pourquoi les résultats doivent être quasiment identiques malgré la différence éventuelle de signe.
# À compléter
scaler = ...
X_std_sklearn = ...
pca = ...
Z_sklearn = ...
print("Variance expliquée — manuel :", ...)
print("Variance expliquée — sklearn :", ...)
print("Écart absolu maximal :", ...)
Exercice 4 — Effet de la standardisation et choix du nombre de composantes¶
- Réalisez une ACP à deux composantes sur les données simplement centrées, puis sur les données standardisées.
- Comparez les projections et les ratios de variance expliquée.
- Réalisez ensuite une ACP conservant toutes les composantes.
- Tracez la variance expliquée individuelle et cumulée.
- Déterminez le nombre minimal de composantes nécessaire pour conserver au moins 90 % puis 95 % de la variance.
- Interprétez le résultat : une variance expliquée élevée garantit-elle une bonne séparation des classes ?
# À compléter : comparaison sans/avec standardisation
...
# À compléter : mthds du coude
...
3. t-SNE¶
t-SNE est une méthode non linéaire principalement destinée à la visualisation. Elle cherche à préserver les voisinages locaux en faisant correspondre des similarités dans l'espace original et dans l'espace projeté.
Contrairement à l'ACP :
- les axes t-SNE n'ont pas d'interprétation directe ;
- les distances globales et la taille relative des groupes sont peu fiables ;
- le résultat dépend de l'initialisation et des hyperparamètres, notamment la
perplexity; - t-SNE ne fournit pas naturellement une transformation simple pour de nouvelles observations.
Exercice 5 — Sensibilité de t-SNE¶
- Appliquez t-SNE sur les données Wine standardisées avec
perplexity=5,30et50. - Utilisez
init="pca",learning_rate="auto"etrandom_state=RANDOM_STATE. - Affichez séparément les trois projections.
- Recommencez avec une autre graine aléatoire pour une valeur de perplexité.
- Discutez la stabilité des voisinages et la prudence nécessaire pour interpréter les groupes.
# À compléter
perplexities = [5, 30, 50]
for perplexity in perplexities:
tsne = ...
Z_tsne = ...
fig, ax = plt.subplots(figsize=(7, 5))
scatter = ax.scatter(...)
ax.set_title(...)
ax.legend(*scatter.legend_elements(), title="Classe")
plt.show()
4. UMAP¶
UMAP construit un graphe de voisinage dans l'espace initial, puis cherche une représentation de faible dimension possédant une structure de voisinage similaire. Il est souvent plus rapide que t-SNE et permet de transformer de nouvelles observations.
Ses principaux hyperparamètres sont :
n_neighbors: compromis entre structure locale et plus globale ;min_dist: compacité autorisée des groupes dans l'espace projeté ;metric: métrique utilisée dans l'espace initial.
Exercice 6 — Influence des hyperparamètres UMAP¶
- Importez
umap.umap_ as umap. - Comparez les projections obtenues sur les données Wine standardisées pour
n_neighborségal à 5, 15 et 50. - Testez au moins deux valeurs de
min_dist. - Comparez qualitativement les résultats à ceux de l'ACP et de t-SNE.
- Expliquez pourquoi une meilleure séparation visuelle ne prouve pas automatiquement une meilleure représentation de la structure réelle.
# À compléter
import umap.umap_ as umap
...
Partie II — Clustering¶
5. k-means : principe et hypothèses¶
k-means partitionne les observations en $k$ groupes en minimisant l'inertie intra-cluster :
$$ \sum_{i=1}^{n}\lVert x_i - \mu_{c(i)} \rVert^2. $$
L'algorithme alterne deux étapes : affectation de chaque point au centroïde le plus proche, puis recalcul des centroïdes. Il est sensible à l'initialisation et suppose implicitement des groupes relativement compacts, convexes et de tailles comparables.
Évaluation¶
- Inertie : mesure de compacité, mais décroît mécaniquement lorsque $k$ augmente.
- Silhouette : métrique interne entre -1 et 1 combinant compacité et séparation.
- ARI et NMI : métriques externes utilisables lorsque des labels de référence sont disponibles. Elles ne supposent pas que les numéros de clusters correspondent aux numéros de classes.
6. Jeu de données Digits¶
Nous allons utiliser sklearn.datasets.load_digits. Le dataset contient des images 8 × 8 de chiffres manuscrits, soit 64 variables par observation.
Exercice 7 — Préparation des données¶
- Chargez
load_digits(). - Affichez 15 images avec leurs labels.
- Examinez les dimensions et la plage des valeurs de pixels.
- Standardisez les variables avant l'ACP et k-means. Discutez néanmoins l'intérêt et les limites de cette standardisation pour des pixels partageant la même unité.
digits = load_digits()
X_digits = pd.DataFrame(digits.data)
y_digits = pd.Series(digits.target, name="classe")
# À compléter : exploration et affichage de 15 images
...
Exercice 8 — Choix de $k$ et clustering dans l'espace original¶
- Pour $k$ allant de 2 à 15, ajustez un k-means avec
n_init=20etrandom_state=RANDOM_STATE. - Tracez l'inertie et le score de silhouette en fonction de $k$ sur deux figures distinctes.
- Proposez une valeur de $k$ sans utiliser les labels, puis comparez-la à la connaissance externe du nombre de chiffres.
- Pour $k=10$, calculez l'ARI et la NMI avec les labels réels.
- Expliquez pourquoi l'accuracy brute n'est pas une métrique appropriée sans réappariement des labels de clusters.
# À compléter
k_values = range(2, 16)
inertias = []
silhouettes = []
for k in k_values:
model = ...
labels = ...
inertias.append(...)
silhouettes.append(...)
# Deux graphiques distincts
...
Exercice 9 — PCA puis k-means¶
- Standardisez les données Digits.
- Appliquez une ACP en conservant 90 % de la variance (
n_components=0.90). - Indiquez le nombre de dimensions conservées.
- Appliquez k-means avec $k=10$ dans cet espace réduit.
- Comparez temps d'ajustement, inertie, silhouette, ARI et NMI avec le clustering dans l'espace original.
- Pour la visualisation seulement, projetez les données sur les deux premières composantes et colorez-les selon les clusters.
- Discutez pourquoi l'espace utilisé pour visualiser ne doit pas nécessairement être celui utilisé pour entraîner k-means.
# À compléter
...
Exercice 10 — UMAP puis k-means : protocole critique¶
- Construisez une représentation UMAP à 2 dimensions, puis appliquez k-means avec $k=10$.
- Calculez silhouette, ARI et NMI.
- Comparez avec l'espace original et l'espace PCA.
- Répétez l'expérience pour plusieurs graines aléatoires et présentez moyenne et écart-type des scores.
- Discutez le risque méthodologique consistant à choisir les hyperparamètres UMAP en observant directement les labels réels.
# À compléter
...
.png)