Programmation pour l’IA
Rendez vous sur la page https://playground.tensorflow.org.
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 ?
Même question avec deux couches ne contenant qu’un seul neurone chacune (\(N_1 \to N_2 \to res\))
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 ?
Que se passe-t-il si vous changez le pas de gradient ? La taille des batches ?
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 = 6On 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)__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\)).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\).Sig(x) et
dSig(x) calculant la sigmoĂŻde. Testez comme au-dessus.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\)).