Travaux pratiques - Classification automatique avec k-means

(la nouvelle version de cette séance, utilisant l’API DataFrame)

Références externes utiles :

Nous travaillerons lors de cette séance sur des données générées lors de la séance de travaux pratiques. Vous aurez à traiter ensuite les données Spambase Data Set issues de l’archive de l’UCI. Nous vous incitons à regarder sur le site de l’UCI des explications plus détaillées concernant ces données, notamment la signification des variables et les classes auxquelles appartiennent les données.

Ouvrez une fenêtre terminal et entrez

[cloudera@quickstart ~]$ export PATH="$PATH:/usr/lib/spark/bin"
[cloudera@quickstart ~]$ mkdir -p tpkmeans/data
[cloudera@quickstart ~]$ cd tpkmeans
[cloudera@quickstart tpkmeans]$ spark-shell

Importation des librairies nécessaires

Entrez

scala> import org.apache.spark.mllib.clustering.KMeans
scala> import org.apache.spark.mllib.linalg.Vectors
scala> import org.apache.spark.mllib.util.KMeansDataGenerator

Génération des données

Nous nous servirons de KMeansDataGenerator.generateKMeansRDD pour obtenir les données de travail. Cette méthode choisit d’abord k centres de groupes à partir d’une loi normale \(d\)-dimensionnelle, d’écart-type \(r\) et ensuite crée autour de chaque centre un groupe à partir d’une loi normale \(d\)-dimensionnelle d’écart-type 1. Les paramètres d’appel sont :

  • sc : le SparkContext concerné

  • numPoints : le nombre total de données générées

  • k : le nombre de centres (donc de groupes dans les données générées)

  • d : la dimension des données

  • r : écart-type pour la loi normale qui génère les centres

  • numPartitions : nombre de partitions pour le RDD généré (2 par défaut)

Nous générerons 1000 données tridimensionnelles dans 5 groupes en utilisant :

// Générer les données
scala> val donneesGenerees = KMeansDataGenerator.generateKMeansRDD(sc, 1000, 5, 3, 5, 1)
scala> val donnees = donneesGenerees.map(s => Vectors.dense(s)).cache()
scala> donnees.take(2)

// Enregistrer les données dans un fichier texte
scala> val donneesTxt = donnees.map(l => l.toString.filter(c => c != '[' & c != ']'))
scala> donneesTxt.saveAsTextFile("file:///home/cloudera/tpkmeans/data/donnees")

numPartitions a été fixé ici à 1 pour faciliter l’enregistrement des fichiers à visualiser. donneesGenerees est de type RDD[Array], nous le transformons en donnees de type RDD[Vector] exigé par l’implémentation de k-means.

K-means avec initialisation par K-means||

Spark MLlib propose une implémentation de l’algorithme de classification automatique k-means. L’initialisation des centres est réalisée par l’algorithme parallèle k-means||, vu aussi en cours, avec 5 itérations (initializationSteps: 5, peut être modifié avec KMeans.setInitializationSteps).

La classification est obtenue en appelant KMeans.train, avec les paramètres suivants :
  • data : données à classifier, sous forme de RDD[Vector]

  • k : nombre de groupes (clusters)

  • maxIterations : nombre maximal d’itérations ; arrêt de l’exécution lorsque les centres sont stabilisés (à epsilon: 1e-4 près) ou maxIterations est atteint ; epsilon peut être modifié en appelant KMeans.epsilon

  • runs : nombre d’exécutions (1 par défaut) ; c’est le meilleur modèle qui est retourné

  • initializationMode : mode d’initialisation des centres, soit random soit k-means|| (par défaut)

  • seed : initialisation du générateur de nombres aléatoires

Pour réaliser la classification automatique entrez dans spark-shell les commandes suivantes :

// Appliquer k-means
scala> val nbClusters = 5
scala> val nbIterations = 200
scala> val clusters = KMeans.train(donnees, nbClusters, nbIterations)

// Evaluer la classification par la somme des inerties intra-classe
scala> val siic1 = clusters.computeCost(donnees)

// Trouver l'indice de groupe pour chaque donnée
scala> val indices = clusters.predict(donnees)

// Enregistrer les indices dans un fichier texte
scala> indices.saveAsTextFile("file:///home/cloudera/tpkmeans/data/indices")

Visualisation des résultats

Dans cette séance nous visualiserons les données à l’aide de gnuplot (installé dans la séance précédente). Vous pouvez vous servir d’autres outils (comme matplotlib ou ggplot2) si vous les maîtrisez déjà ; il vous faudra toutefois les installer.

Pour préparer la visualisation, il est nécessaire de concaténer les lignes du fichier de données et les lignes du fichier d’indices. Nous le ferons directement dans une fenêtre terminal :

[cloudera@quickstart ~]$ cd /home/cloudera/tpkmeans/data
[cloudera@quickstart data]$ paste --delimiters="," donnees/part-00000 indices/part-00000 > donneesGnuplot.txt

Nous utiliserons cette fois gnuplot à l’aide de commandes en ligne. Pour cela, ouvrez une nouvelle fenêtre terminal, placez-vous dans le répertoire /home/cloudera/tpkmeans et lancez gnuplot :

[cloudera@quickstart ~]$ cd /home/cloudera/tpkmeans
[cloudera@quickstart spark]$ gnuplot
gnuplot>

Entrez ensuite les commandes suivantes dans la fenêtre gnuplot (après l’invite gnuplot>) :

gnuplot> set datafile separator ','
gnuplot> set palette defined ( 0 "red", 1 "orange", 2 "brown", 3 "green", 4 "blue" )
gnuplot> splot "/home/cloudera/tpkmeans/data/donneesGnuplot.txt" using 1:2:3:4 with points lc palette

Nous avons d’abord indiqué à gnuplot que le séparateur entre données d’une même ligne était la virgule (et non l’espace, séparateur par défaut), ensuite nous avons défini une palette permettant de donner à chaque point une couleur qui indique le groupe auquel il appartient et enfin nous avons appelé la fonction splot en lui indiquant que les données tridimensionnelles à afficher étaient suivies d’une quatrième dimension correspondant à la couleur du point.

Vous pouvez faire tourner le graphique en maintenant appuyé le bouton gauche de la souris et en déplaçant le pointeur dans la fenêtre graphique.

Questions

Question :

Réalisez une nouvelle fois la classification des mêmes données avec k-means en 5 groupes. Visualisez les résultats en ouvrant une autre fenêtre terminal et en lançant gnuplot dans cette fenêtre (afin de conserver la première fenêtre de visualisation). Que constatez-vous ?

Question :

Réalisez deux fois la classification des mêmes données avec k-means en 5 groupes mais avec une initialisation random plutôt que k-means||. Visualisez les résultats en ouvrant à chaque fois une autre fenêtre terminal et en lançant gnuplot dans cette fenêtre (afin de conserver toutes les fenêtres de visualisation). Que constatez-vous ?

Question :

Réalisez la classification des mêmes données avec k-means et une initialisation k-means|| en 4 groupes et ensuite en 6 groupes. Visualisez à chaque fois les résultats. Que constatez-vous ?

Question :

Multipliez par 3 les données sur une des dimensions initiales. Réalisez la classification des nouvelles données avec k-means (et initialisation par k-means||) en 5 groupes. Visualisez les résultats. Que constatez-vous ?

Question :

Réalisez la classification des données Spambase Data Set issues de l’archive de l’UCI. Comment pré-traiter les données et pourquoi ? Combien de groupes rechercher ? Visualisez ensuite leurs projections sur des groupes de 3 variables.