From b893f11da0a47e5c49c91aa49246f76294945bfe Mon Sep 17 00:00:00 2001 From: augustin64 Date: Wed, 22 Mar 2023 13:03:19 +0100 Subject: [PATCH] Add random offset to mnist images during learning --- src/cnn/cnn.c | 83 ++++++++++++++++++++++++++++++++-- src/cnn/include/cnn.h | 2 +- src/cnn/include/test_network.h | 2 +- src/cnn/main.c | 2 +- src/cnn/test_network.c | 38 +++++++++++----- src/cnn/train.c | 29 ++++++++---- 6 files changed, 131 insertions(+), 25 deletions(-) diff --git a/src/cnn/cnn.c b/src/cnn/cnn.c index c18a37b..abc8357 100644 --- a/src/cnn/cnn.c +++ b/src/cnn/cnn.c @@ -1,7 +1,8 @@ +#include #include #include -#include #include // Is it used ? +#include #include "include/backpropagation.h" #include "include/initialisation.h" @@ -33,7 +34,80 @@ int will_be_drop(int dropout_prob) { return (rand() % 100) < dropout_prob; } -void write_image_in_network_32(int** image, int height, int width, float** input) { +void write_image_in_network_32(int** image, int height, int width, float** input, bool random_offset) { + int i_offset = 0; + int j_offset = 0; + int min_col = 0; + int min_ligne = 0; + + if (random_offset) { + /* + <-- min_ligne + .%%:. + ######%%%%%%%%%. + .:.:%##########: + . .... ##: + .## + ##. + :## + .##. + :#% + %#. + :#% + .##. + ##% + %## + ##. + ##: + :##. + .###. + :### + :#% + <-- max_ligne + ^-- min_col + ^-- max_col + */ + int sum_colonne[width]; + int sum_ligne[height]; + + for (int i=0; i < width; i++) { + sum_colonne[i] = 0; + } + for (int j=0; j < height; j++) { + sum_ligne[j] = 0; + } + + for (int i=0; i < width; i++) { + for (int j=0; j < height; j++) { + sum_ligne[i] += image[i][j]; + sum_colonne[j] += image[i][j]; + } + } + + min_ligne = -1; + while (sum_ligne[min_ligne+1] == 0 && min_ligne < width+1) { + min_ligne++; + } + + int max_ligne = width; + while (sum_ligne[max_ligne-1] == 0 && max_ligne > 0) { + max_ligne--; + } + + min_col = -1; + while (sum_colonne[min_col+1] == 0 && min_col < height+1) { + min_col++; + } + + int max_col = height; + while (sum_colonne[max_col-1] == 0 && max_col > 0) { + max_col--; + } + + i_offset = rand()%(27-max_ligne+min_ligne); + j_offset = rand()%(27-max_col+min_col); + } + int padding = (32 - height)/2; for (int i=0; i < padding; i++) { for (int j=0; j < 32; j++) { @@ -46,7 +120,10 @@ void write_image_in_network_32(int** image, int height, int width, float** input for (int i=0; i < width; i++) { for (int j=0; j < height; j++) { - input[i+2][j+2] = (float)image[i][j] / 255.0f; + int adjusted_i = i + min_ligne - i_offset; + int adjusted_j = j + min_col - j_offset; + // Make sure not to be out of the image + input[i+2][j+2] = adjusted_i < height && adjusted_j < width && adjusted_i >= 0 && adjusted_j >= 0 ? (float)image[adjusted_i][adjusted_j] / 255.0f : 0.; } } } diff --git a/src/cnn/include/cnn.h b/src/cnn/include/cnn.h index 5f117d0..150f9b8 100644 --- a/src/cnn/include/cnn.h +++ b/src/cnn/include/cnn.h @@ -17,7 +17,7 @@ int will_be_drop(int dropout_prob); /* * Écrit une image 28*28 au centre d'un tableau 32*32 et met à 0 le reste */ -void write_image_in_network_32(int** image, int height, int width, float** input); +void write_image_in_network_32(int** image, int height, int width, float** input, bool random_offset); /* * Écrit une image linéarisée de 256*256*3 pixels dans un tableau de taille 260*260*3 diff --git a/src/cnn/include/test_network.h b/src/cnn/include/test_network.h index de7f62b..109229e 100644 --- a/src/cnn/include/test_network.h +++ b/src/cnn/include/test_network.h @@ -6,7 +6,7 @@ /* * 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); +float* test_network(int dataset_type, char* modele, char* images_file, char* labels_file, char* data_dir, bool preview_fails, bool to_stdout, bool with_offset); /* * Classifie un fichier d'images sous le format MNIST à partir d'un réseau préalablement entraîné diff --git a/src/cnn/main.c b/src/cnn/main.c index 2fe2d46..a641b68 100644 --- a/src/cnn/main.c +++ b/src/cnn/main.c @@ -188,7 +188,7 @@ int main(int argc, char* argv[]) { printf_error("Pas de modèle à utiliser spécifié.\n"); return 1; } - test_network(dataset_type, modele, images_file, labels_file, data_dir, preview_fails); + (void)test_network(dataset_type, modele, images_file, labels_file, data_dir, preview_fails, true, false); return 0; } if (! strcmp(argv[1], "recognize")) { diff --git a/src/cnn/test_network.c b/src/cnn/test_network.c index 316aa25..6b83c40 100644 --- a/src/cnn/test_network.c +++ b/src/cnn/test_network.c @@ -12,7 +12,7 @@ #include "include/cnn.h" -void test_network_mnist(Network* network, char* images_file, char* labels_file, bool preview_fails) { +float* test_network_mnist(Network* network, char* images_file, char* labels_file, bool preview_fails, bool to_stdout, bool with_offset) { (void)preview_fails; // Inutilisé pour le moment int width, height; // Dimensions des images int nb_elem; // Nombre d'éléments @@ -36,11 +36,11 @@ void test_network_mnist(Network* network, char* images_file, char* labels_file, // Load image in the first layer of the Network for (int i=0; i < nb_elem; i++) { - if(i %(nb_elem/100) == 0) { + if(i %(nb_elem/100) == 0 && to_stdout) { printf("Avancement: %.0f%%\r", 100*i/(float)nb_elem); fflush(stdout); } - write_image_in_network_32(images[i], height, width, network->input[0][0]); + write_image_in_network_32(images[i], height, width, network->input[0][0], with_offset); forward_propagation(network); maxi = indice_max(network->input[network->size-1][0][0], 10); @@ -59,11 +59,15 @@ void test_network_mnist(Network* network, char* images_file, char* labels_file, free(images[i]); } free(images); - printf("%d Images. Taux de réussite: %.2f%%\tLoss: %lf\n", nb_elem, 100*accuracy/(float)nb_elem, loss/nb_elem); + + float* results = malloc(sizeof(float)*2); + results[0] = 100*accuracy/(float)nb_elem; + results[1] = loss/(float)nb_elem; + return results; } -void test_network_jpg(Network* network, char* data_dir, bool preview_fails) { +float* test_network_jpg(Network* network, char* data_dir, bool preview_fails, bool to_stdout) { (void)preview_fails; // Inutilisé pour le moment jpegDataset* dataset = loadJpegDataset(data_dir); @@ -71,7 +75,7 @@ void test_network_jpg(Network* network, char* data_dir, bool preview_fails) { int maxi; for (int i=0; i < (int)dataset->numImages; i++) { - if(i %(dataset->numImages/100) == 0) { + if(i %(dataset->numImages/100) == 0 && to_stdout) { printf("Avancement: %.1f%%\r", 1000*i/(float)dataset->numImages); fflush(stdout); } @@ -86,23 +90,35 @@ void test_network_jpg(Network* network, char* data_dir, bool preview_fails) { free(dataset->images[i]); } - printf("%d Images. Taux de réussite: %.2f%%\n", dataset->numImages, 100*accuracy/(float)dataset->numImages); + float* results = malloc(sizeof(float)*2); + results[0] = 100*accuracy/(float)dataset->numImages; + results[1] = 0; + free(dataset->images); free(dataset->labels); free(dataset); + + return results; } -void test_network(int dataset_type, char* modele, char* images_file, char* labels_file, char* data_dir, bool preview_fails) { +float* test_network(int dataset_type, char* modele, char* images_file, char* labels_file, char* data_dir, bool preview_fails, bool to_stdout, bool with_offset) { Network* network = read_network(modele); + float* results; if (dataset_type == 0) { - test_network_mnist(network, images_file, labels_file, preview_fails); + results = test_network_mnist(network, images_file, labels_file, preview_fails, to_stdout, with_offset); } else { - test_network_jpg(network, data_dir, preview_fails); + results = test_network_jpg(network, data_dir, preview_fails, to_stdout); } free_network(network); + + if (to_stdout) { + printf("Accuracy: %lf\tLoss: %lf\n", results[0], results[1]); + free(results); // Will not be used if to_stdout is used + } + return results; } @@ -130,7 +146,7 @@ void recognize_mnist(Network* network, char* input_file, char* out) { printf("\"%d\" : [", i); } - write_image_in_network_32(images[i], height, width, network->input[0][0]); + write_image_in_network_32(images[i], height, width, network->input[0][0], false); forward_propagation(network); diff --git a/src/cnn/train.c b/src/cnn/train.c index 3855a56..42033ff 100644 --- a/src/cnn/train.c +++ b/src/cnn/train.c @@ -51,7 +51,7 @@ void* train_thread(void* parameters) { for (int i=start; i < start+nb_images; i++) { if (dataset_type == 0) { - write_image_in_network_32(images[index[i]], height, width, network->input[0][0]); + write_image_in_network_32(images[index[i]], height, width, network->input[0][0], true); forward_propagation(network); maxi = indice_max(network->input[network->size-1][0][0], 10); if (maxi == -1) { @@ -106,6 +106,7 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di srand(time(NULL)); float loss; float batch_loss; // May be redundant with loss, but gives more informations + float test_accuracy = 0.; // Used to decrease Learning rate float accuracy; float batch_accuracy; float current_accuracy; @@ -233,7 +234,7 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di end_time = omp_get_wtime(); elapsed_time = end_time - start_time; - printf("Taux d'apprentissage initial: %lf\n", network->learning_rate); + printf("Taux d'apprentissage initial: %0.2e\n", network->learning_rate); printf("Initialisation: "); printf_time(elapsed_time); printf("\n\n"); @@ -308,7 +309,6 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di } current_accuracy = accuracy * nb_images_total/((j+1)*BATCHES); printf("\rThreads [%d]\tÉpoque [%d/%d]\tImage [%d/%d]\tAccuracy: " YELLOW "%0.2f%%" RESET " \tBatch Accuracy: " YELLOW "%0.2f%%" RESET, nb_threads, i, epochs, BATCHES*(j+1), nb_images_total, current_accuracy*100, batch_accuracy*100); - fflush(stdout); #else (void)nb_images_total_remaining; // Juste pour enlever un warning @@ -331,7 +331,6 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di update_bias(network, network); printf("\rÉpoque [%d/%d]\tImage [%d/%d]\tAccuracy: " YELLOW "%0.4f%%" RESET "\tBatch Accuracy: " YELLOW "%0.2f%%" RESET, i, epochs, BATCHES*(j+1), nb_images_total, current_accuracy*100, batch_accuracy*100); - fflush(stdout); #endif } //* Fin d'une époque: affichage des résultats et sauvegarde du réseau @@ -347,11 +346,25 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di printf("\n"); #endif write_network(out, network); - // If you want to test the network between each epoch, uncomment the following line: - test_network(0, out, "data/mnist/t10k-images-idx3-ubyte", "data/mnist/t10k-labels-idx1-ubyte", NULL, false); + // If you want to test the network between each epoch, uncomment the following lines: + /* + float* test_results = test_network(0, out, "data/mnist/t10k-images-idx3-ubyte", "data/mnist/t10k-labels-idx1-ubyte", NULL, false, false, true); + printf("Tests: Accuracy: %0.2lf%%\tLoss: %lf\n", test_results[0], test_results[1]); + if (test_results[0] < test_accuracy) { + network->learning_rate *= 0.1; + printf("Decreased learning rate to %0.2e\n", network->learning_rate); + } + if (test_results[0] == test_accuracy) { + network->learning_rate *= 2; + printf("Increased learning rate to %0.2e\n", network->learning_rate); + } + test_accuracy = test_results[0]; + free(test_results); - // Learning Rate decay - network->learning_rate -= LEARNING_RATE*(1./(float)(epochs+1)); + test_results = test_network(0, out, "data/mnist/t10k-images-idx3-ubyte", "data/mnist/t10k-labels-idx1-ubyte", NULL, false, false, false); + printf("Tests sans offset: Accuracy: %0.2lf%%\tLoss: %lf\n", test_results[0], test_results[1]); + free(test_results); + */ } //* Fin de l'algo