Tu veux faire du deep learning mais tu ne sais pas par où commencer ? Tu as entendu parler de TensorFlow, Keras, PyTorch et tu es perdu dans la jungle des frameworks ? Ce tutoriel est fait pour toi. On va prendre PyTorch — le framework le plus utilisé en recherche et dans l'industrie — et construire ensemble ton premier réseau de neurones, de A à Z, avec du code que tu peux copier-coller. Pas de jargon inutile, pas de maths intimidantes. Juste du concret.
Pourquoi PyTorch ?
PyTorch est aujourd'hui le standard de facto du deep learning. Développé par Meta (Facebook), il est utilisé par OpenAI, Tesla, DeepMind et des milliers de chercheurs. Ses trois forces :
- La syntaxe Python native : tu écris du Python, pas un DSL exotique
- Le calcul dynamique : tu peux débugger avec
print()comme dans n'importe quel script - L'écosystème : torchvision pour la vision, torchaudio pour l'audio, HuggingFace qui s'appuie dessus
Bref, si tu veux faire de l'IA sérieuse, PyTorch est ton allié.
Ce qu'on va construire
Aujourd'hui, on va créer un réseau de neurones capable de reconnaître des chiffres manuscrits (le célèbre dataset MNIST). À la fin de ce tutoriel, tu auras :
- Compris ce qu'est un tensor (le bloc de base)
- Utilisé autograd pour le calcul automatique des gradients
- Construit un réseau de neurones à deux couches
- Entraîné ton modèle sur des vraies données
- Obtenu plus de 95 % de précision
Étape 1 : Installation
D'abord, on crée un environnement propre et on installe PyTorch.
# Créer un dossier de projet
mkdir mon-premier-pytorch && cd mon-premier-pytorch
# Créer un environnement virtuel
python3 -m venv venv
source venv/bin/activate
# Installer PyTorch (CPU = pas besoin de carte graphique)
pip install torch torchvision💡 Ce que tu dois voir : pip télécharge PyTorch et ses dépendances (~150 Mo). L'installation prend 1 à 2 minutes. Aucune erreur ne doit apparaître en rouge.
Vérifie que tout fonctionne :
import torch
print(f"PyTorch version : {torch.__version__}")
print(f"GPU dispo : {torch.cuda.is_available()}")Si la première ligne affiche une version (ex: 2.x.x), tout est bon. Le GPU n'est pas nécessaire pour ce tutoriel — le CPU suffit.
Étape 2 : Les tensors, le bloc de base
Un tensor, c'est l'équivalent d'un tableau NumPy... mais avec un super-pouvoir : il peut être envoyé sur une carte graphique (GPU) et il garde une trace de toutes les opérations pour le calcul des gradients.
import torch
# Créer un tensor à partir d'une liste Python
x = torch.tensor([1.0, 2.0, 3.0, 4.0])
print(x) # tensor([1., 2., 3., 4.])
print(x.shape) # torch.Size([4])
print(x.dtype) # torch.float32
# Créer des tensors de zéros, de uns, ou aléatoires
zeros = torch.zeros(3, 4) # 3 lignes, 4 colonnes
ones = torch.ones(2, 5) # 2 lignes, 5 colonnes
random = torch.randn(3, 3) # distribution normale
# Opérations mathématiques
a = torch.tensor([1.0, 2.0])
b = torch.tensor([3.0, 4.0])
print(a + b) # tensor([4., 6.])
print(a * b) # tensor([3., 8.]) — multiplication élément par élément
print(a @ b) # tensor(11.) — produit scalaire💡 Ce que tu dois voir : Chaque print() affiche le résultat attendu. Pas d'erreur.
Le concept clé : tout dans PyTorch passe par les tensors. Une image = un tensor de forme (hauteur, largeur, canaux). Un batch de données = un tensor de forme (taille_batch, caractéristiques).
Étape 3 : Autograd — la magie de la dérivation automatique
Le deep learning repose sur la descente de gradient : on calcule la dérivée (le gradient) de l'erreur par rapport à chaque paramètre, puis on ajuste les paramètres dans la direction qui réduit l'erreur.
PyTorch fait ça tout seul avec autograd. Regarde :
# Un tensor qui demande le suivi des gradients
w = torch.tensor([2.0], requires_grad=True)
# On définit une fonction : f(w) = w² + 3w + 1
f = w**2 + 3*w + 1
# On calcule le gradient df/dw automatiquement
f.backward()
print(f"f(w) = {f.item()}") # f(w) = 11.0
print(f"df/dw = {w.grad.item()}") # df/dw = 7.0 (car 2w + 3 = 2*2 + 3 = 7)💡 Ce que tu dois voir : La valeur du gradient calculé automatiquement correspond au calcul manuel. C'est cette magie qui permet d'entraîner des réseaux de neurones avec des millions de paramètres.
Étape 4 : Charger les données MNIST
MNIST, c'est 70 000 images de chiffres manuscrits (28×28 pixels en noir et blanc). PyTorch les télécharge pour nous :
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# Pipeline de transformation : image → tensor → normalisation
transform = transforms.Compose([
transforms.ToTensor(), # Convertit l'image en tensor (0 à 1)
transforms.Normalize((0.1307,), (0.3081,)) # Normalise avec moyenne et écart-type MNIST
])
# Télécharger et charger les données
train_data = datasets.MNIST(
root="./data", # Stockage local
train=True, # Le dataset d'entraînement
download=True, # Télécharge si pas déjà fait
transform=transform # Applique la transformation
)
test_data = datasets.MNIST(
root="./data",
train=False, # Le dataset de test
transform=transform
)
# DataLoader : distribue les données par batch et les mélange
train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
test_loader = DataLoader(test_data, batch_size=1000, shuffle=False)
print(f"Images d'entraînement : {len(train_data)}")
print(f"Images de test : {len(test_data)}")💡 Ce que tu dois voir : Le téléchargement prend ~10 secondes. Tu vois 60000 images d'entraînement et 10000 de test.
Étape 5 : Construire le réseau de neurones
On va créer un réseau super simple, mais efficace : une couche d'entrée de 784 neurones (28×28 pixels), une couche cachée de 128 neurones, et une couche de sortie de 10 neurones (1 par chiffre).
import torch.nn as nn
class MonPremierReseau(nn.Module):
def __init__(self):
super().__init__()
self.couche_cachee = nn.Linear(28*28, 128) # Entrée (784 pixels) → 128 neurones cachés
self.couche_sortie = nn.Linear(128, 10) # 128 neurones cachés → 10 classes (0 à 9)
self.relu = nn.ReLU() # Fonction d'activation (introduit la non-linéarité)
def forward(self, x):
# x a la forme (batch_size, 1, 28, 28) — on l'aplatit
x = x.view(-1, 28*28) # (batch_size, 784)
x = self.relu(self.couche_cachee(x)) # (batch_size, 128) avec ReLU
x = self.couche_sortie(x) # (batch_size, 10)
return x
# Instancier le modèle
modele = MonPremierReseau()
print(modele)💡 Ce que tu dois voir : Le print(modele) affiche l'architecture complète du réseau. Chaque couche Linear montre le nombre de paramètres.
Pourquoi cette architecture ? 784 pixels → on les compresse en 128 caractéristiques intermédiaires → on les projette en 10 scores (un par chiffre). La fonction ReLU remplace les valeurs négatives par zéro — c'est ce qui permet au réseau d'apprendre des relations non-linéaires.
Étape 6 : La boucle d'entraînement
C'est le cœur du deep learning. On répète 5 fois (5 époques) le cycle : prédire → mesurer l'erreur → corriger les paramètres.
import torch.optim as optim
# Fonction de perte : mesure l'écart entre prédictions et réalité
loss_fn = nn.CrossEntropyLoss()
# Optimiseur : ajuste les paramètres selon le gradient
optimizer = optim.Adam(modele.parameters(), lr=0.001)
EPOQUES = 5
modele.train() # Mode entraînement
for epoque in range(EPOQUES):
perte_totale = 0
corrects = 0
for images, labels in train_loader:
# 1. Remettre les gradients à zéro
optimizer.zero_grad()
# 2. Forward pass : prédire
predictions = modele(images)
# 3. Calculer l'erreur
perte = loss_fn(predictions, labels)
# 4. Backward pass : calculer les gradients
perte.backward()
# 5. Mettre à jour les paramètres
optimizer.step()
# Stats pour l'affichage
perte_totale += perte.item()
corrects += (predictions.argmax(dim=1) == labels).sum().item()
precision = 100 * corrects / len(train_data)
print(f"Époque {epoque+1}/{EPOQUES} | Perte: {perte_totale/len(train_loader):.4f} | Précision: {precision:.2f}%")💡 Ce que tu dois voir : La perte diminue et la précision augmente à chaque époque. À la fin de la 5ᵉ époque, tu devrais dépasser 95 % de précision sur les données d'entraînement.
Comprendre la boucle
Chaque itération de la boucle fait 5 choses : 1. zero_grad() — On efface les gradients de l'étape précédente 2. Forward — Le modèle prédit les sorties pour le batch d'images 3. Loss — On mesure l'écart entre ce qui est prédit et la réalité 4. Backward — Autograd calcule les gradients (les dérivées de l'erreur) 5. Step — L'optimiseur ajuste les paramètres dans la bonne direction
Étape 7 : Évaluer le modèle
Maintenant qu'on a entraîné, vérifions si le modèle généralise bien (données qu'il n'a jamais vues).
modele.eval() # Mode évaluation (désactive dropout, etc.)
corrects = 0
total = 0
with torch.no_grad(): # Pas besoin de gradients pour l'évaluation
for images, labels in test_loader:
predictions = modele(images)
corrects += (predictions.argmax(dim=1) == labels).sum().item()
total += labels.size(0)
precision_test = 100 * corrects / total
print(f"\n🎉 Précision sur les données de test : {precision_test:.2f}%")💡 Ce que tu dois voir : Une précision autour de 96-97 % sur le jeu de test. Pour un réseau aussi simple (deux couches), c'est excellent !
Étape 8 : Utiliser ton modèle sur tes propres images
Tu veux tester ton modèle sur un chiffre que tu as dessiné ? Voici comment faire une prédiction :
import torch.nn.functional as F
def predire_chiffre(modele, image_tensor):
"""
image_tensor : tensor de forme (1, 28, 28) avec valeurs entre 0 et 1
Retourne le chiffre prédit et les probabilités de chaque classe.
"""
modele.eval()
with torch.no_grad():
# Ajouter la dimension batch : (1, 1, 28, 28)
x = image_tensor.unsqueeze(0)
logits = modele(x)
probabilites = F.softmax(logits, dim=1)
chiffre_pred = logits.argmax(dim=1).item()
print(f"Chiffre prédit : {chiffre_pred}")
for i, prob in enumerate(probabilites.squeeze()):
barre = "█" * int(prob.item() * 30)
print(f" {i}: {barre} {prob.item():.3f}")
return chiffre_pred
# Exemple avec une image du jeu de test
image_test, label_vrai = test_data[0]
print(f"Vrai chiffre : {label_vrai}")
predire_chiffre(modele, image_test)💡 Ce que tu dois voir : Le modèle affiche la prédiction correcte avec une barre de confiance quasi pleine.
Pièges classiques
1. Oublier model.train() et model.eval()
Certaines couches (comme Dropout et BatchNorm) se comportent différemment en entraînement et en évaluation. Si ta précision de test est bizarrement basse, vérifie que tu as bien appelé .eval() avant d'évaluer.
2. Confondre view() et reshape()
view() exige que le tensor soit contigu en mémoire. Si tu as un bug RuntimeError: view size is not compatible, utilise reshape() ou appelle .contiguous() avant.
3. Laisser requires_grad=True pendant l'évaluation
Ça consomme de la mémoire pour rien. Utilise torch.no_grad() pendant l'inférence — ça accélère le code et réduit l'empreinte mémoire.
4. Batch size trop grand
Si tu dépasses la mémoire de ta carte graphique, réduis la taille du batch (batch_size=32 au lieu de 64 par exemple).
5. Ne pas normaliser les données
Si tu oublies la normalisation, ton réseau va soit ne pas converger, soit converger très lentement. Les données doivent être centrées autour de 0.
Pour aller plus loin
1. Ajoute des convolutions (CNN)
Remplace les couches Linear par des couches nn.Conv2d. Les CNN sont la raison pour laquelle la vision par ordinateur a explosé. Avec un CNN simple (2 convolutions + 2 linéaires), tu passeras facilement à 99 % de précision.
2. Sauvegarde et recharge ton modèle
torch.save(modele.state_dict(), "mon_modele.pth") # Sauvegarder
modele.load_state_dict(torch.load("mon_modele.pth")) # Recharger3. Passe sur GPU
Si tu as une carte graphique NVIDIA, ajoute .to("cuda") partout :
modele = modele.to("cuda")
images, labels = images.to("cuda"), labels.to("cuda")L'entraînement sera 10 à 50 fois plus rapide.
4. Explore les modèles pré-entraînés
Avec torchvision.models, tu peux charger un ResNet, un EfficientNet ou un ViT déjà entraîné sur ImageNet et le fine-tuner sur tes propres données en quelques lignes.
Récapitulatif
Tu as maintenant un vrai réseau de neurones qui reconnaît des chiffres manuscrits avec plus de 95 % de précision. Tu as compris les trois piliers de PyTorch :
Concept | Rôle |
Tensor | Conteneur de données avec suivi des gradients |
Autograd | Calcul automatique des dérivées |
nn.Module | Brique de base pour construire des réseaux |
Tu peux maintenant explorer la classification d'images plus complexes, la génération de texte, ou même entraîner un modèle de diffusion. Tout se construit sur ces briques.
Bon code, et n'oublie pas : le deep learning, c'est 20 % de théorie et 80 % de pratique. Plus tu codes, plus tu comprends.
💡 Idée d'illustration : Une bannière montrant un réseau de neurones simplifié avec des flèches reliant 784 pixels d'entrée → 128 neurones cachés → 10 neurones de sortie (chiffres 0 à 9), sur fond de code Python avec des couleurs chaudes.







