À quoi ça sert

PyTorch sert à entraîner et utiliser des réseaux de neurones en Python. Là où scikit-learn s'arrête (modèles classiques sur données tabulaires), PyTorch prend le relais pour tout ce qui est deep learning : vision par ordinateur, NLP, audio, génération.

Trois briques fondamentales :

  • Tensor — un tableau multidimensionnel, équivalent du ndarray NumPy mais qui peut tourner sur GPU et garder en mémoire les opérations qu'on lui applique.
  • Autograd — calcul automatique des gradients. Tu écris ton calcul, PyTorch calcule les dérivées partielles pour toi.
  • nn.Module — la classe de base pour construire un modèle : on déclare des couches, on définit forward(), PyTorch s'occupe du reste.

À ces trois briques s'ajoutent des outils périphériques : DataLoader pour itérer sur des batchs, torch.optim pour les optimiseurs (SGD, Adam…), et tout l'écosystème torchvision / torchaudio / torchtext pour les modèles pré-entraînés.

PyTorch, TensorFlow, scikit-learn… c'est quoi la différence ?

scikit-learn = ML classique sur données tabulaires (random forest, régression…). PyTorch et TensorFlow font tous les deux du deep learning ; en 2026 PyTorch domine largement la recherche et de plus en plus l'industrie, grâce à une API plus pythonique. YOLO (versions modernes) et HuggingFace Transformers reposent sur PyTorch.

Un exemple d'usage

On entraîne une mini-régression linéaire « à la main » pour voir le pattern de base : tenseurs, modèle, loss, optimiseur, boucle d'entraînement.

python
import torch
from torch import nn

# Données : y = 2x + 1 avec un peu de bruit
X = torch.linspace(0, 10, 100).unsqueeze(1)
y = 2 * X + 1 + torch.randn_like(X) * 0.5

# Modèle : une seule couche linéaire (1 → 1)
model = nn.Linear(in_features=1, out_features=1)
loss_fn = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# Boucle d'entraînement
for epoch in range(200):
    pred = model(X)                  # forward
    loss = loss_fn(pred, y)
    optimizer.zero_grad()             # reset des gradients
    loss.backward()                   # backward (autograd)
    optimizer.step()                   # mise à jour des poids

print("poids appris :", model.weight.item())  # ≈ 2.0
print("biais appris :", model.bias.item())     # ≈ 1.0

Toutes les boucles d'entraînement PyTorch suivent ce squelette : forward → loss → zero_grad → backward → step. Que ce soit pour une régression à 1 paramètre ou un transformeur à 100 milliards de paramètres, c'est le même schéma.

How-to : installer et utiliser PyTorch

  1. Installer PyTorch

    Avec UV, pour la version CPU (suffisant pour apprendre et faire tourner de l'inférence légère) :

    bash
    uv add torch torchvision
    Pour le GPU : passer par l'index officiel

    Si tu as une carte NVIDIA et que tu veux CUDA, suis le configurateur officiel sur pytorch.org/get-started/locally. Il génère la commande exacte selon ton OS, ta version de CUDA et ton outil de package. Avec UV : on ajoute un --extra-index-url pointant sur l'index PyTorch spécifique.

  2. Manipuler des tenseurs

    L'API est volontairement proche de NumPy — si tu connais NumPy, tu connais déjà 80% de l'API tensor.

    python
    import torch
    
    # Créer un tenseur
    t = torch.tensor([[1., 2.], [3., 4.]])
    torch.zeros(3, 4); torch.ones(10)
    torch.randn(3, 4)                # aléatoire normal
    
    # Inspection
    t.shape, t.dtype, t.device
    
    # Opérations vectorisées (comme NumPy)
    t + 1; t * 2; t @ t.T          # produit matriciel
    
    # Conversion avec NumPy
    import numpy as np
    arr = t.numpy()                    # tensor → array
    t2 = torch.from_numpy(arr)         # array → tensor
    
    # Déplacer sur GPU si disponible
    device = "cuda" if torch.cuda.is_available() else "cpu"
    t = t.to(device)
  3. Comprendre autograd

    Si on met requires_grad=True sur un tenseur, PyTorch trace toutes les opérations et peut calculer la dérivée automatiquement :

    python
    x = torch.tensor(3.0, requires_grad=True)
    y = x ** 2 + 2 * x + 1     # y = x² + 2x + 1
    y.backward()                # calcule dy/dx
    print(x.grad)              # 8.0  (= 2x + 2 en x=3)

    C'est ce mécanisme qui rend possible la rétropropagation : on écrit le calcul du loss, PyTorch trouve seul comment ajuster chaque paramètre pour le réduire.

  4. Définir un modèle avec nn.Module

    Pour autre chose qu'une couche unique, on sous-classe nn.Module, on déclare les couches dans __init__, et on définit le passage avant dans forward :

    python
    from torch import nn
    
    class MLP(nn.Module):
        def __init__(self, n_in, n_hidden, n_out):
            super().__init__()
            self.net = nn.Sequential(
                nn.Linear(n_in, n_hidden),
                nn.ReLU(),
                nn.Linear(n_hidden, n_out),
            )
    
        def forward(self, x):
            return self.net(x)
    
    model = MLP(n_in=10, n_hidden=64, n_out=3)
    print(model)
  5. Charger des données avec Dataset et DataLoader

    Dataset = un objet qui répond à __len__ et __getitem__. DataLoader en fait des batchs avec mélange et parallélisme.

    python
    from torch.utils.data import Dataset, DataLoader
    
    class SimpleDataset(Dataset):
        def __init__(self, X, y):
            self.X, self.y = X, y
    
        def __len__(self):
            return len(self.X)
    
        def __getitem__(self, i):
            return self.X[i], self.y[i]
    
    train_ds = SimpleDataset(X_train, y_train)
    train_dl = DataLoader(train_ds, batch_size=32, shuffle=True)
  6. La boucle d'entraînement standard

    python
    loss_fn = nn.CrossEntropyLoss()        # classification
    optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
    
    for epoch in range(10):
        model.train()
        for xb, yb in train_dl:
            xb, yb = xb.to(device), yb.to(device)
            pred = model(xb)
            loss = loss_fn(pred, yb)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        print(f"epoch {epoch} - loss {loss.item():.4f}")
    model.train() vs model.eval()

    Certaines couches (Dropout, BatchNorm) se comportent différemment en entraînement et en évaluation. Toujours appeler model.train() avant la boucle d'entraînement et model.eval() + with torch.no_grad(): au moment de l'évaluation.

  7. Évaluer et prédire

    python
    model.eval()
    with torch.no_grad():            # pas de calcul de gradient
        correct = 0; total = 0
        for xb, yb in test_dl:
            pred = model(xb.to(device))
            correct += (pred.argmax(1).cpu() == yb).sum().item()
            total += len(yb)
    print(f"accuracy : {correct/total:.2%}")
  8. Sauvegarder et recharger un modèle

    python
    # Sauver SEULEMENT les poids (recommandé)
    torch.save(model.state_dict(), "model.pt")
    
    # Recharger : il faut d'abord reconstruire le même nn.Module
    model = MLP(n_in=10, n_hidden=64, n_out=3)
    model.load_state_dict(torch.load("model.pt"))
    model.eval()

Aide-mémoire

python (tenseurs)
torch.tensor([1, 2]); torch.zeros(3, 4); torch.randn(2, 3)
t.shape, t.dtype, t.device
t.to("cuda"); t.cpu(); t.numpy()
t.view(-1, 3), t.unsqueeze(0), t.squeeze()
python (modèle)
from torch import nn
model = nn.Sequential(
    nn.Linear(10, 64), nn.ReLU(),
    nn.Linear(64, 3),
)
loss_fn = nn.CrossEntropyLoss()
optim = torch.optim.Adam(model.parameters(), lr=1e-3)
python (boucle)
for xb, yb in dl:
    pred = model(xb)
    loss = loss_fn(pred, yb)
    optim.zero_grad(); loss.backward(); optim.step()
python (sauver / charger)
torch.save(model.state_dict(), "m.pt")
model.load_state_dict(torch.load("m.pt"))

PyTorch et le reste de l'écosystème

  • NumPy — les tenseurs PyTorch et les arrays NumPy partagent la même mémoire quand c'est possible. On passe de l'un à l'autre avec t.numpy() et torch.from_numpy(arr).
  • scikit-learn — pour un problème tabulaire de taille modeste, sklearn est plus simple et souvent suffisant. PyTorch devient utile dès qu'on a des données non tabulaires (images, texte, audio) ou un dataset trop gros pour sklearn.
  • YOLO — les versions modernes (Ultralytics) sont écrites en PyTorch. Quand tu fais model = YOLO("yolov8n.pt"), tu charges un nn.Module avec ses poids.
  • MLflowmlflow.pytorch.log_model(model, "model") sauvegarde un modèle PyTorch comme artifact de run, prêt à être servi.
  • FastAPI — pour exposer un modèle PyTorch comme service web : on charge le modèle au démarrage, chaque requête fait model(input_tensor) dans un torch.no_grad().
  • matplotlib — pour tracer la loss d'entraînement à chaque epoch et détecter sur-apprentissage / sous-apprentissage.
Aller plus loin : Lightning & HuggingFace

Une fois la boucle PyTorch maîtrisée, deux libs montent d'un cran : PyTorch Lightning structure le code d'entraînement (training step, validation step, checkpointing) pour éviter le boilerplate. HuggingFace Transformers donne accès à des milliers de modèles pré-entraînés (BERT, GPT-2, Whisper…) avec une API uniforme.

Pour aller plus loin