{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "

TP3: Perceptron Mutli-Couches

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans ce TP, nous allons implémenter un réseau de neurone simple à deux couches cachée à partir de zéro.\n", "Même si vous utiliserez des bibliothèques de deep learning comme Pytorch ou Tensorflow par par la suite, l'implémentation d'un réseau à partir de zéro au moins une fois est un exercice extrêmement util, essentiel pour concevoir et optimiser ses propres modèles efficacement.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install numpy\n", "!pip install sklearn\n", "\n", "# Importations des package utiles\n", "import matplotlib # Pour les tracés\n", "import matplotlib.pyplot as plt \n", "import numpy as np # Pour réaliser des opération sur des matrices efficacement\n", "\n", "# Nous utiliserons la bibliothèque sklearn pour comparer notre réseau de neurone \n", "# à celui d'une approche plus simple comme une régression logistique\n", "import sklearn \n", "import sklearn.datasets\n", "import sklearn.linear_model\n", "\n", "from math import exp,log\n", "\n", "# Pour afficher les plots inline et régler l'affichage\n", "%matplotlib inline\n", "matplotlib.rcParams['figure.figsize'] = (10.0, 8.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Generation d'un dataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Commençons par générer un jeu de données avec lequel nous pourrons jouer. La bibliothèque de machine learning scikit-learn dispose de quelques générateurs de données utiles, ce qui nous évite d'avoir à écrire le code nous-mêmes. Nous utiliserons la fonction make_moons, qui crée un dataset à deux classes d'exemples en deux dimensions en forme de deux demi-lunes : chacune des demi-lunes correspond à une classe." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.random.seed(1)\n", "X, y = sklearn.datasets.make_moons(n_samples=300, noise=0.20) # On crée un dataset avec 300 éléments" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 1. Afficher les coordonnées et les labels des deux premiers éléments du dataset." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Vous pouvez utilisez des f-string comme proposés ci-dessous (https://www.geeksforgeeks.org/formatted-string-literals-f-strings-python/)\n", "#print(f\"Le premier point de coordonnées {TODO} a comme label {TODO}\")\n", "#print(f\"Le deuxième point de coordonnées {TODO} a comme label {TODO}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous pouvons afficher ce dataset facilement à l'aide de Matplotlib en utilisant des couleurs pour faire apparaître les labels $y$ : " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.scatter(X[:,0], X[:,1], s=50, c=y+1, cmap=plt.cm.Spectral)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "L'ensemble de données que nous avons généré comporte deux classes, représentées par des points rouges et bleus. Vous pouvez considérer les points bleus comme des patients sains et les points rouges comme des patients malades, les axes $x_1$ et $x_2$ étant des mesures médicales.\n", "\n", "Notre objectif est d'entraîner un classificateur d'apprentissage automatique qui prédit la classe correcte (sain ou malade) à partir des coordonnées $x_1$ et $x_2$. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Trouver la meilleur droite manuellement" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dans cette partie nous allons essayer de trouver la meilleure droite qui sépare notre nuage de points manuellement." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question 2 Crée une fonction qui renvoie 1 si un point de coordonnées $(x_1,x_2$) est en dessous de la droite de coefficient directeur $a$ et d'ordonnée à l'origine $b$." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def pred_linear(a: float, b: float, x1: float, x2: float):\n", " # TODO\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il est important de pouvoir évaluer les performances de nos approches grâce à des métriques. Ici, on choisira l'accuracy qui est simplement le nombre d'éléments bien classifié divisé par le nombre total d'éléments. \n", "\n", "Pour en savoir plus sur les métriques de classification comme la précision, le rappel et leur lien avec l'accuracy, vous pouvez consulter l'excellente Wikipedia suivante (si vous comptez faire du Machine Learning par la suite, la notion de précision/rappel est un classique) https://en.wikipedia.org/wiki/Precision_and_recall\n", "\n", "Question 3. Compléter la fonction accuracy suivante. (1 ligne Python avec une compréhension de liste)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def accuracy(y_true, y_pred):\n", " \"\"\"\n", " Args:\n", " y_true (list[int]): liste d'entier dans {0,1} contenant les labels\n", " y_pred (list[int]): liste d'entier dans {0,1} contenant les prédictions du modèle \n", "\n", " Returns:\n", " float: Accuracy du modèle\n", " \n", " Usage examples:\n", " >>> accuracy([0,0,1], [0,1,1])\n", " 0.666...\n", " \"\"\"\n", " pass #TODO" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#### fonction d'affichage\n", "def plot_decision_boundary(pred_func):\n", " \"\"\"\n", " Affiche les frontières de décision d'une fonction de prédiction binaire.\n", " \"\"\"\n", " # Définir les dimensions de la grille et donner un peu de marge pour l'affichage\n", " x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5\n", " y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5\n", " h = 0.01\n", " # Générer la grille de points avec un distance de h entre eux\n", " xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))\n", " # Tracer la frontière de décision\n", " Z = pred_func(np.c_[xx.ravel(), yy.ravel()])\n", " Z = Z.reshape(xx.shape)\n", " # Afficher le contour et les points d'entrainements\n", " plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)\n", " plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question 4. Jouez à la main avec des paramètres $a$ et $b$ pour obtenir plusieurs frontrières de décisions linéaires et essayez d'obtenir au moins 80% d'accuracy.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 42 #TODO\n", "b = 42 #TODO\n", "def prediction(A, a, b, func):\n", " return np.array([func(a=a, b=b, x1=x[0], x2=x[1]) for x in A])\n", "plot_decision_boundary(lambda x: prediction(x, a, b, pred_linear))\n", "print('le score obtenu est de: ', accuracy(y, prediction(X, a, b, pred_linear)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Régression Logistique" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scikit-learn possède des modèles tels que la régression logistique qui permet de trouver les paramètres a et b optimaux:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "classifier = sklearn.linear_model.LogisticRegressionCV()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 5. Entraîner ce modèle de régression logistique sur le dataset (X,y). Vous pouvez vous aider de la documentation de Scikit-learn pour voir comment entrainer un modèle sur des données : https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#TODO (1 ligne)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une fois le modèle entrainé, on peut l'utiliser pour prédire et tracer la frontière de décision :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plot_decision_boundary(lambda x: classifier.predict(x))\n", "plt.title(\"Logistic Regression\")\n", "print('le score obtenu est de: ', accuracy(y, classifier.predict(X)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 6. Qu'observez vous ? Est ce que un tel résultat était prévisible ? Que faire pour améliorer nos prédictions ?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#TODO" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Question Bonus. Retrouver les coefficients $a, b$ obtenus par la régression logistique de Scikit-learn." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#TODO" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Réseau de neurones" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous allez maintenant créer un réseau de neurones pour résoudre le probleme précédent." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# On va réutiliser le même datasets que précédemment, attention X et y seront des variables globales\n", "# pour la suite (à éviter en général, mais simplifie les notations pour ce petit TP)\n", "np.random.seed(1)\n", "X, y = sklearn.datasets.make_moons(300, noise=0.20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 7 Compléter les variables et les fonctions suivantes pour coder un réseau de neurones à **deux couches cachées**. Chaque couche cachée aura pour l'instant **10 neurones** et on utilisera un learning rate de 1e-2." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# nombre d'exemples dans la base d'entrainement\n", "N = 0 #TODO\n", "\n", "# dimension de l'entrée du réseau\n", "d_input = 0 #TODO \n", "\n", "# dimension de la sortie du réseau \n", "d_output = 0 #TODO\n", "\n", "# dimension des couches cachées i.e. nombre de neurones des couches cachées\n", "d_hidden = 0 #TODO\n", "\n", "# Paramètres de la descente de gradient :\n", "# learning rate pour la descente de gradient\n", "epsilon = 0 #TODO\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 8 Compléter la fonction suivante afin de générer les pamètres de notre réseau de neurones. Pour cela vous utiliserez la bibliothèque random pour généréer des paramètres dans l'intervalle [-0.5, 0.5] grace à la fonction random.random()." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def init_model(d_input: int, d_hidden: int, d_output: int):\n", " \"\"\"\n", " Args:\n", " d_input (int): dimension de l'entrée du réseau\n", " d_hidden (int): dimension de la sortie du réseau \n", " d_output (int): dimension des couches cachée\n", "\n", " Returns:\n", " dict: Dictionnaire contenant 4 clefs, les poids/biais (W1,b1) et (W2,b2) du réseau de neurone. \n", " Chacun de ces poids et de ces biais sont des listes ou liste de listes de float.\n", " \"\"\"\n", " # Initialisation des paramètes aléatoires\n", " random.seed(0)\n", " # Première couche de taille d_input x d_hidden\n", " W1 = [] #TODO\n", " # Biais de la première couche vecteur de taille d_hidden\n", " b1 = [] #TODO\n", " # Seconde couche de taille d_hidden x d_output\n", " W2 = [] #TODO\n", " # Biais de la seconde couche\n", " b2 = [] #TODO\n", " # Le modèle retourné à la fin, c'est un dictionnaire des poids et de biais\n", " model = { 'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}\n", " return model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 9 Implémenter les fonctions mathématiques suivantes qui pourront être utiles dans la suite. Tous les vecteurs $v_1,v_2$ sont des listes pythons et les matrices $X$ et $W$ sont des listes de listes." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Produit sclaire entre deux vecteurs\n", "def dot_product(v1, v2):\n", " pass #TODO3\n", "\n", "# Additionner deux vecteurs\n", "def add_bias(v1, v2):\n", " pass #TODO\n", "\n", "# Obtenir la columns numero \"index\" de W\n", "def get_columns(W, index):\n", " pass #TODO\n", "\n", "# Transposer une matrice\n", "def transpose(W):\n", " pass #TODO\n", "\n", "# Multiplication entre deux matrices ()\n", "def matrix_multiplication(X, W):\n", " pass #TODO\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 10 Compléter la fonction forward_layer en effectuant l'opération suivante:\n", " $$ X \\times W + b $$\n", " Dans laquelle X représent l'entrée, W les poids et et b les biais.\n", " Compléter la fonction sigmoid et feed_forward." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def forward_layer(X, W, b):\n", " pass #TODO" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def sigmoid(x):\n", " return 0 # TODO" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def forward_function(X,W1,b1,W2,b2): \n", " #TODO\n", " z1 = 0 #Sortie de la première couche\n", " a1 = 0 #Activation sigmoid de la première couche\n", " z2 = 0 #Sortie de la deuxième couche\n", " exp_scores = 0# Calculer exp(z2)\n", " probs = 0 # Appliquer la fonction d'activation softmax sur z2\n", " return probs\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Tester votre résultat sur un dataset aléatoire :\n", "np.random.seed(1)\n", "model_test = init_model(4,3,2)\n", "X_debug = [[random.random() for i in range(2)]] # Test avec un exemple en dimension 2}\n", "forward_function(X_debug, model_test['W1'], model_test['b1'], model_test['W2'], model_test['b2'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous devriez trouver : [[0.480..., 0.519...]]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 11 On rappel les équations du feed forward (tout est sous forme matricielles avec la convention de considérer que chaque exemple est un vecteur ligne), donc $X\\in\\mathbb{R}^{N\\times d_{input}}$,$W_1\\in\\mathbb{R}^{d_{input}\\times d_{hidden}}$, etc). Compléter les équations de la back propagation (cela vous demandera un peu de calcul mais pas besoin de mettre le détail ici) et compléter la fonction sigmoid,forward function et train_model. La descente de gradient sur W1 vous est fournie. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$$\n", "\\begin{aligned}\n", "z_1 & = XW_1 + b_1 \\\\ \n", "a_1 & = sigmoid(z_1)=\\frac{1}{1+\\exp(-z_1)} \\\\\n", "z_2 & = a_1W_2 + b_2 \\\\\n", "a_2 & = \\hat{y} = \\mathrm{softmax}(z_2)\\\\\n", "L(y,\\hat{y}) & = - \\frac{1}{N} \\sum_{n \\in N} \\sum_{i \\in C} y_{n,i} \\log\\hat{y}_{n,i}\n", "\\end{aligned}\n", "$$\n", "Back propagation :\n", "$$\n", "\\begin{aligned}\n", "& \\delta_3 = \\\\\n", "& \\delta_2 = \\\\\n", "& \\frac{\\partial{L}}{\\partial{W_2}} = \\\\\n", "& \\frac{\\partial{L}}{\\partial{b_2}} =\\\\\n", "& \\frac{\\partial{L}}{\\partial{W_1}} = \\\\\n", "& \\frac{\\partial{L}}{\\partial{b_1}} = \\\\\n", "\\end{aligned}\n", "$$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "\n", "def train_model(model, nn_hdim, num_epochs=1, print_loss=False):\n", "\n", " W1 = model['W1']\n", " b1 = model['b1']\n", " W2 = model['W2']\n", " b2 = model['b2']\n", "\n", "\n", " # Gradient descent. For each batch...\n", " for i in range(0, num_epochs):\n", " \n", " # Forward propagation : copier/coller l'intérieur de la forward_function définit précédemment\n", " # (on ne l'utilise pas car on a besoin des variable intermédiaires comme a1 pour la descente de gradient\n", " # ci-dessous)\n", " z1 = 0 #Sortie de la première couche\n", " a1 = 0 #Activation sigmoid de la première couche\n", " z2 = 0 #Sortie de la deuxième couche\n", " exp_scores = 0# Calculer exp(z2)\n", " probs = 0\n", " # Calcul de la loss (c)\n", " correct_logprobs = 0 # Calcul de la cross entropy pour chaque exemple\n", " data_loss = 1./N * sum(correct_logprobs) # Loss totale\n", " \n", " \n", " # Backpropagation\n", " #TODO\n", " delta3 = 0\n", " dW2 = 0\n", " db2 = 0\n", " delta2 = 0\n", " delta2 = 0\n", " dW1 = 0\n", " db1 = 0\n", " \n", " # Descente de gradient\n", " W1 =[[w - epsilon * d for d, w in zip(dW1_row, W1_row)]for dW1_row, W1_row in zip(dW1, W1)]\n", " #TODO\n", " b1 = 0\n", " W2 = 0\n", " b2 = 0\n", " # Mise à jour des poids et des biais\n", " model = { 'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}\n", " \n", " # Affichage de la loss\n", " if print_loss and i % 50 == 0:\n", " print(\"Loss a l'époque %i: %f\" %(i, data_loss))\n", " \n", " return model" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous aurons besoin d'une fonction de prédiction qui se sert de notre modèle entrainé pour renvoyer des prédictions. Contrairement aux sorties du modèle qui sont des float dans [0,1] pour chaque classe, la prédiction du modèle vaut 1 sur la classe dont le score est maximale et 0 ailleurs. On utilise la fonction argmax de numpy pour faire cela automatiquement.\n", "\n", " Question 11 Compléter la fonction predict() :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def predict(model, x):\n", " W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']\n", " # Forward propagation, exactement comme avant\n", " z1 = 0 #TODO\n", " a1 = 0 #TODO\n", " z2 = 0 #TODO\n", " exp_scores = 0 #TODO\n", " probs = 0 #TODO\n", " return np.argmax(probs, axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 12 Entrainer le modele pour différents nombre d'epochs et commenter vos résultats." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model = init_model(d_input,d_hidden,d_output)\n", "model = train_model(model,d_hidden, num_epochs=700, print_loss=True)\n", "print(\"L'accuracy finale obtenue est de :\", accuracy(y, predict(model, X)))\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Plot the decision boundary\n", "plot_decision_boundary(lambda x: predict(model, x))\n", "plt.title(\"Decision Boundary for hidden layer size 3\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 13 \n", "Effectuer une validation croisée pour trouver des bons hyper-paramètres sur votre modèle. Pourquoi laisser une partie du jeu de donnée de côté à chaque entrainement ?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Application sur un vrai jeu de données" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous allons maintenant appliquer notre modèle sur un vrai jeu de donnée bien connu dans le monde du Machine Learning : le MNIST (https://en.wikipedia.org/wiki/MNIST_database) qui est dans Sklearn." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "digits = sklearn.datasets.load_digits()\n", "n_samples = len(digits.images)\n", "data = digits.images.reshape((n_samples, -1))\n", "\n", "_, axes = plt.subplots(nrows=1, ncols=10, figsize=(20, 3))\n", "for ax, image, label in zip(axes, digits.images, digits.target):\n", " ax.set_axis_off()\n", " ax.imshow(image, cmap=plt.cm.gray_r, interpolation=\"nearest\")\n", " ax.set_title(\"Training: %i\" % label)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = digits.images.reshape((n_samples, -1)) # On reshape les images en vecteur \n", "y = digits.target\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Question 14 Compléter les dimensions d'entrée et de sortie de votre réseau pour qu'il soit adapté au Dataset MNIST et relancer l'entrainement (attention, l'entrainement prendra maintenant quelques minutes sans optimisation du code supplémentaire). Jouez sur les hyper-paramètres pour obtenir le meilleur score possible sur MNIST." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "N = len(X) \n", "d_input = 0 #TODO\n", "d_output = 0 #TODO \n", "d_hidden = 20 \n", "\n", "# Parametre de la descente de gradient\n", "epsilon = 0.001 # le learning rate doit être plus petit qu'avant sinon l'entrainement diverge" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model = init_model(d_input,d_hidden,d_output)\n", "model = train_model(model,d_hidden, num_epochs=100, print_loss=True)\n", "print(\"L'accuracy finale obtenue est de :\", accuracy(y, predict(model, X)))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Questions 15 (Bonus) Il y a de nombreuses manières de rendre votre réseau de neurone plus performant, vous pouvez vous renseigner sur les points d'améliorations suivants de votre choix, expliquer leur utilité et implémenter ceux que vous voulez en analysant les nouveaux résultats obtenus (sur le jeu de donnée de votre choix). N'hésitez pas à être curieux et à chercher de bonnes ressources pour vous aider !\n", "* Ajout de Weight Decay (https://fr.wikipedia.org/wiki/Weight_decay)\n", "* Utilisation de la bibliothèque Numpy pour gérer les opérations matricielles, plutôt que d'utiliser des listes de listes Python (cela simplifiera votre code et devrait diminuer les temps d'entrainement de plusieurs ordres de magnitude selon la taille de votre réseau, ce qui vous permettra d'entrainer des plus grand réseaux et de mieux optimiser les hyperparamètres sur le dataset MNIST)\n", "* Ajout de couches plus profondes (faire en sorte que le nombre de couches soit un paramètre du modèle)\n", "* Descente de Gradient Stochastique ou par Batch (https://fr.wikipedia.org/wiki/Algorithme_du_gradient_stochastique)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 4 }