This commit is contained in:
julienChemillier 2022-12-27 09:19:37 +01:00
commit bf5f0cf078
22 changed files with 453 additions and 39 deletions

View File

@ -54,3 +54,12 @@ Le réseau neuronal simple donnant des résultats convaincants (approximativemen
Le réseau neuronal convolutif a commencé à être développé depuis [6532ad2](https://github.com/julienChemillier/TIPE/commit/6532ad2545f8882638209cc6918bf37a9f816840). Le réseau neuronal convolutif a commencé à être développé depuis [6532ad2](https://github.com/julienChemillier/TIPE/commit/6532ad2545f8882638209cc6918bf37a9f816840).
Ce commit introduit l'enregistrement du réseau de neurones convolutif, Ce commit introduit l'enregistrement du réseau de neurones convolutif,
suivi de près par les tests unitaires correspondants [b12a03c](https://github.com/julienChemillier/TIPE/commit/b12a03c1baa8e8505066fa07ae2f20882a24854b). suivi de près par les tests unitaires correspondants [b12a03c](https://github.com/julienChemillier/TIPE/commit/b12a03c1baa8e8505066fa07ae2f20882a24854b).
<br/>
<br/>
<br/>
### **30 Novembre 2022** Réparation du NN simple [ffc0c6e](https://github.com/julienChemillier/TIPE/commit/ffc0c6ea9fe30c7e98624ca26867d984ec90c693)
Après un peu de débogage sur le réseau convolutif, on s'est rendu compte que le réseau simple ne fonctionnait pas bien avec des couches intermédiaires et du multithreading.
Le problème principal étant la découpe des batches étant la même à chaque époque.
Après implémentation d'un mélange de Knuth, tout est rentré dans l'ordre donnant des résultats encore plus satisfaisants pour les couches intermédiaires. (De l'ordre de 70% avec deux couches intermédiaires et utilisation du multithreading).

7
doc/README.md Normal file
View File

@ -0,0 +1,7 @@
# README
Plusieurs fichiers sont disponibles:
- [mnist](/doc/mnist) Réseau de neurones non convolutif
- [cnn](/doc/cnn) Réseau de neurones convolutif
- [webserver](/doc/webserver) Serveur web (python) pour créer des entrées manuellement
- [Ressources](/doc/resources.md) Ressources utilisées

31
doc/cnn/README.md Normal file
View File

@ -0,0 +1,31 @@
# Réseau de neurones convolutif [code](/src/cnn)
Cette partie du code implémente un réseau de neuron convolutif.
## Compilation
```bash
make cnn
```
## Fichiers
- [main](/src/cnn/main.c) [[Documentation](/doc/cnn/main.md)] Contient la fonction main et lit les arguments à son appel
- [train](/src/cnn/train.c) [[Documentation](/doc/cnn/train.md)] Contient la partie plus haut niveau du code entraînant le réseau. Implémente la programmation concurrentielle
- [convolution](/src/cnn/convolution.cu) [[Documentation](/doc/cnn/convolution.md)] Convolution de matrices, en C et en CUDA
- [jpeg](/src/cnn/jpeg.c) [[Documentation](/doc/cnn/jpeg.md)] Lecture d'un jeu de données constitué d'images au format JPEG
- [neuron io](/src/cnn/neuron_io.c) [[Documentation](/doc/cnn/neuron_io.md)] Lecture et écriture du réseau de neurone dans un fichier
- [preview](/src/cnn/preview.c) [[Documentation](/doc/cnn/preview.md)] Visualisation des données chargées pour un jeu de données de type JPEG
- [test network](/src/cnn/test_network.c) [[Documentation](/doc/cnn/test_network.md)] Ensemble de fonctions permettant d'évaluer un réseau depuis un jeu de tests et de classifier des images
- [utils](/src/cnn/utils.c) [[Header](/src/cnn/include/utils.h)] Fonctions utilitaires (Copie de réseau, vérification d'égalité, mélange de Knuth)
- [matrix multiplication](/src/cnn/matrix_multiplication.cu) [[Documentation](/doc/cnn/matrix_multiplication.md)] Maintenant inutilisé, test de multiplication de matrices en CUDA
- [backpropagation](/src/cnn/backpropagation.c) [[Documentation](/doc/cnn/backpropagation.md)]
- [cnn](/src/cnn/cnn.c) [[Documentation](/doc/cnn/cnn.md)]
- [creation](/src/cnn/creation.c) [[Documentation](/doc/cnn/creation.md)]
- [function](/src/cnn/function.c) [[Documentation](/doc/cnn/function.md)]
- [include](/src/cnn/include) [[Documentation](/doc/cnn/include.md)]
- [make](/src/cnn/make.c) [[Documentation](/doc/cnn/make.md)]
- [print](/src/cnn/print.c) [[Documentation](/doc/cnn/print.md)]
- [free](/src/cnn/free.c) [[Header](/src/cnn/include/free.h)]
- [update](/src/cnn/update.c) [[Header](/src/cnn/include/update.h)]
- [initialisation](/src/cnn/initialisation.c) [[Header](/src/cnn/include/initialisation.h)]

127
doc/cnn/main.md Normal file
View File

@ -0,0 +1,127 @@
# Main
## Compilation
#### Deux options sont disponibles à la compilation
Une première fonctionnant sous toutes les machines:
```bash
make cnn-main
```
Une seconde utilisant CUDA pour la convolution, qui sera plus rapide, mais ne fonctionnera que sur les machines équipées d'une carte graphique Nvidia (nécessite `nvcc` disponible sous le paquet `cuda` ou `cuda-tools` pour la compilation):
```bash
make cnn-main-cuda
```
## Options à la compilation
- La taille de la couche d'entrée ainsi que la fonction d'activation utilisée sont définissable dans la [création du réseau](/src/cnn/train.c#L116) (L'architecture utilisée se définit ici également, LeNet5 étant adaptée uniquement au jeu de données MNIST)
- La définition du nombre d'époques par défaut se fait dans la définition [`EPOCHS`](/src/cnn/include/train.h#L7)
- La définition de la taille des batches se fait dans la définition [`BATCHES`](/src/cnn/include/train.h#L8)
- Le multi-threading est activé par défaut, se désactive en enlevant la définition [`USE_MULTITHREADING`](/src/cnn/include/train.h#L9) (le multithreading ne fonctionne pas pour le moment)
- Il y a une option pour conserver l'ensemble du jeu de données JPEG dans la mémoire RAM [détails](/doc/cnn/jpeg.md#STORE_TO_RAM)
### Options spécifiques à CUDA
- La définition de la taille des blocs peut-être trop élevée pour certaines carte graphiques, il faudra alors réduire l'une des définitions de [`BLOCKSIZE`](/src/cnn/convolution.cu#L37)
## Utilisation
```
Usage: build/cnn-main ( train | recognize | test ) [OPTIONS]
OPTIONS:
train:
--dataset | -d (mnist|jpg) Format du set de données.
(mnist) --images | -i [FILENAME] Fichier contenant les images.
(mnist) --labels | -l [FILENAME] Fichier contenant les labels.
(jpg) --datadir | -dd [FOLDER] Dossier contenant les images.
--recover | -r [FILENAME] Récupérer depuis un modèle existant.
--epochs | -e [int] Nombre d'époques.
--out | -o [FILENAME] Fichier où écrire le réseau de neurones.
recognize:
--dataset | -d (mnist|jpg) Format de l'image à reconnaître.
--modele | -m [FILENAME] Fichier contenant le réseau entraîné.
--input | -i [FILENAME] Image jpeg ou fichier binaire à reconnaître.
test:
--modele | -m [FILENAME] Fichier contenant le réseau entraîné.
--dataset | -d (mnist|jpg) Format du set de données.
(mnist) --images | -i [FILENAME] Fichier contenant les images.
(mnist) --labels | -l [FILENAME] Fichier contenant les labels.
(jpg) --datadir | -dd [FOLDER] Dossier contenant les images.
--preview-fails | -p Afficher les images ayant échoué.
```
## Entraînement
Entraînement du réseau de neurones
Exemple (MNIST):
```bash
build/cnn-main train \
--dataset mnist \
--epochs 15 \
--images data/mnist/train-images-idx3-ubyte \
--labels data/mnist/train-labels-idx1-ubyte \
--out reseau.bin
```
Exemple (JPG):
```bash
build/cnn-main train \
--dataset jpg \
--epochs 15 \
--datadir data/50States10K/train \
--out reseau.bin
```
Le réseau de neurones entraîné est sauvegardé dans le fichier de sortie à la fin de chaque époque.
## Reconnaissance d'images
### MNIST
La reconnaissance d'images se fait avec un fichier formaté de la même manière que le jeu de données MNIST.
Le plus simple pour dessiner à la main est d'utiliser le [serveur web](/doc/webserver) prévu à cet effet
Note:
Le serveur web ne prend pour le moment qu'une option pour dessiner et faire reconnaître par le réseau de neurones simple.
Cependant, l'image dessinée est stockée dans le fichier `.cache/image-idx3-ubyte`, la faire reconnaître par le réseau convolutif est donc possible avec la commande suivante:
```bash
build/cnn-main recognize \
--dataset jpg \
--modele reseau.bin \
--input .cache/image-idx3-ubyte \
--output json
```
### JPEG
L'image d'entrée doit conserver la même taille que les images ayant servi à entraîner le réseau (256x256 pixels)
Exemple:
```bash
build/cnn-main recognize \
--dataset jpg \
--modele reseau.bin \
--input image.jpeg \
--output json
```
## Test sur le jeu prévu à cet effet
Exemple (MNIST):
```bash
build/cnn-main test \
--dataset mnist \
-i data/mnist/t10k-images-idx3-ubyte \
-l data/mnist/t10k-labels-idx1-ubyte \
-m reseau.bin
```
Exemple (JPG):
```bash
build/cnn-main test \
--dataset jpg \
--datadir data/50States10K/test \
-m reseau.bin
```

View File

@ -1,4 +1,4 @@
# Réseau de neurones convolutionnel [lien](/src/cnn) # Neuron IO
## Lecture/ Écriture du réseau de neurone: ## Lecture/ Écriture du réseau de neurone:
Le fichier est au format IDX (format binaire) Le fichier est au format IDX (format binaire)

27
doc/cnn/train.md Normal file
View File

@ -0,0 +1,27 @@
# Entraînement du réseau
## Théorie
_définition_: Une **époque** est une itération sur l'entièreté du jeu de données. Une époque est divisée en batchs.
_définition_: Un **batch** est une partie d'une époque à la fin de laquelle les modifications nécessaires calculées sont appliquées au réseau.
Ainsi, chaque époque est divisé en batchs de taille égale.
Les images sont itérées dans un ordre différent à chaque époque, permet d'éviter le sur-apprentissage dans une certaine mesure.
### Programmation en parallèle
Lorsque le multithreading est utilisé, chaque époque est divisé en un certain nombre de parties chacune de taille environ égale.
Chaque fil d'exécution se voir alors assigner une de ces parties.
Lorsque chaque thread a fini son exécution, les modifications sont appliquées au réseau.
## Pratique
Le type [TrainParameters](/src/cnn/include/train.h#L15) est utilisé pour donner des arguments à chaque fil d'exécution ou au thread principal dans le cas échéant.
### Sans multithreading
Aucune copie n'est nécessaire pour lancer la fonction [`train_thread`](/src/cnn/train.c#L27), qui est exécutée sur le fil d'exécution principal.
### Avec multithreading
Le réseau principal est copié dans une partie différente de la mémoire pour chaque thread.
En effet, la partie contenant les informations statiques peut-être conservé le même pour chaque thread,
mais le tableau `input` du réseau de neurones contient les données de chaque forward propagation et se doit d'être différent.
Il serait cependant envisageable de ne différencier que cette partie et d'utiliser des `mutex` pour garantir un accès sans conflit à la partie commune d'un même réseau.

18
doc/mnist/README.md Normal file
View File

@ -0,0 +1,18 @@
# Réseau de neurones simple [code](/src/mnist)
Cette partie du code implémente un réseau de neuron simple (non convolutif)
## Compilation
```bash
make mnist
```
## Fichiers
- [main](/src/mnist/main.c) [[Documentation](/doc/mnist/main.md)] Contient la fonction main et les fonctions principales à appeler
- [mnist](/src/mnist/mnist.c) [[Documentation](/src/mnist/include/mnist.h)] Partagé avec le cnn, lit les fichiers du jeu de données [MNIST](http://yann.lecun.com/exdb/mnist/)
- [neural_network](/src/mnist/neural_network.c) [[Documentation](/src/mnist/include/neural_network.h)] Contient le coeur du nn: forward et backward propagation ainsi que quelques utilitaires (copie et patch)
- [neuron_io](/src/mnist/neuron_io.c) [[Documentation](/doc/mnist/neuron_io.md)] Écrire et lire le réseau de neurones depuis un fichier
- [neuron.h](/src/mnist/include/neuron.h) Définit la structure `Network` et les structures qui y sont liées
- [preview](/src/mnist/preview.c) [[Documentation](/doc/mnist/preview.md)] Afficher les images chargées
- [utils](/src/mnist/utils.c) [[Documentation](/doc/mnist/utils.md)] Contient un ensemble de fonctions utiles à des fins de débogage

78
doc/mnist/main.md Normal file
View File

@ -0,0 +1,78 @@
# Main
## Compilation
```bash
make mnist-main
```
## Options à la compilation
- La définition de la taille des batches se fait dans l'un des [`#define`](/src/mnist/main.c#L15)
- Le multi-threading est activé par défaut, réduisible à un seul thread actif en remplaçant [`get_nprocs()`](/src/mnist/main.c#L144) par 1
- L'ajustement du nombre de couches, bien qu'étant une option en ligne de commande, ne définit pas les valeurs pour chaque couche. On préférera donc modifier directement ces valeurs dans le code [ici](/src/mnist/main.c#L140) et [ici](/src/mnist/main.c#L378)
## Utilisation
```bash
Usage: build/mnist-main ( train | recognize | test ) [OPTIONS]
OPTIONS:
train:
--epochs | -e [int] Nombre d'époques (itérations sur tout le set de données).
--couches | -c [int] Nombres de couches.
--neurones | -n [int] Nombre de neurones sur la première couche.
--recover | -r [FILENAME] Récupérer depuis un modèle existant.
--images | -i [FILENAME] Fichier contenant les images.
--labels | -l [FILENAME] Fichier contenant les labels.
--out | -o [FILENAME] Fichier où écrire le réseau de neurones.
--delta | -d [FILENAME] Fichier où écrire le réseau différentiel.
--nb-images | -N [int] Nombres d'images à traiter.
--start | -s [int] Première image à traiter.
recognize:
--modele | -m [FILENAME] Fichier contenant le réseau de neurones.
--in | -i [FILENAME] Fichier contenant les images à reconnaître.
--out | -o (text|json) Format de sortie.
test:
--images | -i [FILENAME] Fichier contenant les images.
--labels | -l [FILENAME] Fichier contenant les labels.
--modele | -m [FILENAME] Fichier contenant le réseau de neurones.
--preview-fails | -p Afficher les images ayant échoué.
```
## Entraînement
Entraînement du réseau de neurones
Exemple:
```bash
build/mnist-main train \
-e 15 \
-i data/mnist/train-images-idx3-ubyte \
-l data/mnist/train-labels-idx1-ubyte \
-o reseau.bin
```
Le réseau de neurones est sauvegardé dans le fichier de sortie à la fin de chaque époque.
## Reconnaissance d'images
La reconnaissance d'images se fait avec un fichier formaté de la même manière que le jeu de données MNIST.
Le plus simple pour dessiner à la main est d'utiliser le [serveur web](/doc/webserver) prévu à cet effet
Exemple:
```bash
build/mnist-main recognize \
-m reseau.bin \
-i .cache/image-idx3-ubyte \
-o json
```
## Test sur le jeu prévu à cet effet
Exemple:
```bash
build/mnist-main test \
-i data/mnist/t10k-images-idx3-ubyte \
-l data/mnist/t10k-labels-idx1-ubyte \
-m reseau.bin
```

View File

@ -1,4 +1,4 @@
# Réseau de neurones simple [lien](/src/mnist) # Neuron IO
## Lecture/ Écriture du réseau de neurone: ## Lecture/ Écriture du réseau de neurone:
Le fichier est au format IDX (format binaire) Le fichier est au format IDX (format binaire)

13
doc/mnist/preview.md Normal file
View File

@ -0,0 +1,13 @@
# Preview
## Compilation
```bash
make mnist-preview
```
## Utilisation
```bash
build/mnist-preview data/mnist/t10k-images-idx3-ubyte data/mnist/t10k-labels-idx1-ubyte
```

32
doc/mnist/utils.md Normal file
View File

@ -0,0 +1,32 @@
# Utils
## Compilation
```bash
make mnist-utils
```
## Utilisation
```bash
Usage: build/mnist-utils ( print-poids | print-biais | creer-reseau | patch-network ) [OPTIONS]
OPTIONS:
print-poids:
--reseau | -r [FILENAME] Fichier contenant le réseau de neurones.
print-biais:
--reseau | -r [FILENAME] Fichier contenant le réseau de neurones.
count-labels:
--labels | -l [FILENAME] Fichier contenant les labels.
creer-reseau:
--out | -o [FILENAME] Fichier où écrire le réseau de neurones.
--number | -n [int] Numéro à privilégier.
patch-network:
--network | -n [FILENAME] Fichier contenant le réseau de neurones.
--delta | -d [FILENAME] Fichier de patch à utiliser.
print-images:
--images | -i [FILENAME] Fichier contenant les images.
print-poids-neurone:
--reseau | -r [FILENAME] Fichier contenant le réseau de neurones.
--neurone | -n [int] Numéro du neurone dont il faut afficher les poids.
```

13
doc/webserver/README.md Normal file
View File

@ -0,0 +1,13 @@
# Webserver
## Pré-requis
```bash
pip install flask
```
## Utilisation
```bash
make webserver
```

View File

@ -1,7 +1,7 @@
#include "struct.h" #include "struct.h"
/* /*
* Effectue la convolution sur le processeur * Effectue la convolution naïvement sur le processeur
*/ */
void make_convolution_cpu(Kernel_cnn* kernel, float*** input, float*** output, int output_dim); void make_convolution_cpu(Kernel_cnn* kernel, float*** input, float*** output, int output_dim);
@ -12,7 +12,7 @@ void make_convolution_cpu(Kernel_cnn* kernel, float*** input, float*** output, i
__global__ void make_convolution_kernel(int k_size, int columns, int rows, float*** bias, size_t pitch_bias, float**** w, size_t pitch_w, float*** input, size_t pitch_input, float*** output, size_t pitch_output, int output_dim); __global__ void make_convolution_kernel(int k_size, int columns, int rows, float*** bias, size_t pitch_bias, float**** w, size_t pitch_w, float*** input, size_t pitch_input, float*** output, size_t pitch_output, int output_dim);
/* /*
* Effectue la convolution sur la carte graphique * Effectue la convolution naïvement sur la carte graphique
*/ */
void make_convolution_device(Kernel_cnn* kernel, float*** input, float*** output, int output_dim); void make_convolution_device(Kernel_cnn* kernel, float*** input, float*** output, int output_dim);
#endif #endif

View File

@ -35,6 +35,6 @@ void* train_thread(void* parameters);
/* /*
* Fonction principale d'entraînement du réseau neuronal convolutif * Fonction principale d'entraînement du réseau neuronal convolutif
*/ */
void train(int dataset_type, char* images_file, char* labels_file, char* data_dir, int epochs, char* out); void train(int dataset_type, char* images_file, char* labels_file, char* data_dir, int epochs, char* out, char* recover);
#endif #endif

View File

@ -7,13 +7,13 @@
* Met à jours les poids à partir de données obtenus après plusieurs backpropagations * Met à jours les poids à partir de données obtenus après plusieurs backpropagations
* Puis met à 0 tous les d_weights * Puis met à 0 tous les d_weights
*/ */
void update_weights(Network* network, Network* d_network); void update_weights(Network* network, Network* d_network, int nb_images);
/* /*
* Met à jours les biais à partir de données obtenus après plusieurs backpropagations * Met à jours les biais à partir de données obtenus après plusieurs backpropagations
* Puis met à 0 tous les d_bias * Puis met à 0 tous les d_bias
*/ */
void update_bias(Network* network, Network* d_network); void update_bias(Network* network, Network* d_network, int nb_images);
/* /*
* Met à 0 toutes les données de backpropagation de poids * Met à 0 toutes les données de backpropagation de poids

View File

@ -8,6 +8,16 @@
#ifndef DEF_UTILS_H #ifndef DEF_UTILS_H
#define DEF_UTILS_H #define DEF_UTILS_H
/*
* Échange deux éléments d'un tableau
*/
void swap(int* tab, int i, int j);
/*
* Mélange un tableau avec le mélange de Knuth
*/
void knuth_shuffle(int* tab, int n);
/* /*
* Vérifie si deux réseaux sont égaux * Vérifie si deux réseaux sont égaux
*/ */

View File

@ -24,6 +24,7 @@ void help(char* call) {
printf("\t(mnist)\t--images | -i [FILENAME]\tFichier contenant les images.\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(mnist)\t--labels | -l [FILENAME]\tFichier contenant les labels.\n");
printf("\t (jpg) \t--datadir | -dd [FOLDER]\tDossier contenant les images.\n"); printf("\t (jpg) \t--datadir | -dd [FOLDER]\tDossier contenant les images.\n");
printf("\t\t--recover | -r [FILENAME]\tRécupérer depuis un modèle existant.\n");
printf("\t\t--epochs | -e [int]\t\tNombre d'époques.\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"); printf("\t\t--out | -o [FILENAME]\tFichier où écrire le réseau de neurones.\n");
printf("\trecognize:\n"); printf("\trecognize:\n");
@ -55,6 +56,7 @@ int main(int argc, char* argv[]) {
int epochs = EPOCHS; int epochs = EPOCHS;
int dataset_type = 0; int dataset_type = 0;
char* out = NULL; char* out = NULL;
char* recover = NULL;
int i = 2; int i = 2;
while (i < argc) { while (i < argc) {
if ((! strcmp(argv[i], "--dataset"))||(! strcmp(argv[i], "-d"))) { if ((! strcmp(argv[i], "--dataset"))||(! strcmp(argv[i], "-d"))) {
@ -80,6 +82,9 @@ int main(int argc, char* argv[]) {
else if ((! strcmp(argv[i], "--out"))||(! strcmp(argv[i], "-o"))) { else if ((! strcmp(argv[i], "--out"))||(! strcmp(argv[i], "-o"))) {
out = argv[i+1]; out = argv[i+1];
i += 2; i += 2;
} else if ((! strcmp(argv[i], "--recover"))||(! strcmp(argv[i], "-r"))) {
recover = argv[i+1];
i += 2;
} else { } else {
printf("Option choisie inconnue: %s\n", argv[i]); printf("Option choisie inconnue: %s\n", argv[i]);
i++; i++;
@ -111,7 +116,7 @@ int main(int argc, char* argv[]) {
printf("Pas de fichier de sortie spécifié, défaut: out.bin\n"); printf("Pas de fichier de sortie spécifié, défaut: out.bin\n");
out = "out.bin"; out = "out.bin";
} }
train(dataset_type, images_file, labels_file, data_dir, epochs, out); train(dataset_type, images_file, labels_file, data_dir, epochs, out, recover);
return 0; return 0;
} }
if (! strcmp(argv[1], "test")) { if (! strcmp(argv[1], "test")) {

View File

@ -32,6 +32,7 @@ void* train_thread(void* parameters) {
int*** images = param->images; int*** images = param->images;
int* labels = (int*)param->labels; int* labels = (int*)param->labels;
int* index = param->index;
int width = param->width; int width = param->width;
int height = param->height; int height = param->height;
@ -41,31 +42,31 @@ void* train_thread(void* parameters) {
float accuracy = 0.; float accuracy = 0.;
for (int i=start; i < start+nb_images; i++) { for (int i=start; i < start+nb_images; i++) {
if (dataset_type == 0) { if (dataset_type == 0) {
write_image_in_network_32(images[i], height, width, network->input[0][0]); write_image_in_network_32(images[index[i]], height, width, network->input[0][0]);
forward_propagation(network); forward_propagation(network);
maxi = indice_max(network->input[network->size-1][0][0], 10); maxi = indice_max(network->input[network->size-1][0][0], 10);
backward_propagation(network, labels[i]); backward_propagation(network, labels[i]);
if (maxi == labels[i]) { if (maxi == labels[index[i]]) {
accuracy += 1.; accuracy += 1.;
} }
} else { } else {
if (!param->dataset->images[i]) { if (!param->dataset->images[index[i]]) {
image = loadJpegImageFile(param->dataset->fileNames[i]); image = loadJpegImageFile(param->dataset->fileNames[index[i]]);
param->dataset->images[i] = image->lpData; param->dataset->images[index[i]] = image->lpData;
free(image); free(image);
} }
write_image_in_network_260(param->dataset->images[i], height, width, network->input[0]); write_image_in_network_260(param->dataset->images[index[i]], height, width, network->input[0]);
forward_propagation(network); forward_propagation(network);
maxi = indice_max(network->input[network->size-1][0][0], param->dataset->numCategories); maxi = indice_max(network->input[network->size-1][0][0], param->dataset->numCategories);
backward_propagation(network, param->dataset->labels[i]); backward_propagation(network, param->dataset->labels[index[i]]);
if (maxi == (int)param->dataset->labels[i]) { if (maxi == (int)param->dataset->labels[index[i]]) {
accuracy += 1.; accuracy += 1.;
} }
free(param->dataset->images[i]); free(param->dataset->images[index[i]]);
param->dataset->images[i] = NULL; param->dataset->images[index[i]] = NULL;
} }
} }
@ -74,8 +75,9 @@ void* train_thread(void* parameters) {
} }
void train(int dataset_type, char* images_file, char* labels_file, char* data_dir, int epochs, char* out) { void train(int dataset_type, char* images_file, char* labels_file, char* data_dir, int epochs, char* out, char* recover) {
srand(time(NULL)); srand(time(NULL));
Network* network;
int input_dim = -1; int input_dim = -1;
int input_depth = -1; int input_depth = -1;
float accuracy; float accuracy;
@ -85,9 +87,10 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di
int nb_images_total_remaining; // Images restantes dans un batch int nb_images_total_remaining; // Images restantes dans un batch
int batches_epoques; // Batches par époque int batches_epoques; // Batches par époque
int*** images; int*** images; // Images sous forme de tableau de tableaux de tableaux de pixels (degré de gris, MNIST)
unsigned int* labels; unsigned int* labels; // Labels associés aux images du dataset MNIST
jpegDataset* dataset; jpegDataset* dataset; // Structure de données décrivant un dataset d'images jpeg
int* shuffle_index; // shuffle_index[i] contient le nouvel index de l'élément à l'emplacement i avant mélange
if (dataset_type == 0) { // Type MNIST if (dataset_type == 0) { // Type MNIST
// Chargement des images du set de données MNIST // Chargement des images du set de données MNIST
@ -109,7 +112,17 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di
} }
// Initialisation du réseau // Initialisation du réseau
Network* network = create_network_lenet5(0.01, 0, TANH, GLOROT, input_dim, input_depth); if (!recover) {
network = create_network_lenet5(1, 0, TANH, GLOROT, input_dim, input_depth);
} else {
network = read_network(recover);
}
shuffle_index = (int*)malloc(sizeof(int)*nb_images_total);
for (int i=0; i < nb_images_total; i++) {
shuffle_index[i] = i;
}
#ifdef USE_MULTITHREADING #ifdef USE_MULTITHREADING
int nb_remaining_images; // Nombre d'images restantes à lancer pour une série de threads int nb_remaining_images; // Nombre d'images restantes à lancer pour une série de threads
@ -139,6 +152,7 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di
param->labels = NULL; param->labels = NULL;
} }
param->nb_images = BATCHES / nb_threads; param->nb_images = BATCHES / nb_threads;
param->index = shuffle_index;
} }
#else #else
// Création des paramètres donnés à l'unique // Création des paramètres donnés à l'unique
@ -163,6 +177,7 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di
train_params->labels = NULL; train_params->labels = NULL;
} }
train_params->nb_images = BATCHES; train_params->nb_images = BATCHES;
train_params->index = shuffle_index;
#endif #endif
for (int i=0; i < epochs; i++) { for (int i=0; i < epochs; i++) {
@ -172,8 +187,12 @@ 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 // 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. // et donnera donc des résultats différents sur les mêmes images.
accuracy = 0.; accuracy = 0.;
knuth_shuffle(shuffle_index, nb_images_total);
batches_epoques = div_up(nb_images_total, BATCHES); batches_epoques = div_up(nb_images_total, BATCHES);
nb_images_total_remaining = nb_images_total; nb_images_total_remaining = nb_images_total;
#ifndef USE_MULTITHREADING
train_params->nb_images = BATCHES;
#endif
for (int j=0; j < batches_epoques; j++) { for (int j=0; j < batches_epoques; j++) {
#ifdef USE_MULTITHREADING #ifdef USE_MULTITHREADING
if (j == batches_epoques-1) { if (j == batches_epoques-1) {
@ -200,24 +219,34 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di
// On attend la terminaison de chaque thread un à un // On attend la terminaison de chaque thread un à un
pthread_join( tid[k], NULL ); pthread_join( tid[k], NULL );
accuracy += train_parameters[k]->accuracy / (float) nb_images_total; accuracy += train_parameters[k]->accuracy / (float) nb_images_total;
}
update_weights(network, train_parameters[k]->network); // On attend que tous les fils aient fini avant d'appliquer des modifications au réseau principal
update_bias(network, train_parameters[k]->network); for (int k=0; k < nb_threads; k++) {
update_weights(network, train_parameters[k]->network, train_parameters[k]->nb_images);
update_bias(network, train_parameters[k]->network, train_parameters[k]->nb_images);
free_network(train_parameters[k]->network); free_network(train_parameters[k]->network);
} }
current_accuracy = accuracy * nb_images_total/((j+1)*BATCHES); current_accuracy = accuracy * nb_images_total/((j+1)*BATCHES);
printf("\rThreads [%d]\tÉpoque [%d/%d]\tImage [%d/%d]\tAccuracy: "YELLOW"%0.1f%%"RESET" ", nb_threads, i, epochs, BATCHES*(j+1), nb_images_total, current_accuracy*100); printf("\rThreads [%d]\tÉpoque [%d/%d]\tImage [%d/%d]\tAccuracy: "YELLOW"%0.1f%%"RESET" ", nb_threads, i, epochs, BATCHES*(j+1), nb_images_total, current_accuracy*100);
fflush(stdout); fflush(stdout);
#else #else
(void)nb_images_total_remaining; // Juste pour enlever un warning
train_params->start = j*BATCHES; train_params->start = j*BATCHES;
// Ne pas dépasser le nombre d'images à cause de la partie entière
if (j == batches_epoques-1) {
train_params->nb_images = nb_images_total - j*BATCHES;
}
train_thread((void*)train_params); train_thread((void*)train_params);
accuracy += train_params->accuracy / (float) nb_images_total; accuracy += train_params->accuracy / (float) nb_images_total;
current_accuracy = accuracy * nb_images_total/((j+1)*BATCHES); current_accuracy = accuracy * nb_images_total/((j+1)*BATCHES);
update_weights(network, network); update_weights(network, network, train_params->nb_images);
update_bias(network, network); update_bias(network, network, train_params->nb_images);
printf("\rÉpoque [%d/%d]\tImage [%d/%d]\tAccuracy: "YELLOW"%0.1f%%"RESET" ", i, epochs, BATCHES*(j+1), nb_images_total, current_accuracy*100); printf("\rÉpoque [%d/%d]\tImage [%d/%d]\tAccuracy: "YELLOW"%0.1f%%"RESET" ", i, epochs, BATCHES*(j+1), nb_images_total, current_accuracy*100);
fflush(stdout); fflush(stdout);
@ -230,6 +259,7 @@ void train(int dataset_type, char* images_file, char* labels_file, char* data_di
#endif #endif
write_network(out, network); write_network(out, network);
} }
free(shuffle_index);
free_network(network); free_network(network);
#ifdef USE_MULTITHREADING #ifdef USE_MULTITHREADING
free(tid); free(tid);

View File

@ -3,7 +3,7 @@
#include "include/update.h" #include "include/update.h"
#include "include/struct.h" #include "include/struct.h"
void update_weights(Network* network, Network* d_network) { void update_weights(Network* network, Network* d_network, int nb_images) {
int n = network->size; int n = network->size;
int input_depth, input_width, output_depth, output_width, k_size; int input_depth, input_width, output_depth, output_width, k_size;
Kernel* k_i; Kernel* k_i;
@ -24,7 +24,7 @@ void update_weights(Network* network, Network* d_network) {
for (int b=0; b<output_depth; b++) { for (int b=0; b<output_depth; b++) {
for (int c=0; c<k_size; c++) { for (int c=0; c<k_size; c++) {
for (int d=0; d<k_size; d++) { for (int d=0; d<k_size; d++) {
cnn->w[a][b][c][d] -= network->learning_rate * d_cnn->d_w[a][b][c][d]; cnn->w[a][b][c][d] -= (network->learning_rate/nb_images) * d_cnn->d_w[a][b][c][d];
d_cnn->d_w[a][b][c][d] = 0; d_cnn->d_w[a][b][c][d] = 0;
} }
} }
@ -36,7 +36,7 @@ void update_weights(Network* network, Network* d_network) {
Kernel_nn* d_nn = dk_i->nn; Kernel_nn* d_nn = dk_i->nn;
for (int a=0; a<input_width; a++) { for (int a=0; a<input_width; a++) {
for (int b=0; b<output_width; b++) { for (int b=0; b<output_width; b++) {
nn->weights[a][b] -= network->learning_rate * d_nn->d_weights[a][b]; nn->weights[a][b] -= (network->learning_rate/nb_images) * d_nn->d_weights[a][b];
d_nn->d_weights[a][b] = 0; d_nn->d_weights[a][b] = 0;
} }
} }
@ -46,7 +46,7 @@ void update_weights(Network* network, Network* d_network) {
int input_size = input_width*input_width*input_depth; int input_size = input_width*input_width*input_depth;
for (int a=0; a<input_size; a++) { for (int a=0; a<input_size; a++) {
for (int b=0; b<output_width; b++) { for (int b=0; b<output_width; b++) {
nn->weights[a][b] -= network->learning_rate * d_nn->d_weights[a][b]; nn->weights[a][b] -= (network->learning_rate/nb_images) * d_nn->d_weights[a][b];
d_nn->d_weights[a][b] = 0; d_nn->d_weights[a][b] = 0;
} }
} }
@ -57,7 +57,7 @@ void update_weights(Network* network, Network* d_network) {
} }
} }
void update_bias(Network* network, Network* d_network) { void update_bias(Network* network, Network* d_network, int nb_images) {
int n = network->size; int n = network->size;
int output_width, output_depth; int output_width, output_depth;
@ -75,7 +75,7 @@ void update_bias(Network* network, Network* d_network) {
for (int a=0; a<output_depth; a++) { for (int a=0; a<output_depth; a++) {
for (int b=0; b<output_width; b++) { for (int b=0; b<output_width; b++) {
for (int c=0; c<output_width; c++) { for (int c=0; c<output_width; c++) {
cnn->bias[a][b][c] -= network->learning_rate * d_cnn->d_bias[a][b][c]; cnn->bias[a][b][c] -= (network->learning_rate/nb_images) * d_cnn->d_bias[a][b][c];
d_cnn->d_bias[a][b][c] = 0; d_cnn->d_bias[a][b][c] = 0;
} }
} }
@ -84,7 +84,7 @@ void update_bias(Network* network, Network* d_network) {
Kernel_nn* nn = k_i->nn; Kernel_nn* nn = k_i->nn;
Kernel_nn* d_nn = dk_i->nn; Kernel_nn* d_nn = dk_i->nn;
for (int a=0; a<output_width; a++) { for (int a=0; a<output_width; a++) {
nn->bias[a] -= network->learning_rate * d_nn->d_bias[a]; nn->bias[a] -= (network->learning_rate/nb_images) * d_nn->d_bias[a];
d_nn->d_bias[a] = 0; d_nn->d_bias[a] = 0;
} }
} else { // Pooling } else { // Pooling

View File

@ -12,11 +12,23 @@
if (network1->var != network2->var) { \ if (network1->var != network2->var) { \
printf_error("network1->" name " et network2->" name " ne sont pas égaux\n"); \ printf_error("network1->" name " et network2->" name " ne sont pas égaux\n"); \
if (indice != -1) { \ if (indice != -1) { \
printf(BOLDBLUE"[ INFO_ ]" RESET " indice: %d\n", indice); \ printf(BOLDBLUE"[ INFO_ ]" RESET " indice: %d\n", indice); \
} \ } \
return false; \ return false; \
} }
void swap(int* tab, int i, int j) {
int tmp = tab[i];
tab[i] = tab[j];
tab[j] = tmp;
}
void knuth_shuffle(int* tab, int n) {
for(int i=1; i < n; i++) {
swap(tab, i, rand() %i);
}
}
bool equals_networks(Network* network1, Network* network2) { bool equals_networks(Network* network1, Network* network2) {
int output_dim; int output_dim;
checkEquals(size, "size", -1); checkEquals(size, "size", -1);

View File

@ -66,7 +66,8 @@ void network_modification(Network* network, uint32_t nb_modifs);
void network_initialisation(Network* network); void network_initialisation(Network* network);
/* /*
* Les deux réseaux donnés sont supposés de même dimensions * Les deux réseaux donnés sont supposés de même dimensions,
* Applique les modifications contenues dans delta à network
*/ */
void patch_network(Network* network, Network* delta, uint32_t nb_modifs); void patch_network(Network* network, Network* delta, uint32_t nb_modifs);

View File

@ -245,6 +245,7 @@ void train(int epochs, int layers, int neurons, char* recovery, char* image_file
for (int j=0; j < nb_threads; j++) { for (int j=0; j < nb_threads; j++) {
free(train_parameters[j]); free(train_parameters[j]);
} }
free(shuffle_indices);
free(train_parameters); free(train_parameters);
// On libère les espaces mémoire utilisés spécialement sur le CPU // On libère les espaces mémoire utilisés spécialement sur le CPU
free(tid); free(tid);