\( \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{\X}{\mathbf{X}} \newcommand{\z}{\mathbf{z}} \newcommand{\w}{\mathbf{w}} \newcommand{\W}{\mathbf{W}} \newcommand{\a}{\mathbf{a}} \newcommand{\b}{\mathbf{b}} \newcommand{\B}{\mathbf{B}} \newcommand{\dpartial}[2]{{\partial #1 \over \partial #2}} \newcommand{\d}{\mathbf{d}} \)

Variation sur les Réseaux de Neurones

Programmation pour l’IA

Florent Capelli

Université d’Artois, CRIL

Limites des Réseaux de Neurones

Réseaux de Neurones

Modèle implémentant une transformation continue et différentiable de \(\mathbb{R}^n\) dans \(\mathbb{R}^m\) :

g n0_0 x₁ n1_0 ReLU n0_0->n1_0 n1_1 ReLU n0_0->n1_1 n1_2 ReLU n0_0->n1_2 n1_3 ReLU n0_0->n1_3 n0_1 x₂ n0_1->n1_0 n0_1->n1_1 n0_1->n1_2 n0_1->n1_3 n0_2 x₃ n0_2->n1_0 n0_2->n1_1 n0_2->n1_2 n0_2->n1_3 n2_0 σ n1_0->n2_0 n2_1 σ n1_0->n2_1 n2_2 σ n1_0->n2_2 n1_1->n2_0 n1_1->n2_1 n1_1->n2_2 n1_2->n2_0 n1_2->n2_1 n1_2->n2_2 n1_3->n2_0 n1_3->n2_1 n1_3->n2_2 n3_0 ReLU n2_0->n3_0 n3_1 ReLU n2_0->n3_1 n3_2 ReLU n2_0->n3_2 n3_3 ReLU n2_0->n3_3 n3_4 ReLU n2_0->n3_4 n2_1->n3_0 n2_1->n3_1 n2_1->n3_2 n2_1->n3_3 n2_1->n3_4 n2_2->n3_0 n2_2->n3_1 n2_2->n3_2 n2_2->n3_3 n2_2->n3_4 n4_0 σ n3_0->n4_0 n3_1->n4_0 n3_2->n4_0 n3_3->n4_0 n3_4->n4_0

m = nn.Sequential(

nn.Linear(3,4),

nn.ReLU(),

nn.Linear(4,3),

nn.Sigmoid(),

nn.Linear(3,5),

nn.ReLU(),

nn.Linear(5,1),

nn.Sigmoid()

)

Limites

Comment traiter des signaux : image, son, etc. avec un réseau de \(\R^n\) dans \(\R^m\) ?

Exemple : classifier une image (chat, chien, humain), une musique (rock, rap, opera), un texte etc.

Comment traiter ce genre de données avec des réseaux de neurones ?

Solutions possibles

Réseaux de Neurones Convolutifs

Convolution 1D

\[ (L*K)[i] = \sum_{a=0}^{k-1} L[i+a] \times K[a] \]

Exemple pour un kernel de taille \(3\) :

L 1 4 2 -1 3 8 0 7
K
K1-11
K1-11
K1-11
K1-11
K1-11
K1-11
L*K -1 1 6 4 -5 15

Convolution 2D

  • Pareil pour les matrices
  • Mathématiquement, si \(M\) est une matrice \(n_1 \times n_2\) et \(K\) une matrice \(k_1 \times k_2\),

\[ (M * K)[i,j] = \sum_{a=0}^{k_1-1} \sum_{b=0}^{k_2-1} M[i+a, j+b] \times K[a,b]\]

où \(0 \leq i < n_1-(k_1-1)\) et \(0 \leq j < n_2 - (k_2-1)\).

\(M*K\) est une matrice \((n_1-k_1+1) \times (n_2 - k_2+1)\).

Image et convolution 2D

Étudié depuis longtemps en traitement de l’image.

Convolutions intéressantes connues :

-2 -1 0
-1 1 1
0 1 2

1 1 1
1 1 1
1 1 1

0 1 0
1 -4 1
0 1 0

Ici, la convolution est appliquée sur chaque couleur \((R, G, B)\) indépendamment (on parle de canaux/channels).

Exemple

M
1 0 1 2
2 3 1 2
0 1 2 0
3 2 2 1
K
1 -1
-1 1
M * K
2 -3 0
0 3 -3
-2 -1 1

Padding

Padding le plus courant : ajouter des lignes/colonnes nulles (mais aussi : copier les dernières colonnes, copier les première etc.)

Exemple avec padding

  • Cas d’une convolution avec un kernel \(3 \times 3\)
  • Ajouter deux colonnes et deux lignes de \(0\).
  • \(M*K\) a la même dimension que \(M\).
1 0 1 2
2 3 1 2
0 1 2 0
3 2 2 1
0 0 0 0 0 0
0 1 0 1 2 0
0 2 3 1 2 0
0 0 1 2 0 0
0 3 2 2 1 0
0 0 0 0 0 0
M'
0 0 0 0 0 0
0 1 0 1 2 0
0 2 3 1 2 0
0 0 1 2 0 0
0 3 2 2 1 0
0 0 0 0 0 0
K
0 1 0
1 -4 1
0 1 0
M' * K
-2 5 -1 -5
-4 -8 4 -5
6 3 -4 5
-10 -2 -3 -2

Couche de Convolution

En Pytorch

Trois couches principalement : nn.Conv1d (séquences), nn.Conv2d (image), nn.Conv3d (dans l’espace)

nn.Conv2d(in_channel=3, out_channel=5, kernel_size=2)

Analyse de paramètres

nn.Conv2d(in_channel=3, out_channel=5, kernel_size=2)

Comparons avec une couche linéaire complètement connectée :

Pooling

Exemple avec \(k_1=k_2=2\) et \(f = \max\) :

1 0 1 2
2 3 1 2
0 1 2 0
3 2 2 1
\(f(B_1)\) \(f(B_2)\)
\(f(B_3)\) \(f(B_4)\)
3 2
3 2

En Pytorch

Beaucoup de couches permettant des opérations de “pooling” :

Pas de paramètres entraînables.

Résumé

Pour le traitement d’image (et plus généralement de signaux) :

Limites

On continue à traiter des signaux bornés, donc pas applicable au texte, musique etc.

Besoin d’autres architectures :

Pytorch : quelques bonnes pratiques

Construction de modèles

Pour construire un modèle, on a essentiellement vu nn.Sequential :

nn.Sequential(
    nn.Linear(10, 3),
    nn.ReLU(),
    nn.Linear(3,1),
    nn.Sigmoid()
)
  • + Syntaxe claire
  • + Rapide à mettre en place
  • - Pas de souplesse :
    • pas de preprocessing de l’entrée
    • pas de réutilisation des couches
    • pas contrôle fin : on pourrait vouloir faire \(Ax\) et \(Bx\) pour deux matrices \(A\) et \(B\) différentes et les recombiner.

Par nn.Module

class MyModel(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.dim = dim
        self.c1a = nn.Linear(dim, dim)
        self.c1b = nn.Linear(dim, dim)
        self.c2 = nn.ReLU()
        self.c3 = nn.Linear(2*dim, 1)
        self.output = nn.Sigmoid()
    def forward(self, x):
        ax = self.c1a(x)
        bx = self.c1b(x)
        x = self.c3(self.c2(torch.cat((ax,bx), dim=1)))
        return self.output(x)
  • Implémentation automatique de .to(), .parameters() etc.
  • Permet d’introduire du partage, de la réutilisation etc.
  • Peut se combiner avec nn.Sequential
  • nn.Sequential est elle-même une classe héritée de nn.Module
self.c = nn.Sequential(nn.ReLU(), 
    nn.Linear(2*dim, 1), 
    nn.Sigmoid())

m.train() et m.eval()

Cas le plus courant : c = nn.Dropout(p=0.3)

Enregistrement de modèles

Sauvegarder un modèle entraîné dans un fichier pour :

Sérialisation

nn.Module contient une méthode state_dict() qui sérialise les valeurs des attributs :

m = MyModel(2)
print(m.state_dict())

>>> OrderedDict([
>>> ('c1a.weight', tensor([[ 0.2510, -0.6375],
>>>                       [ 0.2481, -0.6280]])), 
>>> ('c1a.bias', tensor([0.1261, 0.2936])), 
>>> ('c1b.weight', tensor([[-0.5350, -0.5719],
>>>                        [-0.2772, -0.0570]])), 
>>> ('c1b.bias', tensor([ 0.4638, -0.3181])), 
>>> ('c3.weight', tensor([[ 0.2049, -0.2333, -0.3563,  0.1219]])), 
>>> ('c3.bias', tensor([-0.3907]))
>>> ])

Importer/exporter la sérialisation

m = MyModel(2)
train(m) # on entraîne
torch.save(m.state_dict(), "model.pt")
...
m_new = MyModel(2)
d = torch.load("model.pt")
m_new.load_state_dict(d)