Les composants de PyTorch pour l'apprentissage profond
PyTorch offre plusieurs modules clés pour le développement de réseaux de neurones :
- torch : convertit les tenseurs en format GPU-compatible pour les calculs accélérés.
- torch.autograd : construit automatiquement le graphe de calcul et dérive les gradiants.
- torch.nn : bibliothèque de couches partagées et de fonctions de perte pour les réseaux.
- torch.optim : implémente des algorithmes d'optimisation standards.
Fondations d'un réseau de neurones
Un réseau typique se compose de :
- Couches : blocs de construction élémentaires.
- Modèle : assemblage de couches formant l'architecture complète.
- Fonction de perte : objectif à minimiser pour ajuster les paramètres.
- Optimiseur : algorithme guidant la minimisation de la perte.
Le module torch.nn en détail
Construction du modèle avec nn.Module
Deux approches principales existent pour définir des couches : nn.Module et nn.functional. La première hérite de la classe Module, encapsule les définitions de couches et la méthode forward, et gère automatiquement les paramètres apprenables. En implémentant forward, la méthode backward est automatiquement calculée via autograd.
- Les classes
nn.Xxxs'intègrent avecnn.Sequential, contrairement aux fonctionsnn.functional.xxx. - Les couches comme
nn.Conv2dgèrent les poids et biais de manière interne, alors que les versions fonctionnelles nécessitent une gestion manuelle. - Pour les couches comme Dropout, l'utilisation de
nn.Dropoutpermet une transition automatique entre modes entraînement et évaluation viamodel.eval(), ce quenn.functional.dropoutne fait pas.
En général, on utilise nn.Module pour les couches avec paramètres apprenables (convolution, linéaire), et nn.functional pour les activations et le pooling sans paramètres.
La classe nn.Sequential permet d'empiler des couches. Pour les nommer, on peut utiliser add_module.
import torch
from collections import OrderedDict
class ReseauPersonnalise(torch.nn.Module):
def __init__(self):
super(ReseauPersonnalise, self).__init__()
self.bloc_conv = torch.nn.Sequential(OrderedDict([
("premiere_conv", torch.nn.Conv2d(3, 48, 5, padding=2)),
("activation_relu", torch.nn.ReLU()),
("sous_echantillonnage", torch.nn.MaxPool2d(3))
]))
self.bloc_dense = torch.nn.Sequential(OrderedDict([
("couche_lineaire1", torch.nn.Linear(48*10*10, 256)),
("activation_tanh", torch.nn.Tanh()),
("couche_sortie", torch.nn.Linear(256, 8))
]))
Propagation avant et arrière
La méthode forward() connecte les couches d'entrée, cachées et sortie pour propager les données. La rétropropagation est déclenchée par perte.backward(), utilisant la règle de la chaîne des dérivées partieelles.
Processus d'entraînement
Pendant l'entraînement, le modèle doit être en mode train(), tandis que eval() désactive certains comportements comme le Dropout. Les gradients s'accumulent par défaut, il faut donc appeler optimiseur.zero_grad() avant chaque itération. Le cycle typique est :
- Calculer la perte à partir des prédictions.
- Appeler
perte.backward()pour générer les gradients. - Exécuter
optimiseur.step()pour mettre à jour les paramètres.
Pour utiliser le GPU, on transfère le modèle et les données avec to(device). Le parallélisme multi-GPU est possible avec nn.DataParallel. Les paramètres apprenables sont accessibles via modele.parameters() ou modele.named_parameters().
Les entrées/sorties de forward() sont des Variable, car seul ce type supporte le calcul automatique de gradient. Pour des exemples individuels, on peut ajouter une dimension batch avec unsqueeze(0). Les fonctions de perte courantes incluent nn.MSELoss et nn.CrossEntropyLoss.
Exemple complet de réseau neuronal
import torch
import torch.nn as nn
import torch.nn.functional as F
class MonReseau(nn.Module):
def __init__(self):
super(MonReseau, self).__init__()
self.couche_conv1 = nn.Conv2d(1, 12, 6)
self.couche_conv2 = nn.Conv2d(12, 32, 6)
self.couche_fc1 = nn.Linear(32 * 4 * 4, 256)
self.couche_fc2 = nn.Linear(256, 128)
self.couche_sortie = nn.Linear(128, 10)
def forward(self, entree):
sortie = F.max_pool2d(F.relu(self.couche_conv1(entree)), 2)
sortie = F.max_pool2d(F.relu(self.couche_conv2(sortie)), 2)
sortie = sortie.view(sortie.size(0), -1)
sortie = F.relu(self.couche_fc1(sortie))
sortie = F.relu(self.couche_fc2(sortie))
sortie = self.couche_sortie(sortie)
return sortie
# Initialisation du modèle
modele = MonReseau()
print(modele)
# Affichage des paramètres
for nom, param in modele.named_parameters():
print(nom, ":", param.shape)
# Test avec une entrée aléatoire
entree_test = torch.randn(1, 1, 32, 32)
resultat = modele(entree_test)
print("Taille de sortie:", resultat.shape)
# Réinitialisation des gradients et passage avant
modele.zero_grad()
resultat.backward(torch.ones(1, 10))
# Calcul de la perte avec une cible simulée
cible = torch.arange(0, 10, dtype=torch.float)
criterion = nn.MSELoss()
perte = criterion(resultat, cible)
print("Gradient du biais de la première couche conv:", modele.couche_conv1.bias.grad)
# Mise à jour manuelle des paramètres (à titre d'exemple)
taux_apprentissage = 0.01
for p in modele.parameters():
p.data -= p.grad.data * taux_apprentissage