Programmation pour l’IA
Florent Capelli
Université d’Artois, CRIL
Modèle implémentant une transformation continue et différentiable de \(\mathbb{R}^n\) dans \(\mathbb{R}^m\) :
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()
)
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 ?
\[ (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 | |||||||||
| K | 1 | -1 | 1 | ||||||
| K | 1 | -1 | 1 | ||||||
| K | 1 | -1 | 1 | ||||||
| K | 1 | -1 | 1 | ||||||
| K | 1 | -1 | 1 | ||||||
| K | 1 | -1 | 1 |
| L*K | -1 | 1 | 6 | 4 | -5 | 15 |
|---|
\[ (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)\).

É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).
| 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 le plus courant : ajouter des lignes/colonnes nulles (mais aussi : copier les dernières colonnes, copier les première etc.)
| 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 |
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)
kernel_size (ici \(2 \times 2\))in_channel (couleur RGB, stéréo etc.) et les
additionne.out_channel fois.padding = 1 par exemple qui rajoutera
deux lignes et deux colonnes de \(0\)
(début et fin).nn.Conv2d(in_channel=3, out_channel=5, kernel_size=2)
Comparons avec une couche linéaire complètement connectée :
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 |
Beaucoup de couches permettant des opérations de “pooling” :
kernel_size permet de spécifier la taille de blocnn.MaxPool1d, nn.MaxPool2d,
nn.MaxPool3dnn.AvgPool1d, nn.AvgPool2d,
nn.AvgPool3d etc.Pas de paramètres entraînables.
Pour le traitement d’image (et plus généralement de signaux) :
On continue à traiter des signaux bornés, donc pas applicable au texte, musique etc.
Besoin d’autres architectures :
Pour construire un modèle, on a essentiellement vu
nn.Sequential :
nn.Modulenn.Module.__init__(self) et
forward(self,x).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)m.train() et m.eval()Cas le plus courant : c = nn.Dropout(p=0.3)
train, cette couche renvoie pour chaque
coefficient \(x\) du tenseur d’entrée :
eval, cette couche “disparaît” (renvoie
toujours \(x\)).Sauvegarder un modèle entraîné dans un fichier pour :
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]))
>>> ])torch.save(m.state_dict(), "model.pt")OrderedDict dans un format binaire
(librairie Python pickle)nn.Module contient une méthode
.load_state_dict()