From 7da3544d8b77a495daad94975ad8511c5b65f495 Mon Sep 17 00:00:00 2001 From: augustin64 Date: Fri, 25 Nov 2022 15:17:47 +0100 Subject: [PATCH] Add recognize option --- Makefile | 6 +- src/cnn/cnn.c | 13 +++ src/cnn/include/cnn.h | 5 ++ src/cnn/include/test_network.h | 25 ++++++ src/cnn/include/train.h | 5 -- src/cnn/main.c | 156 ++++++++++++++++++++++++++------- src/cnn/test_network.c | 75 ++++++++++++++++ src/cnn/train.c | 16 +--- 8 files changed, 250 insertions(+), 51 deletions(-) create mode 100644 src/cnn/include/test_network.h create mode 100644 src/cnn/test_network.c diff --git a/Makefile b/Makefile index 2e01f9f..79a8140 100644 --- a/Makefile +++ b/Makefile @@ -65,10 +65,10 @@ $(BUILDDIR)/mnist_%.o: $(MNIST_SRCDIR)/%.c $(MNIST_SRCDIR)/include/%.h # cnn: $(BUILDDIR)/cnn-main $(BUILDDIR)/cnn-main-cuda $(BUILDDIR)/cnn-preview; -$(BUILDDIR)/cnn-main: $(CNN_SRCDIR)/main.c $(BUILDDIR)/cnn_train.o $(BUILDDIR)/cnn_cnn.o $(BUILDDIR)/cnn_creation.o $(BUILDDIR)/cnn_initialisation.o $(BUILDDIR)/cnn_make.o $(BUILDDIR)/cnn_neuron_io.o $(BUILDDIR)/cnn_function.o $(BUILDDIR)/cnn_utils.o $(BUILDDIR)/cnn_update.o $(BUILDDIR)/cnn_free.o $(BUILDDIR)/cnn_jpeg.o $(BUILDDIR)/cnn_convolution.o $(BUILDDIR)/cnn_backpropagation.o $(BUILDDIR)/colors.o $(BUILDDIR)/mnist.o +$(BUILDDIR)/cnn-main: $(CNN_SRCDIR)/main.c $(BUILDDIR)/cnn_train.o $(BUILDDIR)/cnn_test_network.o $(BUILDDIR)/cnn_cnn.o $(BUILDDIR)/cnn_creation.o $(BUILDDIR)/cnn_initialisation.o $(BUILDDIR)/cnn_make.o $(BUILDDIR)/cnn_neuron_io.o $(BUILDDIR)/cnn_function.o $(BUILDDIR)/cnn_utils.o $(BUILDDIR)/cnn_update.o $(BUILDDIR)/cnn_free.o $(BUILDDIR)/cnn_jpeg.o $(BUILDDIR)/cnn_convolution.o $(BUILDDIR)/cnn_backpropagation.o $(BUILDDIR)/colors.o $(BUILDDIR)/mnist.o $(CC) $^ -o $@ $(CFLAGS) -$(BUILDDIR)/cnn-main-cuda: $(BUILDDIR)/cnn_main.o $(BUILDDIR)/cnn_train.o $(BUILDDIR)/cnn_cnn.o $(BUILDDIR)/cnn_creation.o $(BUILDDIR)/cnn_initialisation.o $(BUILDDIR)/cnn_make.o $(BUILDDIR)/cnn_neuron_io.o $(BUILDDIR)/cnn_function.o $(BUILDDIR)/cnn_utils.o $(BUILDDIR)/cnn_update.o $(BUILDDIR)/cnn_free.o $(BUILDDIR)/cnn_jpeg.o $(BUILDDIR)/cnn_cuda_convolution.o $(BUILDDIR)/cnn_backpropagation.o $(BUILDDIR)/cuda_utils.o $(BUILDDIR)/colors.o $(BUILDDIR)/mnist.o +$(BUILDDIR)/cnn-main-cuda: $(BUILDDIR)/cnn_main.o $(BUILDDIR)/cnn_train.o $(BUILDDIR)/cnn_test_network.o $(BUILDDIR)/cnn_cnn.o $(BUILDDIR)/cnn_creation.o $(BUILDDIR)/cnn_initialisation.o $(BUILDDIR)/cnn_make.o $(BUILDDIR)/cnn_neuron_io.o $(BUILDDIR)/cnn_function.o $(BUILDDIR)/cnn_utils.o $(BUILDDIR)/cnn_update.o $(BUILDDIR)/cnn_free.o $(BUILDDIR)/cnn_jpeg.o $(BUILDDIR)/cnn_cuda_convolution.o $(BUILDDIR)/cnn_backpropagation.o $(BUILDDIR)/cuda_utils.o $(BUILDDIR)/colors.o $(BUILDDIR)/mnist.o ifndef NVCC_INSTALLED @echo "$(NVCC) not found, skipping" else @@ -105,7 +105,7 @@ endif # run-tests: build-tests $(foreach file, $(wildcard $(TEST_SRCDIR)/*.sh), $(file);) - @echo "$$(for file in build/test-*; do echo -e \\033[33m#####\\033[0m $$file \\033[33m#####\\033[0m; $$file; done)" + @echo "$$(for file in build/test-*; do echo -e \\033[33m#####\\033[0m $$file \\033[33m#####\\033[0m; $$file || echo "Erreur sur $$file"; done)" build-tests: prepare-tests $(TESTS_OBJ) $(BUILDDIR)/test-cnn_matrix_multiplication $(BUILDDIR)/test-cnn_convolution diff --git a/src/cnn/cnn.c b/src/cnn/cnn.c index 5782757..aa32640 100644 --- a/src/cnn/cnn.c +++ b/src/cnn/cnn.c @@ -16,6 +16,19 @@ // Augmente les dimensions de l'image d'entrée #define PADDING_INPUT 2 +int indice_max(float* tab, int n) { + int indice = -1; + float maxi = FLT_MIN; + + for (int i=0; i < n; i++) { + if (tab[i] > maxi) { + maxi = tab[i]; + indice = i; + } + } + return indice; +} + int will_be_drop(int dropout_prob) { return (rand() % 100) < dropout_prob; } diff --git a/src/cnn/include/cnn.h b/src/cnn/include/cnn.h index 0764257..884d13a 100644 --- a/src/cnn/include/cnn.h +++ b/src/cnn/include/cnn.h @@ -3,6 +3,11 @@ #ifndef DEF_MAIN_H #define DEF_MAIN_H +/* +* Renvoie l'indice de l'élément de valeur maximale dans un tableau de flottants +* Utilisé pour trouver le neurone le plus activé de la dernière couche (résultat de la classification) +*/ +int indice_max(float* tab, int n); /* * Renvoie si oui ou non (1 ou 0) le neurone va être abandonné diff --git a/src/cnn/include/test_network.h b/src/cnn/include/test_network.h new file mode 100644 index 0000000..1c924e9 --- /dev/null +++ b/src/cnn/include/test_network.h @@ -0,0 +1,25 @@ +#include "struct.h" + +#ifndef DEF_TEST_NETWORK_H +#define DEF_TEST_NETWORK_H + +/* +* Renvoie le taux de réussite d'un réseau sur des données de test +*/ +void test_network(int dataset_type, char* modele, char* images_file, char* labels_file, char* data_dir, bool preview_fails); + +/* +* Classifie un fichier d'images sous le format MNIST à partir d'un réseau préalablement entraîné +*/ +void recognize_mnist(Network* network, char* input_file); + +/* +* Classifie une image jpg à partir d'un réseau préalablement entraîné +*/ +void recognize_jpg(Network* network, char* input_file); + +/* +* Classifie une image à partir d'un réseau préalablement entraîné +*/ +void recognize(int dataset_type, char* modele, char* input_file); +#endif \ No newline at end of file diff --git a/src/cnn/include/train.h b/src/cnn/include/train.h index 2afc718..2cfe564 100644 --- a/src/cnn/include/train.h +++ b/src/cnn/include/train.h @@ -27,11 +27,6 @@ typedef struct TrainParameters { } TrainParameters; -/* -* Renvoie l'indice maximal d'un tableau tab de taille n -*/ -int indice_max(float* tab, int n); - /* * Fonction auxiliaire d'entraînement destinée à être exécutée sur plusieurs threads à la fois */ diff --git a/src/cnn/main.c b/src/cnn/main.c index e9cd7f4..07915df 100644 --- a/src/cnn/main.c +++ b/src/cnn/main.c @@ -2,9 +2,11 @@ #include #include #include +#include #include #include "include/initialisation.h" +#include "include/test_network.h" #include "../include/colors.h" #include "include/function.h" #include "include/creation.h" @@ -15,10 +17,8 @@ void help(char* call) { - printf("Usage: %s ( train | dev ) [OPTIONS]\n\n", call); + printf("Usage: %s ( train | recognize | test ) [OPTIONS]\n\n", call); printf("OPTIONS:\n"); - printf("\tdev:\n"); - printf("\t\t--conv | -c\tTester la fonction dev_conv().\n"); printf("\ttrain:\n"); printf("\t\t--dataset | -d (mnist|jpg)\tFormat du set de données.\n"); printf("\t(mnist)\t--images | -i [FILENAME]\tFichier contenant les images.\n"); @@ -26,12 +26,17 @@ void help(char* call) { printf("\t (jpg) \t--datadir | -dd [FOLDER]\tDossier contenant les images.\n"); printf("\t\t--epochs | -e [int]\t\tNombre d'époques.\n"); printf("\t\t--out | -o [FILENAME]\tFichier où écrire le réseau de neurones.\n"); -} - - -void dev_conv() { - Network* network = create_network_lenet5(0, 0, TANH, GLOROT, 32, 1); - forward_propagation(network); + printf("\trecognize:\n"); + printf("\t\t--dataset | -d (mnist|jpg)\tFormat de l'image à reconnaître.\n"); + printf("\t\t--modele | -m [FILENAME]\tFichier contenant le réseau entraîné.\n"); + printf("\t\t--input | -i [FILENAME]\tImage jpeg ou fichier binaire à reconnaître.\n"); + printf("\ttest:\n"); + printf("\t\t--modele | -m [FILENAME]\tFichier contenant le réseau entraîné.\n"); + printf("\t\t--dataset | -d (mnist|jpg)\tFormat du set de données.\n"); + printf("\t(mnist)\t--images | -i [FILENAME]\tFichier contenant les images.\n"); + printf("\t(mnist)\t--labels | -l [FILENAME]\tFichier contenant les labels.\n"); + printf("\t (jpg) \t--datadir | -dd [FOLDER]\tDossier contenant les images.\n"); + printf("\t\t--preview-fails | -p\t\tAfficher les images ayant échoué.\n"); } @@ -42,27 +47,6 @@ int main(int argc, char* argv[]) { help(argv[0]); return 1; } - if (! strcmp(argv[1], "dev")) { - int option = 0; - // 0 pour la fonction dev_conv() - int i = 2; - while (i < argc) { - // Utiliser un switch serait sans doute plus élégant - if ((! strcmp(argv[i], "--conv"))||(! strcmp(argv[i], "-c"))) { - option = 0; - i++; - } else { - printf("Option choisie inconnue: %s\n", argv[i]); - i++; - } - } - if (option == 0) { - dev_conv(); - return 0; - } - printf("Option choisie inconnue: dev %d\n", option); - return 1; - } if (! strcmp(argv[1], "train")) { char* dataset = NULL; char* images_file = NULL; @@ -130,6 +114,118 @@ int main(int argc, char* argv[]) { train(dataset_type, images_file, labels_file, data_dir, epochs, out); return 0; } + if (! strcmp(argv[1], "test")) { + char* dataset = NULL; // mnist ou jpg + char* modele = NULL; // Fichier contenant le modèle + char* images_file = NULL; // Fichier d'images (mnist) + char* labels_file = NULL; // Fichier de labels (mnist) + char* data_dir = NULL; // Dossier d'images (jpg) + int dataset_type; // Type de dataset (0 pour mnist, 1 pour jpg) + bool preview_fails = false; + int i = 2; + while (i < argc) { + if ((! strcmp(argv[i], "--dataset"))||(! strcmp(argv[i], "-d"))) { + dataset = argv[i+1]; + i += 2; + } + else 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_file = argv[i+1]; + i += 2; + } + else if ((! strcmp(argv[i], "--labels"))||(! strcmp(argv[i], "-l"))) { + labels_file = argv[i+1]; + i += 2; + } + else if ((! strcmp(argv[i], "--datadir"))||(! strcmp(argv[i], "-dd"))) { + data_dir = argv[i+1]; + i += 2; + } + else if ((! strcmp(argv[i], "--preview-fails"))||(! strcmp(argv[i], "-p"))) { + preview_fails = true; + i++; + } + else { + printf("Option choisie inconnue: %s\n", argv[i]); + i++; + } + } + if ((dataset!=NULL) && !strcmp(dataset, "mnist")) { + dataset_type = 0; + if (!images_file) { + printf("Pas de fichier d'images spécifié\n"); + return 1; + } + if (!labels_file) { + printf("Pas de fichier de labels spécifié\n"); + return 1; + } + } + else if ((dataset!=NULL) && !strcmp(dataset, "jpg")) { + dataset_type = 1; + if (!data_dir) { + printf("Pas de dossier de données spécifié.\n"); + return 1; + } + } + else { + printf("Pas de type de dataset spécifié.\n"); + return 1; + } + + if (!modele) { + printf("Pas de modèle à utiliser spécifié.\n"); + return 1; + } + test_network(dataset_type, modele, images_file, labels_file, data_dir, preview_fails); + return 0; + } + if (! strcmp(argv[1], "recognize")) { + char* dataset = NULL; // mnist ou jpg + char* modele = NULL; // Fichier contenant le modèle + char* input_file = NULL; // Image à reconnaître + int dataset_type; + int i = 2; + while (i < argc) { + if ((! strcmp(argv[i], "--dataset"))||(! strcmp(argv[i], "-d"))) { + dataset = argv[i+1]; + i += 2; + } + else if ((! strcmp(argv[i], "--modele"))||(! strcmp(argv[i], "-m"))) { + modele = argv[i+1]; + i += 2; + } + else if ((! strcmp(argv[i], "--input"))||(! strcmp(argv[i], "-i"))) { + input_file = argv[i+1]; + i += 2; + } else { + printf("Option choisie inconnue: %s\n", argv[i]); + i++; + } + } + if ((dataset!=NULL) && !strcmp(dataset, "mnist")) { + dataset_type = 0; + } else if ((dataset!=NULL) && !strcmp(dataset, "jpg")) { + dataset_type = 1; + } + else { + printf("Pas de type de dataset spécifié.\n"); + return 1; + } + if (!input_file) { + printf("Pas de fichier d'entrée spécifié, rien à faire.\n"); + return 1; + } + if (!modele) { + printf("Pas de modèle à utiliser spécifié.\n"); + return 1; + } + recognize(dataset_type, modele, input_file); + return 0; + } printf("Option choisie non reconnue: %s\n", argv[1]); help(argv[0]); return 1; diff --git a/src/cnn/test_network.c b/src/cnn/test_network.c new file mode 100644 index 0000000..b51ad75 --- /dev/null +++ b/src/cnn/test_network.c @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "../mnist/include/mnist.h" +#include "include/neuron_io.h" +#include "include/struct.h" +#include "include/jpeg.h" +#include "include/free.h" +#include "include/cnn.h" + +void test_network(int dataset_type, char* modele, char* images_file, char* labels_file, char* data_dir, bool preview_fails) { + +} + + +void recognize_mnist(Network* network, char* input_file) { + int width, height; // Dimensions de l'image + int nb_elem; // Nombre d'éléments + int maxi; // Catégorie reconnue + + // Load image + int* mnist_parameters = read_mnist_images_parameters(input_file); + int*** images = read_mnist_images(input_file); + nb_elem = mnist_parameters[0]; + + width = mnist_parameters[1]; + height = mnist_parameters[2]; + free(mnist_parameters); + + printf("Image\tCatégorie détectée\n"); + // Load image in the first layer of the Network + for (int i=0; i < nb_elem; i++) { + write_image_in_network_32(images[i], height, width, network->input[0][0]); + forward_propagation(network); + maxi = indice_max(network->input[network->size-1][0][0], 10); + + printf("%d\t%d\n", i, maxi); + + for (int j=0; j < height; j++) { + free(images[i][j]); + } + free(images[i]); + } + free(images); +} + +void recognize_jpg(Network* network, char* input_file) { + int width, height; // Dimensions de l'image + int maxi; + + imgRawImage* image = loadJpegImageFile(input_file); + width = image->width; + height = image->height; + + write_image_in_network_260(image->lpData, height, width, network->input[0]); + forward_propagation(network); + maxi = indice_max(network->input[network->size-1][0][0], 50); + + printf("Catégorie reconnue: %d\n", maxi); + free(image->lpData); + free(image); +} + +void recognize(int dataset_type, char* modele, char* input_file) { + Network* network = read_network(modele); + + if (dataset_type == 0) { + recognize_mnist(network, input_file); + } else { + recognize_jpg(network, input_file); + } + + free_network(network); +} \ No newline at end of file diff --git a/src/cnn/train.c b/src/cnn/train.c index e161e2f..b7761b0 100644 --- a/src/cnn/train.c +++ b/src/cnn/train.c @@ -19,18 +19,8 @@ #include "include/train.h" - -int indice_max(float* tab, int n) { - int indice = -1; - float maxi = FLT_MIN; - - for (int i=0; i < n; i++) { - if (tab[i] > maxi) { - maxi = tab[i]; - indice = i; - } - } - return indice; +int div_up(int a, int b) { // Partie entière supérieure de a/b + return ((a % b) != 0) ? (a / b + 1) : (a / b); } @@ -182,7 +172,7 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di // du multi-threading car chaque copie du réseau initiale sera légèrement différente // et donnera donc des résultats différents sur les mêmes images. accuracy = 0.; - batches_epoques = nb_images_total / BATCHES; + batches_epoques = div_up(nb_images_total, BATCHES); nb_images_total_remaining = nb_images_total; for (int j=0; j < batches_epoques; j++) { #ifdef USE_MULTITHREADING