cnn/export.c: Ajout de visual-propagation

Cette option permet de visualiser l'état des différentes couches
après propagation sur une image donnée
(fonctionne uniquement sur des datasets MNIST pour le moment)
This commit is contained in:
augustin64 2023-05-08 18:53:04 +02:00
parent af288166d6
commit 7c2c911976
4 changed files with 213 additions and 12 deletions

View File

@ -1,15 +1,32 @@
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <math.h>
#include "include/free.h" #include "include/backpropagation.h"
#include "include/struct.h"
#include "../include/colors.h"
#include "include/neuron_io.h" #include "include/neuron_io.h"
#include "../include/colors.h"
#include "../include/mnist.h"
#include "include/struct.h"
#include "include/jpeg.h"
#include "include/free.h"
#include "include/cnn.h"
void help(char* call) { void help(char* call) {
printf("Usage: %s ( print-poids-kernel-cnn ) [OPTIONS]\n\n", call); printf("Usage: %s ( print-poids-kernel-cnn | visual-propagation ) [OPTIONS]\n\n", call);
printf("OPTIONS:\n");
printf("\tprint-poids-kernel-cnn\n");
printf("\t\t--modele | -m [FILENAME]\tFichier contenant le réseau entraîné\n");
printf("\tvisual-propagation\n");
printf("\t\t--modele | -m [FILENAME]\tFichier contenant le réseau entraîné\n");
printf("\t\t--images | -i [FILENAME]\tFichier contenant les images.\n");
printf("\t\t--numero | -n [numero]\tNuméro de l'image dont la propagation veut être visualisée\n");
printf("\t\t--out | -o [BASE_FILENAME]\tLes images seront stockées dans ${out}_layer-${numéro de couche}_feature-${kernel_numero}.jpeg\n");
printf("\n");
printf_warning("Seul les datasets de type MNIST sont pris en charge pour le moment\n");
} }
@ -65,6 +82,87 @@ void print_poids_ker_cnn(char* modele) {
} }
void write_image(float** data, int width, char* base_filename, int layer_id, int kernel_id) {
int filename_length = strlen(base_filename) + (int)log10(layer_id+1)+1 + (int)log10(kernel_id+1)+1 + 21;
char* filename = (char*)malloc(sizeof(char)*filename_length);
sprintf(filename, "%s_layer-%d_feature-%d.jpeg", base_filename, layer_id, kernel_id);
imgRawImage* image = (imgRawImage*)malloc(sizeof(imgRawImage));
image->numComponents = 3;
image->width = width;
image->height = width;
image->lpData = (unsigned char*)malloc(sizeof(unsigned char)*width*width*3);
for (int i=0; i < width; i++) {
for (int j=0; j < width; j++) {
float color = fmax(fmin(data[i][j], 1.), 0.)*255;
image->lpData[(i*width+j)*3] = color;
image->lpData[(i*width+j)*3 + 1] = color;
image->lpData[(i*width+j)*3 + 2] = color;
}
}
storeJpegImageFile(image, filename);
free(image->lpData);
free(image);
free(filename);
}
void visual_propagation(char* modele_file, char* images_file, char* out_base, int numero) {
Network* network = read_network(modele_file);
int* mnist_parameters = read_mnist_images_parameters(images_file);
int*** images = read_mnist_images(images_file);
int nb_elem = mnist_parameters[0];
int width = mnist_parameters[1];
int height = mnist_parameters[2];
free(mnist_parameters);
if (numero < 0 || numero >= nb_elem) {
printf_error("Numéro d'image spécifié invalide.");
printf(" Le fichier contient %d images.\n", nb_elem);
exit(1);
}
// Forward propagation
write_image_in_network_32(images[numero], height, width, network->input[0][0], false);
forward_propagation(network);
for (int i=0; i < network->size-1; i++) {
if (i == 0) {
write_image(network->input[0][0], width, out_base, 0, 0);
} else {
if ((!network->kernel[i]->cnn)&&(!network->kernel[i]->nn)) {
for (int j=0; j < network->depth[i]; j++) {
write_image(network->input[i][j], network->width[i], out_base, i, j);
}
} else if (!network->kernel[i]->cnn) {
// Couche de type NN, on n'affiche rien
} else {
write_image(network->input[i][0], network->width[i], out_base, i, 0);
}
}
}
free_network(network);
for (int i=0; i < nb_elem; i++) {
for (int j=0; j < width; j++) {
free(images[i][j]);
}
free(images[i]);
}
free(images);
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
if (argc < 2) { if (argc < 2) {
@ -92,6 +190,50 @@ int main(int argc, char* argv[]) {
print_poids_ker_cnn(modele); print_poids_ker_cnn(modele);
return 0; return 0;
} }
if (! strcmp(argv[1], "visual-propagation")) {
char* modele = NULL; // Fichier contenant le modèle
char* images = NULL; // Dossier contenant les images
char* out_base = NULL; // Préfixe du nom de fichier de sortie
int numero = -1; // Numéro de l'image dans le dataset
int i = 2;
while (i < argc) {
if ((! strcmp(argv[i], "--modele"))||(! strcmp(argv[i], "-m"))) {
modele = argv[i+1];
i += 2;
} else if ((! strcmp(argv[i], "--images"))||(! strcmp(argv[i], "-i"))) {
images = argv[i+1];
i += 2;
} else if ((! strcmp(argv[i], "--out"))||(! strcmp(argv[i], "-o"))) {
out_base = argv[i+1];
i += 2;
} else if ((! strcmp(argv[i], "--numero"))||(! strcmp(argv[i], "-n"))) {
numero = strtol(argv[i+1], NULL, 10);
i += 2;
} else {
printf_warning("Option choisie inconnue: ");
printf("%s\n", argv[i]);
i++;
}
}
if (!modele) {
printf_error("Pas de modèle à utiliser spécifié.\n");
return 1;
}
if (!images) {
printf_error("Pas de fichier d'images spécifié.\n");
return 1;
}
if (!out_base) {
printf_error("Pas de fichier de sortie spécifié.\n");
return 1;
}
if (numero == -1) {
printf_error("Pas de numéro d'image spécifié.\n");
return 1;
}
visual_propagation(modele, images, out_base, numero);
return 0;
}
printf_error("Option choisie non reconnue: "); printf_error("Option choisie non reconnue: ");
printf("%s\n", argv[1]); printf("%s\n", argv[1]);
help(argv[0]); help(argv[0]);

View File

@ -33,6 +33,11 @@ typedef struct jpegDataset {
*/ */
imgRawImage* loadJpegImageFile(char* lpFilename); imgRawImage* loadJpegImageFile(char* lpFilename);
/*
* Write a JPEG image to lpFilename
*/
int storeJpegImageFile(struct imgRawImage* lpImage, char* lpFilename);
/* /*
* Load a complete dataset from its path * Load a complete dataset from its path
*/ */

View File

@ -15,17 +15,19 @@ typedef struct Kernel_cnn {
int k_size; // k_size = dim_input - dim_output + 1 int k_size; // k_size = dim_input - dim_output + 1
int rows; // Depth de l'input int rows; // Depth de l'input
int columns; // Depth de l'output int columns; // Depth de l'output
float*** bias; // bias[columns][dim_output][dim_output] float*** bias; // bias[columns][dim_output][dim_output]
float*** d_bias; // d_bias[columns][dim_output][dim_output] float*** d_bias; // d_bias[columns][dim_output][dim_output]
#ifdef ADAM_CNN_BIAS #ifdef ADAM_CNN_BIAS
float*** s_d_bias; // s_d_bias[columns][dim_output][dim_output] float*** s_d_bias; // s_d_bias[columns][dim_output][dim_output]
float*** v_d_bias; // v_d_bias[columns][dim_output][dim_output] float*** v_d_bias; // v_d_bias[columns][dim_output][dim_output]
#endif #endif
float**** weights; // weights[rows][columns][k_size][k_size] float**** weights; // weights[rows][columns][k_size][k_size]
float**** d_weights; // d_weights[rows][columns][k_size][k_size] float**** d_weights; // d_weights[rows][columns][k_size][k_size]
#ifdef ADAM_CNN_WEIGHTS #ifdef ADAM_CNN_WEIGHTS
float**** s_d_weights; // s_d_weights[rows][columns][k_size][k_size] float**** s_d_weights; // s_d_weights[rows][columns][k_size][k_size]
float**** v_d_weights; // v_d_weights[rows][columns][k_size][k_size] float**** v_d_weights; // v_d_weights[rows][columns][k_size][k_size]
#endif #endif
} Kernel_cnn; } Kernel_cnn;
@ -33,23 +35,26 @@ typedef struct Kernel_nn {
// Noyau ayant une couche vectorielle en sortie // Noyau ayant une couche vectorielle en sortie
int size_input; // Nombre d'éléments en entrée int size_input; // Nombre d'éléments en entrée
int size_output; // Nombre d'éléments en sortie int size_output; // Nombre d'éléments en sortie
float* bias; // bias[size_output] float* bias; // bias[size_output]
float* d_bias; // d_bias[size_output] float* d_bias; // d_bias[size_output]
#ifdef ADAM_DENSE_BIAS #ifdef ADAM_DENSE_BIAS
float* s_d_bias; // s_d_bias[size_output] float* s_d_bias; // s_d_bias[size_output]
float* v_d_bias; // v_d_bias[size_output] float* v_d_bias; // v_d_bias[size_output]
#endif #endif
float** weights; // weight[size_input][size_output] float** weights; // weight[size_input][size_output]
float** d_weights; // d_weights[size_input][size_output] float** d_weights; // d_weights[size_input][size_output]
#ifdef ADAM_DENSE_WEIGHTS #ifdef ADAM_DENSE_WEIGHTS
float** s_d_weights; // s_d_weights[size_input][size_output] float** s_d_weights; // s_d_weights[size_input][size_output]
float** v_d_weights; // v_d_weights[size_input][size_output] float** v_d_weights; // v_d_weights[size_input][size_output]
#endif #endif
} Kernel_nn; } Kernel_nn;
typedef struct Kernel { typedef struct Kernel {
Kernel_cnn* cnn; // NULL si ce n'est pas un cnn Kernel_cnn* cnn; // NULL si ce n'est pas un cnn
Kernel_nn* nn; // NULL si ce n'est pas un nn Kernel_nn* nn; // NULL si ce n'est pas un nn
int activation; // Id de la fonction d'activation et -Id de sa dérivée int activation; // Id de la fonction d'activation et -Id de sa dérivée
int linearisation; // 1 si c'est la linéarisation d'une couche, 0 sinon int linearisation; // 1 si c'est la linéarisation d'une couche, 0 sinon
int pooling; // 0 si pas pooling, 1 si average_pooling, 2 si max_pooling int pooling; // 0 si pas pooling, 1 si average_pooling, 2 si max_pooling
@ -60,10 +65,13 @@ typedef struct Network{
int dropout; // Probabilité d'abandon d'un neurone dans [0, 100] (entiers) int dropout; // Probabilité d'abandon d'un neurone dans [0, 100] (entiers)
float learning_rate; // Taux d'apprentissage du réseau float learning_rate; // Taux d'apprentissage du réseau
int initialisation; // Id du type d'initialisation int initialisation; // Id du type d'initialisation
int max_size; // Taille du tableau contenant le réseau int max_size; // Taille du tableau contenant le réseau
int size; // Taille actuelle du réseau (size ≤ max_size) int size; // Taille actuelle du réseau (size ≤ max_size)
int* width; // width[size] int* width; // width[size]
int* depth; // depth[size] int* depth; // depth[size]
Kernel** kernel; // kernel[size], contient tous les kernels Kernel** kernel; // kernel[size], contient tous les kernels
float**** input_z; // Tableau de toutes les couches du réseau input_z[size][couche->depth][couche->width][couche->width] float**** input_z; // Tableau de toutes les couches du réseau input_z[size][couche->depth][couche->width][couche->width]
float**** input; // input[i] = f(input_z[i]) où f est la fonction d'activation de la couche i float**** input; // input[i] = f(input_z[i]) où f est la fonction d'activation de la couche i

View File

@ -74,6 +74,52 @@ imgRawImage* loadJpegImageFile(char* lpFilename) {
return lpNewImage; return lpNewImage;
} }
int storeJpegImageFile(imgRawImage* lpImage, char* lpFilename) {
struct jpeg_compress_struct info;
struct jpeg_error_mgr err;
unsigned char* lpRowBuffer[1];
FILE* fHandle;
fHandle = fopen(lpFilename, "wb");
if(fHandle == NULL) {
#ifdef DEBUG
fprintf(stderr, "%s:%u Failed to open output file %s\n", __FILE__, __LINE__, lpFilename);
#endif
return 1;
}
info.err = jpeg_std_error(&err);
jpeg_create_compress(&info);
jpeg_stdio_dest(&info, fHandle);
info.image_width = lpImage->width;
info.image_height = lpImage->height;
info.input_components = 3;
info.in_color_space = JCS_RGB;
jpeg_set_defaults(&info);
jpeg_set_quality(&info, 100, TRUE);
jpeg_start_compress(&info, TRUE);
/* Write every scanline ... */
while(info.next_scanline < info.image_height) {
lpRowBuffer[0] = &(lpImage->lpData[info.next_scanline * (lpImage->width * 3)]);
jpeg_write_scanlines(&info, lpRowBuffer, 1);
}
jpeg_finish_compress(&info);
fclose(fHandle);
jpeg_destroy_compress(&info);
return 0;
}
jpegDataset* loadJpegDataset(char* folderPath) { jpegDataset* loadJpegDataset(char* folderPath) {
jpegDataset* dataset = (jpegDataset*)malloc(sizeof(jpegDataset)); jpegDataset* dataset = (jpegDataset*)malloc(sizeof(jpegDataset));
imgRawImage* image; imgRawImage* image;