Travaux pratiques - UMAP¶
(correspond à 1 séance de TP)
Références externes utiles :
L’objectif de cette séance de TP est de présenter l’utilisation des fonctionnalités de réduction de non-linéaire du paquet umap-learn permettant de manipuler l’algorithme UMAP, ainsi que de donner une meilleure compréhension de cette méthode et de l’influence de ses paramètres principaux sur les résultats. Pour atteindre cet objectif, nous allons examiner les résultats de plusieurs réductions de dimension sur un jeu de données synthétique (le « gâteau roulé ») puis sur des chiffres manuscrits.
Installation du paquet umap-learn
¶
Pour commencer cette séance de TP, il est indispensable de disposer
d’une installation fonctionnelle de Python et de scikit-learn mais aussi
du paquet umap-learn
. Il peut s’installer à l’aide de la commande
pip install umap-learn
, via Anaconda ou directement depuis Jupyter
via la commande magique ci-dessous :
%pip install umap-learn
import umap
Expérimentations sur le gâteau roulé¶
Dans un premier temps, nous allons examiner le jeu de données du gâteau roulé (le Swiss Roll). Il s’agit d’un jeu de données en trois dimensions correspondant à une surface bidimensionnelle enroulée sur elle-même.
# Import des bibliothèques utiles
import sklearn
import matplotlib.pyplot as plt
Le swiss roll est intégré dans scikit-learn, qui dispose d’une fonction préfaite pour générer automatiquement un jeu de données suivant cette distribution. Nous pouvons en produire une matrice d’observations contenant 1500 exemples et les visualiser en 3D.
from sklearn.datasets import make_swiss_roll
from mpl_toolkits.mplot3d import Axes3D
X, color = make_swiss_roll(n_samples=1500)
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection="3d")
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=color, s=50, cmap=plt.cm.Spectral)
plt.show()
L’usage de UMAP au travers de la bibliothèque umap-learn
est
relativement aisé puisque ce paquet suit la même interface que les
algorithmes que nous avons manipulé jusqu’ici dans scikit-learn. La
classe umap.UMAP
permet d’instancier un modèle.
Le paramètre principal est le nombre de dimensions de l’espace réduit,
spécifié par le paramètre n_components
, comme pour t-SNE. Par
défaut, UMAP projette les données dans un espace bidimensionnel à des
fins de visualisation (n_components=2
) mais il est bien entendu
possible de choisir un espace de destination de n’importe quelle
dimension.
Le principal paramètre de UMAP, analogue à la perplexité pour t-SNE, est
le nombre de voisins utilisé pour lors de la phase de calcul des
similarités dans l’espace des observations. Il se règle à l’aide du
paramètre n_neighbors
. Plus ce nombre est élevé, plus l’importance
accordée à la structure globale sera élevée. Avec un nombre de voisins
considéré faible, UMAP accordera plus d’importance aux structures
locales. Par défaut, les 15 plus proches voisins sont considérés.
Un dernier paramètre d’intérêt de UMAP est la valeur minimale de
distance autorisée dans l’espace d’arrivée, contrôlée par le paramètre
min_dist
. Pour rappel, cette distance contrôle implicitement la
forme de la similarité dans l’espace d’arrivée. Plus cette valeur est
faible, plus des points voisins dans l’espace de départ seront
rapprochés dans l’espace d’arrivée. Augmenter cette valeur permet de
réduire l’agglutinement des points, possiblement au détriment de la
structure locale. Par défaut, min_dist=0.1
.
En plus de ces paramètres, l’optimisation de UMAP
peut également
être réglée à l’aide des paramètres optionnels suivants:
metric
: permet de changer la métrique utilisée pour le calcul des distances dans l’espace de départ (par défaut, la distance euclidienne est utilisée).init
: modifie l’algorithme d’initialisation des points dans l’espace d’arrivée. Les possibilités sont"spectral"
(mode par défaut) ou"random"
(aléatoire).a
etb
: plutôt que de spécifiermin_dist
, il est possible de modifier directement les paramètres de la fonction de similarité.learning_rate
: permet de contrôler le pas d’apprentissage de la descente de gradient.n_epochs
: contrôle le nombre d’itérations d’apprentissage de la descente de gradient. Il peut dans de rares cas être nécessaire d’augmenter la valeur par défaut (200) si UMAP affiche un avertissement de non-convergence.
Nous pouvons ainsi appliquer UMAP sur le gâteau roulé de la façon suivante :
from umap import UMAP
um = UMAP(n_components=2, n_neighbors=15, min_dist=0.1)
embeddings = um.fit_transform(X)
print(embeddings.shape)
Les vecteurs réduits obtenus sont bien des observations bidimensionnelles. Nous pouvons afficher le nuage de points dans l’espace réduit:
plt.scatter(embeddings[:,0], embeddings[:,1], c=color, cmap=plt.cm.Spectral)
plt.show()
Question
Expérimenter avec différentes valeurs pour le paramètre n_neighbors
.
Que se passe-t-il quand le nombre de voisins considéré est faible ? Et
lorsqu’il est élevé (relativement au nombre d’observations du jeu de
données) ? En quoi ces résultats pouvait-on s’attendre à ces résutlats ?
Question
Expérimenter avec différentes valeurs pour le paramètre min_dist
(la
plage de valeurs autorisées est entre 0 et 1). Que constatez-vous ?
Jeu de données Digits¶
Digits est un jeu de données contenant des images en niveaux de gris de chiffres manuscrits de \(8\times 8 = 64\) pixels. Il comporte 1797 observations réparties en 10 classes, une par chiffre. Un total de 43 personnes ont participé à la collecte de données. Les valeurs de pixels sont des entiers dans l’intervalle \([0,16]\). Plus d’informations sont disponibles dans la documentation de scikit-learn.
Ce jeu de données est préintégré dans scikit-learn, ce qui facilite sa manipulation. Nous allons commencer par charger ces données en mémoire et afficher les dix premières observations sous forme d’image.
from sklearn import datasets
X, y = datasets.load_digits(return_X_y=True)
fig = plt.figure(figsize=(12, 4))
for i in range(10):
fig.add_subplot(1, 10, i+1)
plt.imshow(X[i].reshape((8,8)), cmap=plt.cm.binary)
plt.axis("off")
plt.show()
Question
Appliquer une réduction de dimension non-linéaire à l’aide de UMAP pour projeter les données en deux dimensions. Que constatez-vous du point de vue de la séparation des différents groupes de chiffres?
UMAP peut également tenir compte de l’information contenue dans les étiquettes de groupe lors du calcul des distances. Cela permet de renforcer la séparation des clusters correspondant à des classes différentes.
Pour ce faire, les méthodes .fit()
et .fit_transform()
de
l’objet UMAP()
acceptent un paramètre optionnel y
qui permet de
passer en argument les informations sur les groupes. Se référer à la
documentation
pour plus de détails.
Question
Appliquer une réduciton de dimension non-linéaire supervisée en utilisant UMAP pour projeter les données en deux dimensions. Que constatez-vous par rapport à l’approche non-supervisée utilisée précédemment ?
Validation du modèle¶
Une façon de valider la pertinence de la projection obtenue par UMAP est de comparer les performances d’un classifieur par k-plus proches voisins (kNN):
sur les données d’origine (les images \(8\times 8\)),
sur les données réduites (la projection dans le plan).
Le classifieur kNN est disponible dans scikit-learn via le module
sklearn.neighbors.KNeihgborsClassifier
.
from sklearn.neighbors import KNeighborsClassifier
La méthode .fit
requiert une matrice d’observations X
ainsi que
les étiquettes de classe y
. Nous pouvons facilement utiliser sklearn
pour entraîner un kNN sur le jeu de données Digits, ici par exemple
avec \(k=10\) :
knn = KNeighborsClassifier(n_neighbors=10)
knn.fit(X, y)
print(knn.score(X, y))
Question
En vous aidant de la
documentation
de la classe KNeighborsClassifier
de scikit-learn, que renvoie la
méthode .score()
?
Question
Appliquer une classification par les 10 plus proches voisins sur les données Digits projetées par UMAP non-supervisé. Quel score obtenez-vous ? Quelle conclusion pouvez-vous en tirer?
Comparer avec le même protocole mais appliqué aux données projetées via t-SNE.
Question
(optionnel) Comparer les temps de calcul de t-SNE et de UMAP dans les
mêmes conditions (même jeu de données, même machine, même dimension
cible). Pour un résultat plus représentatif d’une application sur des
données réelles, on utilisera le jeu de données MNIST (images
\(28\times 28\) en niveaux de gris). Dans Jupyter, les commandes
magiques %%time
et %%timeit
peuvent être utiles. Que
constatez-vous ?
from sklearn.datasets import fetch_openml
X_mnist, y_mnist = fetch_openml('mnist_784', return_X_y=True)
... # à compléter