TP L2-Apprentissage

Posted on Wed 13 February 2019 in cours

Le cours d'introduction à l'apprentissage automatique est divisé en deux parties distinctes. La première abordera le perceptron (apprentissage supervisé) et la seconde l'apprentissage non supervisé sur des tâches de partitionnement de données..

L'objectif du cours est d'introduire les notions fondamentales sur sujet traité dans le but de mettre ensuite en pratique à l'aide de Java.

Cours

  • Cours du 03/09/19 : Introduction général
  • Cours du 10/09/19 : Le modèle du perceptron
  • Cours du 17/09/19 : Apprentissage du percetron
  • Cours du 24/09/19 : Les différents ensembles
  • Cours du 01/10/19 : Perceptron multiclasses
  • Cours du 08/10/19 : Perceptron multiclasses (fin) et métrique
  • Cours du 15/10/19 : Fin Partie 1 (questions), début apprentissage non-supervisé


TPs

Le langage de programmation est Java. Ce langage est au programme du L2. L'outil de travail est Eclipse: un environnement de développement intégré. Les premiers TPs seront l'occasion de découvrir eclipse et java et de mettre en oeuvre progressivement tous les éléments du programme pour faire un perceptron.

TP semaine du 16/09/2019 : Prise en main et Perceptron

L'objectif de ce premier TP sera de se familiariser avec Eclipse et de voir rapidement comment manipuler les fonctions et les tableaux. On se servira de la base de données MNIST qui sera utile plus tard.

Eclipse : le début

Le travail avec Eclipse s'organise en "Project", pour ce module vous allez créer un "Java Project", dont le nom sera ReseauDeNeurones (notez que le nom est en un seul mot). En java et donc avec Eclipse, l'organisation se fait selon les niveaux suivants:

  • un répertoire = un package
  • une classe = un fichier

Lancez Eclipse (ATTENTION : pour lancer eclipse il faut d'abord ouvrir un terminal, taper "export GTK_IM_MODULE=ibus" puis "eclipse"), puis :

  • Créer le "Java Project" ReseauDeNeurones
  • Créer votre première classe LectureImage comme suit:
    1. File -> New Java Class
    2. Une fenêtre de dialogue s'ouvre et il faut la compléter
    3. Créer le package image à ce moment là (champ vide vers le haut de la fenêtre)
    4. Sélectionner la case pour la création d'un main (très important; à faire pour une seule classe)

Dans ce TP nous avons créé un premier package qui s'appelle image mais qui pour l'instant ne sert pas. Attention, le nom de la classe, ici LectureImage, doit être exactement le même dans votre code et dans le nom de la classe créée.

Lorsque vous compilerez votre code, plus bas, pensez bien à choisir Java Application et non Java Applet. Un clic gauche sur le bouton à droite de "compile and run" (bouton play vert) permet de choisir "run as"->"Java Application).

De plus, pensez à fermer les autres projets Java actuellement ouverts, si il y en a (clic droit sur le projet à fermer, puis "close project").

Avant de compiler votre code, il faut au préalable télécharger les fichiers nécessaires et paramètrer eclipse. Commencez par télécharger les fichiers suivants

Le premier fichier jar est une librarie qu'il faut intégrer à notre projet. Effectuer la manipulation suivante :

  • clic droit sur le projet, puis sélectionner Properties
  • Aller dans la partie Java Build Path* puis l'onglet ''Librairies''
  • Puis sélectionner "Modulepath" et faites add external jars
  • Ajouter alors le fichier mnist-tools.jar

Les deux autres fichiers seront à utiliser dans le programme, il faudra les décompresser d'abord et bien noter où ils sont pour pouvoir remplir le programme.

Complétez maintenant la classe grâce à l'exemple de code donné plus bas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package image;
import mnisttools.MnistReader;

public class LectureImage {

    public static void main(String[] args) {
        String path="..."; // TODO : indiquer le chemin correct
        String labelDB=path+"train-labels.idx1-ubyte";
        String imageDB=path+"train-images.idx3-ubyte";
        // Creation de la base de donnees
        MnistReader db = new MnistReader(labelDB, imageDB);
        // Acces a la premiere image
        int idx = 1; // une variable pour stocker l'index
                    // Attention la premiere valeur est 1.
        int [][] image = db.getImage(idx); /// On recupere la premiere l'image numero idx
        // Son etiquette ou label
        int label = db.getLabel(idx);
        // Affichage du label
        System.out.print("Le label est "+ label+"\n"); 
        // note: le caractère \n est le 'retour charriot' (retour a la ligne).
        // Affichage du nombre total d'image
        System.out.print("Le total est "+ db.getTotalImages()+"\n");
        /* A vous de jouer pour la suite */
    }
}
Premières manipulations d'une image
  • Comment connaître la taille de l'image ?
  • Programmer dans votre classe LectureImage un parcours de chaque pixel de l'image. Ce parcours se fera de gauche à droite et de haut en bas. A chaque pixel, afficher la valeur de l'entier.
  • Modifier votre parcours de l'image pour calculer également la valeur minimum et maximum des pixels par colonne et par ligne.
Binarisation

Comme premier traitement d'image, nous allons ''binariser'' l'image. Pour cela, il faut choisir un seuil sur le niveau de gris puis remplacer la valeur entière par 1 si le niveau de gris est supérieur à ce seuil, et 0 sinon.

  • Programmer la binarisation de l'image
  • Ajouter un affichage console qui affiche un espace lorsque la valeur est 0 et un ''X'' lorsqu'elle est à 1. Vous devriez être capable de reconnaître l'image avec cet affichage.

Vous pouvez aussi visualiser le jeu de données ici.

Création d'une fonction

Pour plus d'efficacité, nous allons créer une fonction qui effectue la binarisation. Cette fonction devra être dans la classe ''LectureImage'' comme dans l'exemple qui suit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package image;
import mnisttools.MnistReader;

public class LectureImage {

    // Fonction qui binarise une image
    // Attention l'image est modifiee.
    // la variable seuil doit etre entre 0 et 255 (a tester)
    public static int[][] binarisation(int[][] image, int seuil){
        // ecrire la fonction ici
    }

    public static void main(String[] args) {
        // ...
    }

}

En java, une fonction se déclare et se définit en même temps. La signature contient:

  • public static : à voir en cours de java
  • int[][] : la valeur de retour
  • binarisation: son nom
  • int[][] image : la liste des paramètres, ici il y en deux l'image à modifier et le seuil.
Pour aller plus loin

Si vous avez fini, vous pouvez faire le travail suivant. Commencer par créer une fonction qui compte le nombre de pixel(s) allumé(s) sur chaque ligne d'une image binarisée. Ensuite extraire pour chaque image, quel est le nombre maximum de pixel(s) allumé(s) sur une ligne. En regardant les images correspondant au chiffre 1 et au chiffre 8, êtes-vous capable de trouver une façon séparer les 1 des 8 à l'aide de la quantité calculée au préalable ?

TP semaine du 23/09/2019 : Perceptron et classification binaire

Pour ce TP nous allons coder le modèle du perceptron en java et commencer à classifier des données "jouets" afin de prendre en main le problème progressivement.

Pour ce TP, nous laissons de côté les images et allons ainsi commencer la réalisation d'un neurone artificiel, en java, sur des données en 2D, c'est-à-dire des points dans le plan. Le code qui sera réalisé est indépendant du type de données, nous allons donc créer un nouveau package, toujours dans le projet ''ReseauDeNeurones'', nommé ''perceptron''. (clic droit sur ReseauDeNeurones -> New -> Class -> nommer le package perceptron et nommer la classe OnlinePerceptron).

En toute généralité, nous allons considérer un ensemble d'apprentissage composé de paires (observation, référence). L'observation est un vecteur de nombres réels qui représente l'objet à classer, alors que la référence (label ou étiquette) est un entier qui représente la classe à prédire (la "bonne réponse"), qui peut être ici +1 ou -1.

L'algorithme d'apprentissage du perceptron '''online''' procède selon la hiérarchie suivante: * l'apprentissage est un ensemble d' ''époques'' * lors d'une époque, chaque exemple d'apprentissage est considéré une fois et une seule. * le traitement d'un exemple (un point de donnée) d'apprentissage consiste en : * l'inférence du modèle (examiner la réponse donnée par le modèle), * la mise à jour des paramètres (vecteur w) du modèle, si l'exemple s'avère mal classé.

Introduction

La dimension de l'espace de représentation est une constante entière. Pour représenter une observation (un vecteur, noté x dans le cours), nous allons utiliser un tableau de réels, tout comme pour le vecteur (w) qui regroupe les paramètres du modèle. Une étiquette (notée y) est représentée par un entier.

Voici un modèle pour commencer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package perceptron;

import java.util.Arrays;

public class OnlinePerceptron  {
        public static final int DIM = 3; // dimension de l'espace de representation
        public static float[] w = new float[DIM]; // parametres du modèle
        public static float[][] data = { // les observations
          {1,0,0}, {1,0,1} , {1,1,0},
          {1,1,1}
        };
    public static int[] refs = {-1, -1, -1, 1}; // les references

    public static void main(String[] args) {
        // Exemple de boucle qui parcourt tous les exemples d'apprentissage
        // pour en afficher a chaque fois l'observation et la reference.
        for (int i = 0; i < data.length; i++) {
            float[] x = data[i];
            System.out.println("x= "+Arrays.toString(x)+ " / y = "+refs[i]);
        }
    }

}
  • Pourquoi l'espace de représentation est-il de dimension 3 alors que nous sommes en deux dimensions ?
  • Pourquoi la première coordonnée de data est toujours égale à 1 ?
Partie 1

A l'aide de votre cours, établir la liste des fonctions à écrire afin de mettre en oeuvre l'apprentissage des paramètres du modèle. Pour chaque fonction, il faut spécifier:

  • son nom
  • son type de retour
  • la liste des arguments d'entrée
  • un commentaire expliquant ce qui est fait

Une fois votre liste faite, assurez-vous que vous êtes capable d'écrire à partir de cette liste de fonctions, le ''main'' de l'application qui effectue cet apprentissage et retourne le vecteur de paramètres.

Partie 2
  • Réaliser en Java les différentes fonctions ainsi que le main.
  • En combien d'époques converge le perceptron, en partant du vecteur initial w:(3.5,-1.5,10.0) pour les paramètres ? (vous pouvez aussi choisir d'initialiser par le vecteur nul, w:(0,0,0))
  • Initialisez w à partir de nombres aléatoires uniformément pris dans l'intervalle [0,1], est-ce que l'algorithme finit toujours pas converger ? (trouver une séparatrice)
  • Vérifiez sur une feuille l'allure de la droite obtenue (tracer la droite) et que c'est bien une solution.
  • En utilisant w: (-4,1,1) comme vecteur initial pour les paramètres, en combien de temps converge l'algorithme ?
  • En considérant le nouveau jeu de données ci-dessous, que se passe-t-il et pourquoi ?

Jeu de données :

1
2
3
4
5
public static float[][] data = { // les observations
    {1,0,0}, {1,0,1} , {1,1,0},
    {1,1,1}
};
public static int[] refs = {1, -1, -1, 1}; // les references
Pour aller plus loin

On peut regarder comment la séparatrice évolue à chaque mise à jour. Reprenons pour cela le premier jeu de données. Il faudra donc

  • identifier les paramètres de la séparatrice à partir du vecteur w
  • afficher les coordonnées x, y de deux points (par exemple prendre x=-1 et x=+2) à chaque mise à jour (le mieux est de sauter deux ligne entre chaque affichage).

Exemple de fichier

1
2
3
4
5
6
-1 0
2 1


-1 2
2 2
  • recopier les coordonnées affichées dans la console dans un fichier (par exemple : sep.d).
  • utiliser le programme gnuplot (gnuplot dans un terminal) et utiliser la commande plot for[i=1:100] "sep.d" index i w lp

Si vous voulez afficher les données sur le même plot, il faut utiliser la commande set object circle at x,y size 0.1 en remplaçant x et y par les valeurs voulues.

NEW : si besoin vous pouvez utiliser le squelette de programme suivant : OnlinePerceptron.java. Pensez bien à renommer le fichier.

TP semaine du 30/09/2019 : Classification d'images

Dans ce TP nous allons appliquer l'algorithme du perceptron au cas de la classification d'images. Avant toute chose, assurer vous bien que le perceptron codé la semaine précédente peut s'adapter à des données de dimension quelconque (il faudra vérifier que aucune des fonctions ne soit spécifique au cas à deux dimensions).

Pour commencer

Pour ce TP on commencera par écrire un morceau de code permettant de charger les images de la base de données MNIST. Commencer par rajouter la classe ImageOnlinePerceptron à votre package perceptron. Ensuite, commencer le travail à partir du code suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package perceptron;

import java.util.Random;
import mnisttools.MnistReader;

public class ImageOnlinePerceptron {

    /* Les donnees */
    public static String path="";
    public static String labelDB=path+"train-labels-idx1-ubyte";
    public static String imageDB=path+"train-images-idx3-ubyte";

    /* Parametres */
    // Na exemples pour l'ensemble d'apprentissage
    public static final int Na = 1000; 
    // Nv exemples pour l'ensemble d'évaluation
    public static final int Nv = 1000; 
    // Nombre d'epoque max
    public final static int EPOCHMAX=40;
    // Classe positive (le reste sera considere comme des ex. negatifs):
    public static int  classe = 5 ;

    // Générateur de nombres aléatoires
    public static int seed = 1234;
    public static Random GenRdm = new Random(seed);

    /*
    *  BinariserImage : 
    *      image: une image int à deux dimensions (extraite de MNIST)
    *      seuil: parametre pour la binarisation
    *
    *  on binarise l'image à l'aide du seuil indiqué
    *
    */
    public static int[][] BinariserImage(int[][] image, int seuil) {
            // TODO
    }

    /*
    *  ConvertImage : 
    *      image: une image int binarisée à deux dimensions
    *
    *  1. on convertit l'image en deux dimension dx X dy, en un tableau unidimensionnel de tail dx.dy
    *  2. on rajoute un élément en première position du tableau qui sera à 1
    *  La taille finale renvoyée sera dx.dy + 1
    *
    */
    public static float[] ConvertImage(int[][] image) {
            // TODO
    }

    /*
    *  InitialiseW :
    *      sizeW : la taille du vecteur de poids
    *      alpha : facteur à rajouter devant le nombre aléatoire
    *
    *  le vecteur de poids est crée et initialisé à l'aide d'un générateur
    *  de nombres aléatoires.
    */
    public static float[] InitialiseW(int sizeW, float alpha) {
            // TODO
    }


    public static void main(String[] args) {
        System.out.println("# Load the database !");
        /* Lecteur d'image */ 
        MnistReader db = new MnistReader(labelDB, imageDB);

        /* Creation des donnees */
        System.out.println("# Build train for digit "+ classe);
        /* Tableau où stocker les données */
        // TODO
    }
}

Pour la partie d'initialisation, vous aurez les tâches suivantes à implémenter

  1. Récupérer la dimension des données afin d'initialiser le tableau où elles seront stockées. Le tableau aura la taille \(N_a \times (D+1)\), attention, ici \(D\) correpond à la dimension une fois l'image convertie au format unidimensionel).
  2. Compléter la fonction BinariserImage:
    • on va binariser l'image (0/1) avec le seuil passer en argument
  3. Compléter la fonction ConvertImage:
    • convertir l'image sur une seule dimension
    • rajouter un élément à 1 sur la première case (pourquoi ?)
  4. Initialiser un tableau float[][] trainData avec les Na premières images
  5. Initialiser un tableau int[] trainRefs, avec les références correctement ajuster pour la classe voulu (+1 pour la bonne classe, -1 pour le reste)
  6. Implémenter la fonction InitialiseW qui créé un vecteur de poids et l'initialise aléatoirement (voir la variables GenRdm).

Vérifier que vous arrivez à faire marcher correctement votre perceptron.

Ensemble de Validation

On veut maintenant utiliser l'ensemble de validation pour savoir comment fixer le nombre maximum d'itération et le taux d'apprentissage.

Si vous n'avez pas mis de taux d'apprentissage dans l'algorithme du perceptron, c'est le moment de le rajouter. Ajouter à votre perceptron une variable (passée en paramètre) \(\eta\) (eta). Elle sera à rajouter lors de la mise à jour des paramètres du perceptron

$$ \begin{pmatrix} w_0 \\ w_1 \\ ... \\ w_D \end{pmatrix} \leftarrow \begin{pmatrix} w_0 \\ w_1 \\ ... \\ w_D \end{pmatrix} + \eta \tilde{y} \begin{pmatrix} 1 \\ x_1 \\ ... \\ x_D \end{pmatrix} $$

Ensuite, vous devez

  1. Modifier le début du main pour charger \(N_v\) exemples de validation. Vous ferez attention à ce qu'il soit différent des exemples d'apprentissage et vous metterez les données dans un tableau validData et validRefs
  2. Rajouter une fonction qui calcul le nombre d'erreurs de classification. Attention : ici il ne faudra pas changer les paramètres du perceptron
  3. Ecrire une boucle permettant de : Effectuer une époque, calculer le nombre d'erreurs sur les ensembles d'apprentissage et de test.
Courbe

Une fois que le travail précédent est fait, vous pouvez tracer les courbes suivantes :

  • le nombre d'exemples mal classés sur l'ensemble d'apprentissage et sur l'ensemble de test en fonction du nombre d'époques pour \(\eta=0.1,0.2,0.5,1\) .
  • le nombre de faux positifs et de faux négatifs sur les deux ensembles à chaque itération pour \(\eta=0.1,1\)

Pour visualiser les courbes, vous pouvez écrire dans un fichier les données à l'aide de la classe FileWriter:

1
2
3
4
FileWriter fw = new FileWriter("test.d");
fw.write(""+trainData[100][0]+"\n");
fw.write("nouvelle ligne\n");
fw.close();

et charger les données dans Excel ou utiliser gnuplot.

TP semaine du 07/10/2019 : Classification multi-classe

Tout d'abord, il est important d'avoir fini le TP précédent avant de passer à la suite. Ensuite il est normal de ne pas finir ce TP en une seule séance.

Pour ce TP, nous allons mettre en oeuvre un classifieur multi-classe qui peut associer à une image le chiffre qu'elle représente. Pour cela nous allons adopter la modélisation probabiliste vue en cours. Il faudra donc pour chacune des dix classes considérer un ensemble de poids \(w_{ij}\) où i correspond à un pixel de l'image et où j correspond à la classe. Pour chaque époque, on passera en revue toutes les images de l'ensemble d'apprentissage et on mettra à jour les poids du réseau en utilisant la montée de gradient pour une fonction de coût de type "vraisemblance" (ou likelihood). Pour classifier le jeu de données, à chaque image on attribuera la classe pour laquelle sa probabilité (donnée par la fonction softmax) sera la plus grande.

Commencer par créer une nouvelle classe ''perceptronMulti' dans votre package perceptron. Cette classe contiendra le code du perceptron multiclasse. Pour cette classe il faudra effectuer créer les méthodes suivantes

  1. une méthode OneHot, qui prend en entrée une étiquette (ici un entier dans [0,9]) et renvoie en sortie un vecteur d'entiers, avec la i-ème composante correspondant au numero de l'étiquette à 1 et le reste à 0.
  2. une méthode InfPerceptron, qui prend en entrée une donnée, tous les poids du perceptron et renvoie un vecteur de float contenant la probabilité d'appartenir à une classe. Ici vous utiliserez la formule du cours (ici pour le neurone \(l\)) :
    $$ y_l = \frac{\exp\left(\sum_{i=1}^D x_i w_{il} + w_{0l}\right)}{\sum_k \exp\left(\sum_{i=1}^D x_i w_{ik} + w_{0k}\right)} $$
    Il peut être utile ici de faire une méthode qui calcule le produit scalaire \(\sum_{i=1}^D x_i w_{il} + w_{0l}\).
  3. une méthode qui initialise les poids du perceptron de façon aléatoire (cf le TP précédent). Ici vous utiliserez 1/D comme valeur pour \(\alpha\).
  4. une méthode qui effectue la mise à jour des poids du perceptron. Je vous rappelle qu'ici, il faudra implémenter la formule suivante (où \(p_l\) est le OneHot vecteur de la donnée correspondante):
$$ w_{il} \leftarrow w_{il} - x_i \eta \left( y_l - p_l \right) \\ w_{0l} \leftarrow w_{0l} - \eta \left( y_l - p_l \right) $$

Avant de continuer il est important de vérifier que chacune de ces fonctions marchent. Vérifier si les tests ci-dessous marchent:

  • Donner en entrée à la fonction OneHot toutes les étiquettes possibles et vérifier la sortie
  • Vérifier que tous les éléments du vecteur de sortie de la fonction InfPerceptron sont positifs ou nuls, et vérifier que la somme des éléments est égal à 1.
  • Pour la mise à jour des paramètres, contrôler bien les indices des variables.

Une fois ces fonctions correctement implémentées, vous n'aurez plus qu'à faire la méthode "epoque". Une grande différence par rapport au perceptron précédent est que la mise à jour des poids s'effectue à chaque itération.

Chargement des données

Il faut maintenant charger les données. Soit dans une nouvelle classe (ImageOnlinePerceptronMulti) soit dans le main (déconseillé), vous vous inspirerez du TP du 30/09 afin de récupérer \(N_a\) données d'apprentissage et \(N_v\) données de validation. La différence avec le cas binaire est que cette fois-ci vous devrez bien récupérer l'étiquette de chaque donnée (un entier entre 0 et 9) qui vous servira à faire l'apprentissage.

Testez votre perceptron

Mettez en place la boucle permettant de mettre en oeuvre un certain nombre d'époques.

TP semaine du 14/10/2019 : Classification multi-classe (suite)

Avant de procéder, il faudra avoir entièrement fini la partie sur le perceptron multiclasse, ce TP va se baser sur les résultats obtenus à partir du perceptron. Le travail demander pour aujourd'hui est le suivant. Tout d'abord du code :

  1. Ecrire une fonction CostFunction qui prend en paramètre les poids du perceptron et un ensemble d'images et calcul la valeur de la fonction de coût.

On rappelle ici la définition, pour une donnée \(\vec{x}\):

$$ e(\vec{x}) = \prod_l y_l(\vec{x})^{p_l} $$

\(p_l\) correspond à la \(l\)-ième composante du vecteur renvoyé par la fonction OneHot. La fonction de coût totale sera calculée en moyennant la contribution sur toutes les données passées en argument (\(E_{\rm Total}=M^{-1}\sum_{m=1^M} \log\left[ e(\vec{x}^{(m)}) \right]\)), où \(M\) est le nombre d'images.

Passons maintenant à l'utilisation du perceptron. Tout d'abord, vous choisirez pour commencer \(N_a=5000\) et \(N_v=1000\).

  1. Faites varier le taux d'apprentissage \(\alpha\) de votre code. En affichant la courbe du nombre d'erreur en fonction du nombre d'époque (ainsi que la valeur de la fonction de cout) à la fois sur l'ensemble d'apprentissage et de validation trouvez la meilleure valeur de \(\alpha\) dans l'intervalle \([0.005,0.5]\). Pour la suite on utilisera cette valeur de \(\alpha\).
  2. En maintenant \(N_v=1000\), faites varier \(N_a\) de 100 jusqu'à 10000 et observer les performances sur le nombre d'erreurs commises. Indiquer quelle est le bon nombre ed données à utiliser dans cet exemple. Pour la suite, vous prendrez vos meilleurs paramètres pour \(\alpha\) et \(N_a\).
  3. Entrainer votre perceptron. Sur l'ensemble de validation, calculer le profil moyen pour chaque classe : pour chaque classe, vous regardez quelles soont les données positivement classifiées et calculer l'image moyenne sur ces données. Du code java est fourni plus bas pour expliquer comment enregistrer une image.
  4. Regarder l'allure des 10 images les moins bien classées. Pour savoir si une image est classée avec certitude par le percetron il faut regarder la réponse donnée par l'inférence. Donc, il faut d'abord ne considérer que les images correctement classées, et ensuite choisir parmi cells-ci, les 10 qui ont la probabilité \(y_l(\vec{x})\) (donnée par le perceptron) la plus faible.
  5. Calculer la matrice de confusion, à la fois pour l'ensemble d'entraînement et de validation. Quelles sont les erreurs les plus fréquentes ?
Code pour sauver une image
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// chemin d'acces
String path="";

// On récupère une image
int [][] image = db.getImage(idx);

// On la sauvegarde
int numberOfColumns = 28;//image.length;
int numberOfRows = 28; //image[0].length;
BufferedImage bimage = new BufferedImage(numberOfColumns, numberOfRows, BufferedImage.TYPE_BYTE_GRAY);

for(int i=0; i<28; i++) {
    for(int j=0; j<28; j++) {
         int c = image[i][j]; // ici 0 pour noir, 255 pour blanc
         int rgb = new Color(c,c,c).getRGB();
         bimage.setRGB(j,i,rgb);x
    }
}

// enregistrement
File outputfile = new File(path+"imagesChiffreManuscrit.png");
ImageIO.write(bimage, "png", outputfile);

.

Faire un graphe en gnuplot

Dans un fichier, vous enregistrez les différentes valeurs de la façon qui suit

1
2
3
4
5
1 100 80
2 50 70
3 45 50
...
100 10 20

Ensuite, ouvrez gnuplot et tapez les commandes suivantes pour afficher les deux courbes sur le même graphe

  • plot 'data.d' using 1:2
  • replot 'data.d' using 1:3

Définition de la matrice de confusion

Voici (finalement) la définition de la matrice de confusion.

  • Sur la diagonale \(i\), on compte le nombre d'éléments bien classés pour la classe \(i\).
  • Sur la ligne de la classe \(i\), vous compterez les éléments appartenant à la classe \(j\) (indice de colonne) et classés dans \(i\) par le perceptron. Ici on regarde donc pour quels classes le perceptron \(i\) se trompent.
  • Automatiquement : sur la colonne de la classe \(i\), on peut observer par quels perceptrons les éléments de la classe \(i\) sont classifiés.