De Biasi Loris & Dos Reis Cedric
04.04.2020 - Machine Learning - High Performance Computing
# Rapport - Computational Graph
## Intro
Ce travail avait pour objectif de nous familiariser avec le machine learning et plus précisement les perceptrons multi couche ainsi la descente de gradient stochastique. Pour ce faire, une base de libraire nous était fourni, il ne nous restait plus qu'à compléter certaines parties.
Une brève explication des divers classes sera faite dans ce document, ainsi qu'une démonstration de nos resultats.
Pour commencer, nous allons parler de la classe MLP qui est la classe principale.
## Multi Layer Perceptron (MLP)
Le MLP de ce projet est constitué de trois couches (une seule couche cachée). La couche d'entrée (input) contient deux noeuds, l'unique couche cachée (hidden) contient 100 noeuds et finalement, la couche de sortie (output) contient trois noeuds.
Le graphe computationnel est constitué de deux "linear layer". Le premier prenant en entrée les deux noeuds d'entrés et en sortie les 100 noeuds, puis le second prenant les 100 noeuds en entrés et les 3 noeuds de sorties en sortie.
Image du graphe du projet (la couche cachée devrait contenir 100 noeuds et non pas cinq):

La première couche va uniquement recevoir les données d'entrée. Elle transmettra donc uniquement les valeurs reçues à la couche enfant.
La couche cachée (hidden layer) va elle recevoir les valeurs d'entrée ainsi que les poids associés à chaque lien. Chaque noeud va donc recevoir la valeur de chaque noeud parent (gauche du graphe) ainsi que le poids du lien. Pour calculer la valeur de sortie de ce noeud, la fonction d'activation ReLu est utilisée. c'est le résultat de cette fonction qui sera transmis à la couche enfant.
De la même façon que pour la couche cachée, chaque noeud de la couche de sortie va recevoir la valeur de chaque noeud parent ainsi que le poids associé au lien.
==explain what are the local derivatives that the node computes==
## Linear (nn)
La classe Linear permet d'appliquer une application linéaire aux données d'entrée. Elle a deux méthodes. Une méthode "\_\_init\_\_" et une méthode "\_\_call\_\_". La méthode "\_\_init\_\_" permet d'instancier la liste contenant tous les poids ainsi qu'une liste contenant tous les biais. La méthode "\_\_call\_\_" permet quant à elle une "forward pass", cela consiste à multiplier la liste contenant toutes les entrées, par la liste contenant tous les poids, puis d'y ajouter la valeur des biais.
## Cross Entropy Loss
La classe Cross Entropy Loss permet, comme son nom l'indique, de calculer la "perte" (loss) en utilisant la méthode "Cross Entropy". Elle contient deux méthodes, une méthode "\_forward" et une méthode "\_\_call\_\_".
La méthode "\_forward" permet de calculer la perte lors de la partie "forward" (de Input à Output). Elle est basée sur la méthode du même nom de Pytorch. Cette méthode va calculer la perte puis, selon le type de réduction sélectionné (par défaut : mean), retourner soit : la moyenne, la somme ou bien le résultat non modifié.
La méthode "\_\_call\_\_" est quant à elle une méthode wrapper qui va appeller "\_forward". Elle permet de corriger un bug lors de la backpropagation. Pour corriger le bug, elle va créer une copie des données d'entrée, effectuer la méthode "\_forward" dessus puis stocker le résultat dans une nouvelle variable (result), calculer le gradient en utilisant la copie et finalement copier les valeurs de gradient et la fonction utiliser dans la nouvelle variable (result).
## SGD Optimizer
La méthode step de la classe SGD consiste simplement à appliquer le mécanisme de mise à jour de la descente de gradient stochasique. Pour cela, toutes les couches sont parcourues et pour chaque couche, le résultat de la multiplication du taux d'apprentissage (learning rate, lr) et des gradients de chaque noeud est soustraite à la couche.
```[python]
for key in self.parameters.params:
self.parameters.params[key].data -= self.lr * self.parameters.params[key].grad
```
## Variable
La class Variable représente chaque noeud du graphe computationnel. Elle contient différentes informations permettant de construire ce noeud et les noeud qui suivent. Ses informations sont : l'opération d'origine qui a créé ce noeud, le gradient local, des références sur ces noeuds enfants.
La méthode "backward" sert à faire la descente de gradient. Elle appelle le backward de l'opération à l'origine du noeud. Il est possible que l'opération d'origine soit indéfinit si le noeud est un noeud feuille (c.à.d sans parent). La méthode reçoit uniquement un booléen en argument. Ce booléen détermine l'utilisation ou non des valeurs sauvegardées dans la méthode "update_grad".
La méthode "update_grad" sert à mettre à jour le gradient local du noeud par rapport à ces enfants. Elle reçoit plusieurs arguments, le premier sert à calculer le nouveau gradient local, le deuxième est une référence au noeud enfant qui a appelé la méthode backward et le troisième est un booléen. Ce booléen sert à déterminer si les références des "enfants" et de l'opération d'origine sont à sauvegarder afin de pouvoir les réutiliser. Si on considère que la valeur du gradient local à l'initialisation est 0 alors la mise à jour n'est qu'une addition avec la valeur reçue.
## Tests et résultat
Voici les résultats que nous obtenons avec la seed et les valeurs données. Nous obtenons bien 90% de précisions, comme dit dans la documentation fournit.
```
Epoch 1/1000, loss 1.4014, acc 0.33
Epoch 2/1000, loss 1.2659, acc 0.35
Epoch 3/1000, loss 1.1786, acc 0.31
Epoch 4/1000, loss 1.0813, acc 0.44
Epoch 5/1000, loss 1.0077, acc 0.57
Epoch 6/1000, loss 0.9524, acc 0.62
Epoch 7/1000, loss 0.9146, acc 0.73
Epoch 8/1000, loss 0.8884, acc 0.79
Epoch 9/1000, loss 0.8498, acc 0.79
Epoch 10/1000, loss 0.8243, acc 0.79
Epoch 20/1000, loss 0.6498, acc 0.86
Epoch 30/1000, loss 0.5576, acc 0.89
Epoch 40/1000, loss 0.4963, acc 0.89
Epoch 50/1000, loss 0.4508, acc 0.89
Epoch 60/1000, loss 0.4086, acc 0.91
Epoch 70/1000, loss 0.4070, acc 0.89
Epoch 80/1000, loss 0.3781, acc 0.90
Epoch 90/1000, loss 0.3642, acc 0.91
Epoch 100/1000, loss 0.3453, acc 0.91
Epoch 200/1000, loss 0.2694, acc 0.91
Epoch 300/1000, loss 0.2416, acc 0.92
Epoch 400/1000, loss 0.2259, acc 0.92
Epoch 500/1000, loss 0.2101, acc 0.93
Epoch 600/1000, loss 0.2191, acc 0.92
Epoch 700/1000, loss 0.2192, acc 0.91
Epoch 800/1000, loss 0.1728, acc 0.94
Epoch 900/1000, loss 0.1821, acc 0.93
Epoch 1000/1000, loss 0.1725, acc 0.94
Accuracy on test set: 0.90
```
Représentation graphique du résultat :
