diff --git a/src/cnn/export.c b/src/cnn/export.c index 5a2d166..e95b426 100644 --- a/src/cnn/export.c +++ b/src/cnn/export.c @@ -1,15 +1,32 @@ +#include #include #include #include +#include -#include "include/free.h" -#include "include/struct.h" -#include "../include/colors.h" +#include "include/backpropagation.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) { - 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[]) { if (argc < 2) { @@ -92,6 +190,50 @@ int main(int argc, char* argv[]) { print_poids_ker_cnn(modele); 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("%s\n", argv[1]); help(argv[0]); diff --git a/src/cnn/include/jpeg.h b/src/cnn/include/jpeg.h index 42120d2..166215b 100644 --- a/src/cnn/include/jpeg.h +++ b/src/cnn/include/jpeg.h @@ -33,6 +33,11 @@ typedef struct jpegDataset { */ imgRawImage* loadJpegImageFile(char* lpFilename); +/* +* Write a JPEG image to lpFilename +*/ +int storeJpegImageFile(struct imgRawImage* lpImage, char* lpFilename); + /* * Load a complete dataset from its path */ diff --git a/src/cnn/include/struct.h b/src/cnn/include/struct.h index 1f04c43..2e04481 100644 --- a/src/cnn/include/struct.h +++ b/src/cnn/include/struct.h @@ -15,17 +15,19 @@ typedef struct Kernel_cnn { int k_size; // k_size = dim_input - dim_output + 1 int rows; // Depth de l'input int columns; // Depth de l'output + float*** bias; // bias[columns][dim_output][dim_output] float*** d_bias; // d_bias[columns][dim_output][dim_output] #ifdef ADAM_CNN_BIAS - 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*** s_d_bias; // s_d_bias[columns][dim_output][dim_output] + float*** v_d_bias; // v_d_bias[columns][dim_output][dim_output] #endif + float**** weights; // weights[rows][columns][k_size][k_size] float**** d_weights; // d_weights[rows][columns][k_size][k_size] #ifdef ADAM_CNN_WEIGHTS - 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**** 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] #endif } Kernel_cnn; @@ -33,23 +35,26 @@ typedef struct Kernel_nn { // Noyau ayant une couche vectorielle en sortie int size_input; // Nombre d'éléments en entrée int size_output; // Nombre d'éléments en sortie + float* bias; // bias[size_output] float* d_bias; // d_bias[size_output] #ifdef ADAM_DENSE_BIAS - float* s_d_bias; // s_d_bias[size_output] - float* v_d_bias; // v_d_bias[size_output] + float* s_d_bias; // s_d_bias[size_output] + float* v_d_bias; // v_d_bias[size_output] #endif + float** weights; // weight[size_input][size_output] float** d_weights; // d_weights[size_input][size_output] #ifdef ADAM_DENSE_WEIGHTS - float** s_d_weights; // s_d_weights[size_input][size_output] - float** v_d_weights; // v_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] #endif } Kernel_nn; typedef struct Kernel { Kernel_cnn* cnn; // NULL si ce n'est pas un cnn 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 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 @@ -60,10 +65,13 @@ typedef struct Network{ int dropout; // Probabilité d'abandon d'un neurone dans [0, 100] (entiers) float learning_rate; // Taux d'apprentissage du réseau int initialisation; // Id du type d'initialisation + int max_size; // Taille du tableau contenant le réseau int size; // Taille actuelle du réseau (size ≤ max_size) + int* width; // width[size] int* depth; // depth[size] + 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; // input[i] = f(input_z[i]) où f est la fonction d'activation de la couche i diff --git a/src/cnn/jpeg.c b/src/cnn/jpeg.c index cbcf7e7..723c39f 100644 --- a/src/cnn/jpeg.c +++ b/src/cnn/jpeg.c @@ -74,6 +74,52 @@ imgRawImage* loadJpegImageFile(char* lpFilename) { 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* dataset = (jpegDataset*)malloc(sizeof(jpegDataset)); imgRawImage* image;