🔥 PyTorch – Le Framework Deep Learning "Pythonic"
Guide complet IDEO-Lab : Tensors, Autograd, nn.Module, Boucle d'entraînement & CUDA.
Vue d'ensemble
Bibliothèque ML/DL (Meta). Pythonic, Tensors & Autograd.
Deep Learning Meta (FAIR)PyTorch vs TensorFlow
R&D, flexibilité (Eager) vs Production, écosystème (Graph).
Pythonic Recherche (R&D)Installation (Python)
pip install torch. Version CUDA spécifique (via site PyTorch).
Concept N°1 : Tensors
Tableaux N-D (comme NumPy). torch.Tensor.
Opérations (Ops)
torch.add, torch.matmul, torch.randn. API NumPy-like.
Concept N°2 : Autograd
requires_grad=True. Enregistre les opérations.
Calcul des Gradients
loss.backward() (Backpropagation). tensor.grad.
CPU vs GPU (.to)
device = 'cuda', tensor.to(device), model.to(device).
Concept N°3 : nn.Module
La classe de base (class Net(nn.Module)). __init__, forward.
nn.Sequential
Modèle simple : une pile linéaire de couches (comme Keras).
nn.Sequential ModèleCouches (torch.nn)
nn.Linear (Dense), nn.Conv2d, nn.ReLU, nn.Embedding.
Fonctions de Perte (Loss)
nn.CrossEntropyLoss, nn.MSELoss, nn.L1Loss.
Optimiseurs (torch.optim)
optim.Adam, optim.SGD. Prend model.parameters().
🔁 Boucle d'entraînement
La boucle manuelle (pas de .fit()) : zero_grad, loss.backward, step.
Data (Dataset & DataLoader)
API pour charger, batcher et mélanger les données.
Dataset DataLoaderSauvegarde & Chargement
torch.save(model.state_dict()). load_state_dict().
Écosystème (Déploiement)
TorchScript (JIT), TorchServe (Serveur), PyTorch Mobile.
TorchScript TorchServeVitrine (Qui l'utilise ?)
Meta, Tesla (Autopilot), OpenAI (GPT), Hugging Face 🤗.
OpenAI (GPT) TeslaLiens Utiles & Formation
pytorch.org (Wizard install), Tutoriels officiels, Docs API.
Documentation TutorielsCheat-sheet
Syntaxe Tensors, nn.Module, Boucle d'entraînement.
Qu'est-ce que PyTorch ?
PyTorch est une bibliothèque (library) open-source de Machine Learning (ML) et Deep Learning (DL), développée principalement par le **Facebook AI Research (FAIR)** lab de Meta.
Elle est connue pour sa **simplicité** et son intégration profonde avec Python (elle est "Pythonic").
PyTorch est construit sur deux piliers :
- Tensors : Des
ndarray(comme NumPy) qui peuvent tourner sur **GPU**. - Autograd : Un moteur de différenciation automatique pour calculer les gradients (la backpropagation) de manière dynamique.
Flux de travail (Conceptuel)
PyTorch utilise l'exécution "Eager" (avide) par défaut. Le code est exécuté ligne par ligne, comme du Python normal. Cela rend le débogage (ex: print()) trivial.
+-------------------------+
| main.py (Votre code) |
| (torch.Tensor, nn.Module) |
+-------------------------+
|
| (1. Eager Execution)
| (Exécuté ligne par ligne)
▼
+-------------------------+
| Python Interpreter |
| (Avec Autograd) |
+-------------------------+
|
| (2. Ops exécutées par)
▼
[Runtime (CPU / GPU CUDA)]
PyTorch (PT) et TensorFlow (TF) sont les deux leaders. Le choix dépend souvent de la préférence et de l'objectif.
Forces de PyTorch (R&D, Flexibilité)
- Approche "Pythonic" : Se sent natif en Python (
print(), bouclesfor, débogagepdb). - Eager-First (Graphes Dynamiques) : Le graphe de calcul est construit à la volée. Idéal pour les modèles dont l'architecture change (ex: NLP, RNN).
- API Simple : L'API (
nn.Module) est souvent considérée comme plus simple et plus flexible que Keras. - Communauté R&D : Dominant dans le monde de la recherche (ex: OpenAI, Hugging Face). Les nouveaux papiers sont souvent implémentés en PyTorch.
Forces de TensorFlow (Production, Écosystème)
- Écosystème "End-to-End" : TFX (TensorFlow Extended) offre un écosystème MLOps (production) plus mature (validation, service, monitoring).
- Déploiement "Serveur" : TensorFlow Serving est une solution C++ haute performance pour servir les modèles.
- Déploiement "Edge" : TensorFlow Lite (TFLite) est très mature pour le déploiement sur mobile (Android/iOS) et microcontrôleurs.
- Scalabilité (TPU) : Intégration historique supérieure avec les TPU (Tensor Processing Units) de Google.
L'installation de PyTorch (surtout avec GPU) nécessite une commande pip spécifique, liée à votre version de CUDA.
Règle N°1 : Toujours utiliser le "Wizard" (assistant) sur le site pytorch.org.
Commande (Générée par le site)
(Exemple pour pip, Linux, CUDA 12.1)
# 1. (Recommandé) Créer un environnement virtuel python -m venv torch_env source torch_env/bin/activate # 2. Installer PyTorch (avec support CUDA 12.1) # (Cette commande inclut PyTorch, TorchVision (images) # et TorchAudio (audio) pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # (Exemple pour CPU uniquement) # pip3 install torch torchvision torchaudio
Vérification (CPU & GPU)
Nécessite les drivers NVIDIA et le CUDA Toolkit (version correspondante).
# Lancer 'python'
import torch
# 1. Vérifier la version
print(torch.__version__)
# 2. VÉRIFICATION GPU (LA PLUS IMPORTANTE)
# (Doit retourner True si CUDA est OK)
is_cuda = torch.cuda.is_available()
print(f"CUDA disponible: {is_cuda}")
if is_cuda:
# (Nom de votre carte GPU)
print(f"Nom GPU: {torch.cuda.get_device_name(0)}")
Le **Tensor** (torch.Tensor) est l'unité de donnée centrale de PyTorch. C'est un tableau multi-dimensionnel, très similaire à un ndarray de NumPy.
import torch import numpy as np # 1. Création # (Directe) t1 = torch.tensor([[1, 2], [3, 4]]) # (Type: int64) t2 = torch.tensor([[1., 2.], [3., 4.]]) # (Type: float32) # (Aléatoire) t_rand = torch.rand(3, 3) # (Shape 3x3, 0 à 1) t_zeros = torch.zeros(2, 2) # 2. NumPy <-> PyTorch (Interopérabilité) # (Partagent la même mémoire, très rapide) np_arr = np.array([1, 2]) t_from_np = torch.from_numpy(np_arr) t_tensor = torch.tensor([3, 4]) np_from_t = t_tensor.numpy() # 3. Attributs print(t1.shape) # torch.Size([2, 2]) print(t1.dtype) # torch.int64 print(t1.device) # 'cpu' (par défaut)
L'API des opérations est conçue pour ressembler à NumPy.
a = torch.tensor([[1, 2], [3, 4]]) b = torch.tensor([[5, 6], [7, 8]]) # 1. Addition (Element-wise) c1 = a + b c2 = torch.add(a, b) # 2. Multiplication (Element-wise) d = a * b # 3. Multiplication de Matrices (Dot product) e1 = a @ b e2 = torch.matmul(a, b) # 4. Broadcasting # (Le scalaire 2 est "étiré") f = a * 2 # [[2, 4], [6, 8]]
requires_grad)torch.autograd est le moteur de PyTorch pour la différenciation automatique (le calcul des gradients).
Si un Tensor (ex: les poids d'un modèle) a requires_grad=True, PyTorch va **enregistrer** (tracker) toutes les opérations effectuées dessus pour construire un graphe de calcul dynamique.
# x = Poids (Paramètre) # On veut calculer le gradient de 'y' par rapport à 'x' x = torch.tensor(3.0, requires_grad=True) # y = Donnée (Input) # (Pas besoin de gradient pour les inputs) y = torch.tensor(2.0, requires_grad=False) # z = x^2 * y z = (x * x) * y # (PyTorch a construit un graphe: z -> * -> y # -> * -> x # -> x) print(z.requires_grad) # True (car 'x' l'a demandé)
.backward())Une fois la sortie (loss) calculée, on appelle .backward() sur cette sortie. Autograd remonte alors le graphe (backpropagation) et accumule les gradients dans l'attribut .grad de chaque tensor qui avait requires_grad=True.
Exemple (suite de 2.2)
x = torch.tensor(3.0, requires_grad=True) y = torch.tensor(2.0, requires_grad=False) z = (x * x) * y # z = 18 # 1. Lancer la backpropagation (calculer les gradients) z.backward() # 2. Inspecter le gradient # (dz/dx = 2*x*y = 2*3*2 = 12) print(x.grad) # tensor(12.) # (y n'avait pas requires_grad) print(y.grad) # None
Important : Les gradients s'accumulent. C'est pourquoi on doit appeler optimizer.zero_grad() avant chaque .backward() dans la boucle d'entraînement.
.to(device))Pour utiliser le GPU (CUDA), il faut explicitement déplacer les **tensors** ET le **modèle** sur le "device" (périphérique) GPU.
# 1. Définir le device (Bonne pratique)
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Utilisation de: {device}")
# 2. Déplacer un Tensor
x_cpu = torch.tensor([1, 2])
x_gpu = x_cpu.to(device)
print(x_cpu.device) # cpu
print(x_gpu.device) # cuda:0 (si GPU disponible)
# 3. Déplacer un Modèle
# (Ceci déplace tous les poids/paramètres du modèle)
model = MyModel()
model.to(device)
# 4. (Dans la boucle d'entraînement)
# (Les données DOIVENT être sur le même device que le modèle)
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
nn.Module (Modèles)torch.nn.Module est la classe de base pour **tous** les modèles de réseaux de neurones. On crée une classe Python qui hérite de nn.Module.
Deux méthodes sont cruciales :
__init__(self): On définit les couches (nn.Linear,nn.Conv2d...). Ce sont les couches qui ont des poids (requires_grad=True).forward(self, x): On définit la logique du "forward pass" (comment les donnéesxtraversent les couches). (PyTorch gère lebackwardautomatiquement).
Exemple (MLP simple)
import torch.nn as nn
import torch.nn.functional as F
class SimpleNet(nn.Module):
# 1. Définir les couches (avec poids)
def __init__(self):
super(SimpleNet, self).__init__()
# (Couche 1: 784 (input) -> 128 (hidden))
self.fc1 = nn.Linear(784, 128)
# (Couche 2: 128 (hidden) -> 10 (output))
self.fc2 = nn.Linear(128, 10)
# 2. Définir le "forward pass"
def forward(self, x):
# x shape: [batch_size, 784]
# (Couche 1 + Activation ReLU)
x = self.fc1(x)
x = F.relu(x) # (ReLU n'a pas de poids, F.* est OK)
# (Couche 2)
x = self.fc2(x)
# (Pas de Softmax ici, car CrossEntropyLoss
# l'inclut pour la stabilité numérique)
return x
# Instancier le modèle
model = SimpleNet()
print(model)
nn.Sequential (Modèles simples)Pour les modèles qui sont une simple pile linéaire (comme dans l'exemple 3.1), PyTorch fournit nn.Sequential (similaire à Keras Sequential) pour éviter d'écrire une classe.
Exemple (MLP simple, équivalent 3.1)
import torch.nn as nn
# (Input: 784)
model = nn.Sequential(
# (Couche 1: 784 -> 128)
nn.Linear(784, 128),
# (Activation)
nn.ReLU(),
# (Couche 2: 128 -> 10)
nn.Linear(128, 10)
# (Pas de Softmax, géré par CrossEntropyLoss)
)
print(model)
torch.nn) & Activations (F.*)torch.nn (Modules) : Couches qui ont des poids (nn.Linear). Doivent être définies dans __init__. torch.nn.functional (Fonctions) : Opérations qui n'ont pas de poids (F.relu). Peuvent être appelées dans forward.
Couche (nn.*) | Usage |
|---|---|
nn.Linear(in, out) | Couche "Dense" (Fully-Connected). |
nn.Conv2d(...) | Convolution 2D (Images). |
nn.MaxPool2d(...) | Pooling (Images). |
nn.Embedding(num, dim) | Lookup table (NLP). |
nn.LSTM(...) / nn.GRU(...) | Couches récurrentes (Séquences). |
nn.Dropout(p) | Régularisation. |
nn.ReLU() / nn.Sigmoid() | (Version "Module", si utilisée dans nn.Sequential). |
La fonction de perte (ou "critère") mesure l'erreur entre la prédiction (outputs) et la vérité terrain (labels). C'est ce scalaire (loss) que l'on minimise.
| Perte | Usage |
|---|---|
nn.CrossEntropyLoss() | Classification (Multi-classes). (Combine LogSoftmax et NLLLoss). |
nn.BCEWithLogitsLoss() | Classification (Binaire ou Multi-label). (Combine Sigmoid et BCELoss). |
nn.MSELoss() (L2) | Régression (Mean Squared Error). |
nn.L1Loss() (L1) | Régression (Mean Absolute Error). |
# (Pour une classification à 10 classes) criterion = nn.CrossEntropyLoss() # (Dans la boucle d'entraînement) outputs = model(inputs) # (Sorties 'logits', SANS softmax) loss = criterion(outputs, labels) # (Calcule l'erreur) # (Puis on appelle la backpropagation) loss.backward()
torch.optim)L'optimiseur implémente l'algorithme de descente de gradient (ex: Adam, SGD). Son rôle est de mettre à jour les poids (model.parameters()) en utilisant les gradients (calculés par .backward()).
Exemple (Adam)
import torch.optim as optim
model = SimpleNet()
learning_rate = 0.001
# 1. Définir l'optimiseur
# (On lui passe les poids que l'on veut entraîner,
# et le 'learning rate')
optimizer = optim.Adam(
model.parameters(),
lr=learning_rate
)
# 2. Utilisation (dans la boucle d'entraînement)
# (Après loss.backward() ...)
optimizer.step()
# (Avant le prochain .backward() ...)
optimizer.zero_grad()
C'est la différence majeure avec Keras (.fit()). PyTorch demande d'écrire la boucle d'entraînement manuellement. C'est plus verbeux, mais 100% flexible.
Les 5 Étapes (par batch)
# (Objets déjà définis : model, dataloader, criterion, optimizer, device)
# Mettre le modèle en mode "entraînement" (active Dropout, etc.)
model.train()
for epoch in range(NUM_EPOCHS):
running_loss = 0.0
# (Boucle sur les 'batch' de données)
for inputs, labels in dataloader:
# (Déplacer les données sur GPU/CPU)
inputs = inputs.to(device)
labels = labels.to(device)
# --- 1. Zéro: RAZ des gradients ---
optimizer.zero_grad()
# --- 2. Forward Pass: Prédire ---
outputs = model(inputs)
# --- 3. Loss: Calculer l'erreur ---
loss = criterion(outputs, labels)
# --- 4. Backward Pass: Calculer les gradients ---
loss.backward()
# --- 5. Step: Mettre à jour les poids ---
optimizer.step()
running_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {running_loss / len(dataloader)}")
Dataset & DataLoader)L'API torch.utils.data gère le chargement des données.
1. Dataset
Classe abstraite qui stocke les données (images, texte...). Doit implémenter __len__ (taille totale) et __getitem__ (récupérer 1 item).
from torch.utils.data import Dataset
class MonDataset(Dataset):
def __init__(self, data, targets):
self.data = data
self.targets = targets
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
# (Retourne 1 échantillon)
x = self.data[idx]
y = self.targets[idx]
return x, y
2. DataLoader
"Enveloppe" le Dataset. Gère le batching (lots), le shuffling (mélange), et le multi-processing (chargement parallèle).
from torch.utils.data import DataLoader
# (Instancier le Dataset)
train_dataset = MonDataset(x_train, y_train)
# (Instancier le DataLoader)
train_loader = DataLoader(
dataset=train_dataset,
batch_size=32,
shuffle=True, # (Mélanger à chaque epoch)
num_workers=4 # (4 process pour charger)
)
# (Utilisation dans la boucle)
# for inputs, labels in train_loader:
# ...
state_dict)La bonne pratique PyTorch est de ne sauvegarder que le state_dict (un dictionnaire Python des poids/paramètres), et non le modèle entier. (Cela rend le code de chargement indépendant des fichiers d'origine).
Sauvegarde (state_dict)
# (Après l'entraînement...) PATH = "mon_modele.pth" # (Sauvegarde le dictionnaire de Poids) torch.save(model.state_dict(), PATH)
Chargement & Inférence (.eval())
PATH = "mon_modele.pth"
# 1. Ré-instancier l'architecture
# (Doit être la *même* classe qu'à l'entraînement)
model = SimpleNet()
# 2. Charger les poids
model.load_state_dict(torch.load(PATH))
# 3. Mode ÉVALUATION (TRÈS IMPORTANT)
# (Désactive Dropout, BatchNorm, etc.)
model.eval()
# 4. Inférence (sans gradients)
with torch.no_grad():
input_test = torch.rand(1, 784)
prediction = model(input_test)
PyTorch comble son retard sur TensorFlow en matière de déploiement "end-to-end".
| Outil | Cible | Description |
|---|---|---|
| TorchScript (JIT) | Production (C++) | (Équivalent @tf.function). Compile le modèle Python en un graphe (.ptl) exécutable en C++ (haute performance), sans dépendance Python. |
| TorchServe | Production (Serveur) | (Équivalent TF Serving). Outil de Meta & AWS pour "servir" des modèles (API REST/gRPC). |
| PyTorch Mobile | Mobile & IoT | (Équivalent TF Lite). Exécute des modèles TorchScript optimisés sur Android et iOS. |
| PyTorch Lightning | R&D (Haut Niveau) | (Équivalent Keras .fit()). Une surcouche qui abstrait la boucle d'entraînement (4.2) et gère le multi-GPU. |
PyTorch domine le monde de la **Recherche & Développement** et est la base des modèles "state-of-the-art" (SOTA), notamment en NLP.
| Entreprise / Projet | Cas d'usage |
|---|---|
| Meta (Facebook) | Créateur. Utilisé pour la modération, la traduction, la recommandation (feed), et la R&D (ex: Llama). |
| OpenAI | Base de tous leurs modèles majeurs : GPT-3/4, DALL-E, CLIP. |
| Hugging Face 🤗 | La bibliothèque transformers (le standard du NLP) est construite "PyTorch-first". |
| Tesla | Utilisé pour l'entraînement (R&D) des modèles de Tesla Autopilot (vision par ordinateur). |
| Monde Académique | Le framework de choix pour la plupart des universités (Stanford, MIT, ...) et des papiers de recherche (CVPR, NeurIPS...). |
Ressources pour apprendre et travailler avec PyTorch.
| Site | Description |
|---|---|
| pytorch.org | Le site officiel. Utilisez l'assistant d'installation (Wizard) sur la page d'accueil. |
| PyTorch Tutorials | (pytorch.org/tutorials) Les tutoriels officiels. Excellents, du 60-min blitz à des exemples (GANs, NLP...). |
| PyTorch Docs (API) | (pytorch.org/docs/stable) La documentation de l'API (ex: torch.nn, torch.optim). |
| PyTorch (GitHub) | Le dépôt officiel. |
Tensors & Autograd
import torch
import torch.nn as nn
import torch.optim as optim
# Tensors
x = torch.rand(4, 4)
x = x.to("cuda") # (Si GPU dispo)
# Autograd
w = torch.tensor(2.0, requires_grad=True)
y = w * 3
y.backward()
# w.grad == 3.0
Modèle (nn.Module)
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(10, 5)
def forward(self, x):
return self.fc1(x)
model = Net().to(device)
Boucle d'Entraînement
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
# (Boucle sur les batchs)
for inputs, labels in dataloader:
inputs, labels = inputs.to(device), labels.to(device)
# 1. RAZ Gradients
optimizer.zero_grad()
# 2. Forward
outputs = model(inputs)
# 3. Loss
loss = criterion(outputs, labels)
# 4. Backward (Calcul Gradients)
loss.backward()
# 5. Step (MAJ Poids)
optimizer.step()
