\( \newcommand{\R}{\mathbb{R}} \newcommand{\Rp}{\mathbb{R_+}} \newcommand{\Z}{\mathbb{Z}} \newcommand{\Rn}{\mathbb{R}^n} \newcommand{\N}{\mathbb{N}} \renewcommand{\to}{\rightarrow} \newcommand{\from}{\colon} \renewcommand{\Vec}{\mathsf{vec}} \newcommand{\Ker}{\mathrm{Ker}} \renewcommand{\Im}{\mathrm{Im}} \newcommand{\rang}{\mathrm{rang}} \newcommand{\O}{\mathcal{O}} \newcommand{\C}{\mathcal{C}} \newcommand{\U}{\mathcal{U}} \newcommand{\x}{\mathbf{x}} \newcommand{\z}{\mathbf{z}} \newcommand{\w}{\mathbf{w}} \newcommand{\a}{\mathbf{a}} \newcommand{\dpartial}[2]{{\partial #1 \over \partial #2}} \newcommand{\d}{\mathbf{d}} \)

TP 5 : Réseaux de Neurones

Programmation pour l’IA

Rendez vous sur la page https://playground.tensorflow.org.

  1. Essayez de séparer le jeux de données 1 avec une réseau de neurones ne contenant qu’un seul neurone. Qu’observez-vous ? Que se passe-t-il si vous faites varier la fonction d’activation ?

  2. Même question avec deux couches ne contenant qu’un seul neurone chacune (\(N_1 \to N_2 \to res\))

  3. Avec un réseau de neurone à une couche : combien de neurones faut-il au minimum pour séparer ce jeux de données ? Même question avec deux couches. Cela dépend-il de la fonction d’activation ?

  4. Que se passe-t-il si vous changez le pas de gradient ? La taille des batches ?

  5. Séparez le jeu de données en ajoutant \(x_1^2\) et \(x_2^2\) en entrée. Pouvez-vous le faire avec un neurone linéaire ?

Reprendre le dernier exercice du TP4 et étudier la solution. Adaptez pour implémenter une régression logistique.

On va commencer à développer un petit programme pour entraîner un réseau de neurones pour bien comprendre comment cela marche. Cette semaine, on voit le cas à un neurone. On étudiera la backpropagation la semaine prochaine et on l’implémentera rapidement avant de voir comment on peut rapidement faire la même choses en utilisant des librairies adaptées.

On commence dans cet exercice par l’entraînement d’un seul neurone par descente de gradient. On va définir une classe pour manipuler des fonctions de \(\R\) dans \(\R\) avec leur dérivée :

class Derivable:
    def __init__(self, f, df):
        self.f = f
        self.d = df
    def __call__(self, x):
        return self.f(x)
        
# exemple
q = Derivable(lambda x: x**2, lambda x: 2*x)
q(3) # retourne 3*3 = 9
q.d(3) # retourne 2*3 = 6

On va aussi considérer une classe “Neurone” qui a une fonction d’activation dérivable et \(k\) paramètre en entrée.

class Neurone:
    def __init__(self, k, f, df, W=None):
        self.activation = Derivable(f,df)
        if W is not None:
            self.W = W
        else:
            self.W = np.random.uniform(0,1,k)
  1. Écrire une méthode __call__(self, X) pour la classe Neurone qui retourne la valeur du neurone sur l’entrée X (qui est donc un tableau de taille \(k\)).
  2. Pour tester:
    1. Écrivez une fonction ReLU(x) et une fonction dReLU(x) calculant respectivement la fonction \(ReLU\) et sa dérivée \({dReLU \over dx}\). On conviendra que, en \(0\), la dérivée de ReLU vaut \(0\).
    2. Créez un neurone ReLU avec 3 entrées avec des poids respectifs \((1, 1, -1)\) et essayez de l’évaluer sur \((1,2,3)\), \((1,1,1)\) et \((5,1,2)\).
    3. Même question en écrivant deux fonctions Sig(x) et dSig(x) calculant la sigmoïde. Testez comme au-dessus.
  3. On va entraîner notre neurone. On va définir une méthode fit(self, X, y, delta=0.3, eps=0.01) qui va faire une descente de gradient jusqu’à ce que le gradient soit plus petit que esp avec un pas de delta. On prendra la fonction d’erreur quadratique normalisée, c’est-à-dire :

\[E = {1 \over |S|} \sum_{(\x,y) \in S} (f(\w^T\x)-y)^2\]

où \(f\) est la fonction d’activation. On a donc :

\[\begin{align*} \dpartial{E}{w_i} & = {2 \over |S|} \sum_{(\x,y) \in S} \dpartial{f}{w_i}(\w^T\x) (f(\w^T\x)-y) \\ & = {2 \over |S|} \sum_{(\x,y) \in S} x_i f'(\w^T\x) (f(\w^T\x)-y) \end{align*}\]

Donc

\[\Delta E = {2 \over |S|} \sum_{(\x,y) \in S} \x f'(\w^T\x) (f(\w^T\x)-y)\]

Testez en faisant une régression linéaire avec le jeu de données iris.csv (pour séparer Setosa des autres en regardant la taille des sépales). On essaiera une fonction d’activation linéaire (identité) et une sigmoïde. Quelle précision obtenez-vous ? (Attention, dans le cas d’une fonction d’activation linéaire, on utilisera le signe pour décider de la classe finale ; pour la sigmoïde, on regardera si on est plus proche de \(0\) ou de \(1\)).