From 8189a03abf58db2c46f4dd2dc6dbafcccf896aa6 Mon Sep 17 00:00:00 2001 From: eynard Date: Thu, 16 Dec 2021 17:06:51 +0100 Subject: [PATCH] =?UTF-8?q?des=20r=C3=A9sultats,=20mais=20faux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CahierDesCharges.md | 129 ++++++++++++++++++++++++++++++++++++++++++++ sobek/network.py | 74 ++++++++++++++----------- testLearning.py | 13 +++-- timeTest.py | 12 +++++ timeTest2.py | 40 ++++++++++++++ 5 files changed, 231 insertions(+), 37 deletions(-) create mode 100644 CahierDesCharges.md create mode 100644 timeTest.py create mode 100644 timeTest2.py diff --git a/CahierDesCharges.md b/CahierDesCharges.md new file mode 100644 index 0000000..91db5de --- /dev/null +++ b/CahierDesCharges.md @@ -0,0 +1,129 @@ +# Conception d’une librairie de réseaux neuronaux + +## Samedi 23 Octobre 2021 + +**Hugo EYNARD +Thomas BLUSSON +Romain MOREAU +Gabriel CHAVANON** + +Responsable référent: +Gabriel CHAVANON + +Dépot GITEA: +https://dwarves.iut-fbleau.fr/gitiut/blusson/PT21-22-Reseau-Neurones + + +## Sommaire + +- Sommaire +- Cahier des charges fonctionnelles + - Contexte + - Etudes détaillées des objectifs (analyses des besoins) + - Calendrier et priorisation des objectives +- Cahier des charges techniques et méthodologique + - Bibliographie + + +## Cahier des charges fonctionnelles + + +### Contexte + +Client : Pierre VALARCHER (tuteur) + +Description: + +Nous comptons concevoir notre propre librairie de réseaux neuronaux et l’optimiser +par la suite à travers différents tests (reconnaître des caractères manuscrites). Pour +ceci nous ne comptons pas nous appuyer sur des solutions déjà existantes (comme +TensorFlow) mais bien tout réaliser de A à Z. Pour ce qui est de l’optimisation (une +meilleure vitesse d'exécution et une meilleure précision des résultats) on pourra +utiliser des tests et des outils comparatifs (ancienne version de notre projet ou +encore des librairies déjà existantes). + +Contraintes : + +On réalisera ce projet en Python orienté objet et grâce à ses librairies, tout notre +travail sera disponible surnotre dépôt GITEA. + +Existant : + +Nous avons à notre disposition un jeu de données contenant des images de chiffres +annotés avec le chiffre correspondant. Ilnous permettra d'entraînernotre réseau +neuronal pour le tester. + + +### Etudes détaillées des objectifs (analyses des besoins) + +Fonctionnalités : +-Choix de la fonction d’activation pour chaque couche de neurones +-Choix du nombre de couches de neurones +-Choix du type de neurones pour chaque couche +-Choix du nombre de neurones dans chaque couche +-Faire une prédiction à partir d’un réseau de neurone +-Entraîner le réseau de neurones +-Exporter et importer l’état (modèle, biais et poids) d’un réseau de neurones +-Visualiser l'entraînement d’un réseau de neurone à deux entrées + +Bob le développeur possède un jeu de données comportant des images de chats et +de chiens annotés. Il commence par importer Sobek. Ilchoisit ensuitepour son +modèlederéseauneuronald’avoirunematrice 360 par 360 pourentrée.Ildécidede +mettre 2 premières couches de 180 neurones convolutifs,puis 2 couchesde 64 +neuronesdenses(ouclassiques)etenfinunesortiede 2 neuronesdenses(Unpour +chienet un pourchat). Il sépareson jeude donnéesen 2 parties:ilutiliseles 3 +premiers quarts pour entraîner son réseau neuronal et après quelques minutes +d'attente, il utilise le dernier quart pour estimer sa précision. Après quelques +modifications et tests de l'architecture de son réseaupour obtenir une meilleure +précision, Bob est satisfait et peut maintenant utiliserson réseaupour faire des +prédictions. + + +### Calendrier et priorisation des objectives + +Jalon 0 : Cahier des charges (à signer) début novembre +Jalon 1 : Perceptron multicouche (première itération du projet), mi décembre M +1.1 : Partie prédiction du perceptron multicouche M +1.2 : Partie apprentissage du perceptron multicouche M +1.3 : Estimation de la précision du perceptron multicouche S +1.4 : Exportation et importation de l’état du réseau neuronal M +Jalon 2 : Utilisation concrète du réseau neuronal, fin décembre M +Jalon 3 : Visualisation 2D de l’entraînement du réseau neuronal M +Jalon 4 : Réseau de neurones convolutif (version optimisée du perceptron multicouche), fin janvier S +4.1 : Création d’un système de choix de type de couche M +4.2 : Implémentation du neurone convolutif M +Jalon 5 : Utilisation concrète du réseau convolutif, fin février C + + +## Cahier des charges techniques et méthodologique + +Nous avons choisi d’utiliser le Python car c’est un langage plutôt abordable qui est +communément utilisé pour le machine learning. On compte également utiliser Scipy +(NumPy (gestion des tableaux), Matplotlib (visualisation de données sous forme +graphique). + +Méthodologie de travail : +On envisage d’utiliser la méthode agile (1 sprint correspond à 1 jalon) ainsi que le +pair programming. + +On compte utiliser très fréquemment GIT, et faire des tests unitaires dès que +possible. On utilisera make pour l’exécution des tests et des mises en application. + + +### Bibliographie + +https://en.wikipedia.org/wiki/Types_of_artificial_neural_networks + +https://youtu.be/bVQUSndDllU + +https://youtu.be/aircAruvnKk + +https://www.mygreatlearning.com/blog/open-source-python-libraries/ + +https://fr.wikipedia.org/wiki/Matplotlib + +https://brilliant.org/wiki/backpropagation/ + +_Apprentissage machine Clé de l’intelligence artificielle_ - Rémi Gilleron, 2019 + +_Deep Learning with Python, 2nd Edition_ - FrançoisChollet, 2021 \ No newline at end of file diff --git a/sobek/network.py b/sobek/network.py index 8f2714d..186a91e 100755 --- a/sobek/network.py +++ b/sobek/network.py @@ -1,4 +1,5 @@ import numpy as np +import math class network: @@ -10,7 +11,7 @@ class network: self.__inputLayerSize = inputLayerSize oldLayerSize = inputLayerSize for layerSize in layerSizes: - self.__weights.append( np.random.default_rng(42).random((oldLayerSize, layerSize)) ) + self.__weights.append( np.random.random((layerSize, oldLayerSize)) ) oldLayerSize = layerSize self.__biases = [[0]*layerSize for layerSize in layerSizes] self.__weights = np.array(self.__weights, dtype=object) @@ -24,7 +25,7 @@ class network: def __sigmoid(value, derivative=False): if (derivative): return network.__sigmoid(value) * (1 - network.__sigmoid(value)) - return 1/(1+np.exp(-value)) + return 1/(1+math.exp(-value)) def process(self, _input, __storeValues=False): if type(_input) != np.ndarray: @@ -35,61 +36,70 @@ class network: # raise TypeError("The input vector must contain floats!") if (__storeValues): - self.activations = np.array([]) - self.outputs = np.array([]) + self.activations = [] + self.outputs = [] for layerWeights, bias in zip(self.__weights, self.__biases): - _input = np.matmul(_input, layerWeights) + + _input = np.matmul(layerWeights, _input) _input = np.add(_input, bias) if (__storeValues): - print("-------------------") - print(bias) - print("-------------------") - self.activations = np.append(self.activations, _input) - self.activations[len(self.activations)-1] = np.insert(self.activations[len(self.activations)-1], 0, bias) + self.activations.append(_input.copy()) #reLu application - with np.nditer(_input, op_flags=['readwrite'], flags=['refs_ok']) as layer: - for neuron in layer: - neuron = network.__reLu(neuron) + for neuron in range(len(_input)): + _input[neuron] = network.__sigmoid(_input[neuron]) #On peut comparer la performance si on recalcul plus tard if (__storeValues): - self.outputs = np.append(self.outputs, _input) - self.outputs[len(self.outputs)-1] = np.insert(self.outputs[len(self.outputs)-1], 0, 1) + self.outputs.append(_input.copy()) + + self.activations = np.array(self.activations, dtype=object) + self.outputs = np.array(self.outputs, dtype=object) return _input + + def train(self, inputs, desiredOutputs, learningRate): - ErrorSums = [[0]*(len(layer)+1) for layer in self.__biases] + errorSums = [[[0]*(len(neuron)) for neuron in layer] for layer in self.__weights] + self.__errors = [[0]*(len(layer)) for layer in self.__weights] + for _input, desiredOutput in zip(inputs, desiredOutputs): self.__output = self.process(_input, True) self.__desiredOutput = desiredOutput - for layerNumber in range(len(ErrorSums)-1, -1, -1): - ErrorSums[layerNumber][0] += self.__partialDerivative(layerNumber, 0) - for neuronNumber in range(1, len(ErrorSums[layerNumber])): - print("layer : " + str(layerNumber) + " neuron : " + str(neuronNumber)) - ErrorSums[layerNumber][neuronNumber] += self.__partialDerivative(layerNumber, neuronNumber) - for i in range(len(ErrorSums)): - for j in range(len(ErrorSums[i])): - ErrorSums[i][j] = 1 / ErrorSums[i][j] - self.__biases[i, j] -= learningRate * ErrorSums[i][j] - + for layerNumber in range(len(errorSums)-1, -1, -1): + for neuronNumber in range(len(errorSums[layerNumber])): + for weightNumber in range(len(errorSums[layerNumber][neuronNumber])): + #print("layer : " + str(layerNumber) + " neuron : " + str(neuronNumber) + " weight : " + str(weightNumber)) + errorSums[layerNumber][neuronNumber][weightNumber] += self.__partialDerivative(layerNumber, neuronNumber, weightNumber) + + total = 0 + + for i in range(len(errorSums)): + for j in range(len(errorSums[i])): + for k in range(len(errorSums[i][j])): + errorSums[i][j][k] = errorSums[i][j][k] / len(inputs) + total += errorSums[i][j][k] + self.__weights[i][j][k] -= learningRate * errorSums[i][j][k] + + print("Error : " + str(total)) def __Error(self, layer, neuron): - return self.__ErrorFinalLayer(neuron) if (layer == len(self.__weights)-1) else self.__ErrorHiddenLayer(layer, neuron) + if (self.__errors[layer][neuron] == 0 ): + self.__errors[layer][neuron] = self.__ErrorFinalLayer(neuron) if (layer == len(self.__weights)-1) else self.__ErrorHiddenLayer(layer, neuron) + return self.__errors[layer][neuron] def __ErrorFinalLayer(self, neuron): - print(self.activations) - return network.__reLu(self.activations[len(self.activations)-1][neuron], True) * (self.__output[neuron] - self.__desiredOutput[neuron]) + return network.__sigmoid(self.activations[len(self.activations)-1][neuron], True) * (self.__output[neuron] - self.__desiredOutput[neuron]) def __ErrorHiddenLayer(self, layer, neuron): upperLayerLinksSum = 0 for upperLayerNeuron in range(len(self.__weights[layer+1]-1)): #A comparer avec un acces direct au erreurs precalcules upperLayerLinksSum += self.__weights[layer+1][upperLayerNeuron][neuron] * self.__Error(layer+1, neuron) - return network.__reLu(self.activations[layer][neuron], True) * upperLayerLinksSum + return network.__sigmoid(self.activations[layer][neuron], True) * upperLayerLinksSum - def __partialDerivative(self, layer, neuron): - return self.__Error(layer, neuron) * self.outputs[layer][neuron] \ No newline at end of file + def __partialDerivative(self, layer, neuron, weight): + return self.__Error(layer, neuron) * self.outputs[layer-1][weight] \ No newline at end of file diff --git a/testLearning.py b/testLearning.py index d9eb4d3..aa63cab 100644 --- a/testLearning.py +++ b/testLearning.py @@ -6,20 +6,23 @@ random.seed() myNetwork = network(1, 8, 8, 10) -for j in range(5): +for j in range(3000): inputs = [] desiredOutputs = [] + + if (j%50 == 0): + print(j) - for i in range(1000): + for i in range(200): inputs.append([random.randrange(10)]) inputs = np.array(inputs, dtype=object) - for i in range(1000): + for i in range(200): desiredOutputs.append([0]*10) desiredOutputs[i][9 - inputs[i][0]] = 1 desiredOutputs = np.array(desiredOutputs, dtype=object) - myNetwork.train(inputs, desiredOutputs, 0.1) + myNetwork.train(inputs, desiredOutputs, 0.01) print(myNetwork.process(np.array([8.0], dtype=object))) -print(myNetwork.process(np.array([7.0], dtype=object))) \ No newline at end of file +print(myNetwork.process(np.array([1.0], dtype=object))) \ No newline at end of file diff --git a/timeTest.py b/timeTest.py new file mode 100644 index 0000000..cfa7c46 --- /dev/null +++ b/timeTest.py @@ -0,0 +1,12 @@ +import random +import numpy as np + +inputs = [] + +for i in range(10000000): + inputs.append([random.randrange(10)]) +inputs = np.array(inputs, dtype=object) + +inputs = np.insert(inputs, 0, 1, axis=1) + +print(inputs) \ No newline at end of file diff --git a/timeTest2.py b/timeTest2.py new file mode 100644 index 0000000..696144f --- /dev/null +++ b/timeTest2.py @@ -0,0 +1,40 @@ +import random +import numpy as np +import time + +weights = np.random.default_rng(42).random((10, 10)) +biases = np.random.default_rng(42).random(10) +biases = np.array(biases, dtype=object) + +time1 = time.perf_counter() + +for k in range(1000): + _input = [] + for i in range(10): + _input.append(random.randrange(10)) + _input = np.array(_input, dtype=object) + + for f in range(100): + _input = np.matmul(_input, weights) + _input = np.add(_input, biases) + +time2 = time.perf_counter() + +weights = np.random.default_rng(42).random((11, 10)) + +time3 = time.perf_counter() + +for k in range(1000): + _input = [] + for i in range(10): + _input.append(random.randrange(10)) + _input = np.array(_input, dtype=object) + + for f in range(100): + _input = np.insert(_input, 0, 1, axis=0) + _input = np.matmul(_input, weights) + +time4 = time.perf_counter() + +print("Multiplication et addition : " + str(time2-time1) + " secondes") +print("Insertion puis multiplication : " + str(time4-time3) + " secondes") \ No newline at end of file