Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 82

Building a PyTorch Training Loop

Recommended resources

Bonjour ! Nous avons franchi une étape décisive dans la leçon précédente en définissant la fonction de perte et l'optimiseur. Nous possédons maintenant toutes les pièces du puzzle :

  1. Un modèle (nn.Module) qui peut faire des prédictions.
  2. Un chargeur de données (DataLoader) qui fournit les données en lots.
  3. Une fonction de perte (nn.CrossEntropyLoss) pour mesurer l'erreur du modèle.
  4. Un optimiseur (optim.Adam) pour ajuster le modèle afin de réduire cette erreur.

Dans cette leçon, nous allons assembler ces pièces pour construire le moteur de l'apprentissage : la boucle d'entraînement (training loop). C'est le cœur de tout processus de deep learning, un cycle qui se répète pour permettre au modèle d'apprendre progressivement à partir des données. À la fin de cette leçon, vous saurez implémenter une boucle d'entraînement complète en orchestrant la passe avant, le calcul de la perte, la rétropropagation et la mise à jour des poids par l'optimiseur.

1. L'Anatomie d'une Boucle d'Entraînement

L'entraînement d'un modèle de deep learning n'est pas un événement ponctuel, mais un processus itératif. Ce processus est structuré par deux boucles imbriquées :

  • La boucle externe (Epochs) : Une époque (epoch) correspond à un passage complet sur l'ensemble du jeu de données d'entraînement. Entraîner un modèle pour 10 époques signifie qu'il a "vu" chaque exemple d'entraînement 10 fois.
  • La boucle interne (Batches) : Comme nous l'avons vu, il est inefficace (et souvent impossible) de traiter tout le jeu de données en une seule fois. Le DataLoader nous sert les données en mini-lots (mini-batches). Cette boucle interne itère sur tous les lots fournis par le DataLoader pour une époque donnée.

C'est à l'intérieur de cette boucle de lots que la magie opère. Pour chaque lot, nous effectuons une série d'opérations qui constituent un pas d'apprentissage.

La documentation de PyTorch décrit bien cette structure.


2. Les Cinq Étapes Clés de l'Entraînement

Pour chaque lot de données, nous suivons un cycle précis en cinq étapes. Ces étapes sont le cœur de l'algorithme de descente de gradient et sont universelles en deep learning.

La vidéo suivante, "PyTorch in 1 Hour", explique de manière très intuitive ces cinq étapes, d'abord conceptuellement, puis avec du code.

Voici ces cinq étapes traduites en code PyTorch. L'image ci-dessous annote un script Python avec exactement ces étapes.

Voyons chaque ligne en détail.

Étape 1 : La Passe Avant (Forward Pass)
Le modèle reçoit les données d'entrée (X_train dans l'image) et effectue une prédiction. C'est un simple appel au modèle, qui exécute sa méthode forward.







# `model` est une instance de notre classe nn.Module
# `X_batch` est un lot de données provenant du DataLoader
y_logits = model(X_batch) 

Étape 2 : Calcul de la Perte (Loss Calculation)
Nous comparons les prédictions (y_logits) aux étiquettes réelles (y_batch) en utilisant la fonction de perte que nous avons définie. Le résultat est un score unique (un tenseur scalaire) qui représente l'erreur du modèle pour ce lot.







# `loss_fn` est une instance de notre fonction de perte (ex: nn.CrossEntropyLoss)
loss = loss_fn(y_logits, y_batch)

Étape 3 : Mise à Zéro des Gradients (Zero Gradients)
Avant de calculer les nouveaux gradients, il est impératif de remettre à zéro les gradients de l'itération précédente. PyTorch, par défaut, accumule les gradients. Si on ne les remet pas à zéro, les gradients de plusieurs lots se mélangeraient, rendant la mise à jour incorrecte.







# `optimizer` est une instance de notre optimiseur (ex: optim.Adam)
optimizer.zero_grad()

Étape 4 : La Passe Arrière (Backward Pass)
C'est ici que la magie d'Autograd opère. En appelant .backward() sur la perte, PyTorch remonte le graphe de calcul (que nous avons vu dans une leçon précédente) et calcule le gradient de la perte par rapport à chaque paramètre du modèle (tous les tenseurs avec requires_grad=True).

loss.backward()

Étape 5 : Mise à Jour des Poids (Optimizer Step)
Maintenant que les gradients sont calculés (et stockés dans l'attribut .grad de chaque paramètre), nous demandons à l'optimiseur d'effectuer la mise à jour. Il va modifier chaque paramètre en se basant sur le gradient calculé et le taux d'apprentissage (learning_rate).

optimizer.step()

Et c'est tout ! Le cycle recommence avec le lot suivant.


3. Implémentation Complète

En pratique, on encapsule cette logique dans des fonctions pour rendre le code plus propre et réutilisable. L'article "PyTorch in One Hour" et la documentation PyTorch montrent tous deux des exemples clairs.

Voici à quoi ressemble une fonction de boucle d'entraînement typique, en s'inspirant de la documentation de PyTorch.

import torch
import torch.nn as nn







# Supposons que nous avons :
# model: notre instance de nn.Module
# dataloader: notre instance de DataLoader
# loss_fn: notre fonction de perte
# optimizer: notre optimiseur

def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)






    # Mettre le modèle en mode entraînement
    model.train()
    
    for batch, (X, y) in enumerate(dataloader):






        # 1. Passe avant
        pred = model(X)
        






        # 2. Calcul de la perte
        loss = loss_fn(pred, y)







        # 3. Mise à zéro des gradients
        optimizer.zero_grad()
        






        # 4. Passe arrière
        loss.backward()
        






        # 5. Mise à jour des poids
        optimizer.step()

        if batch % 100 == 0:
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")

Le "Mantra en Trois Lignes"

Comme le souligne la vidéo "PyTorch in 1 Hour", une fois la passe avant et le calcul de la perte effectués, le cœur de l'apprentissage se résume à trois lignes de code que vous verrez partout.


4. model.train() vs model.eval()

Vous avez peut-être remarqué la ligne model.train() au début de la boucle. C'est une pratique essentielle. Appeler model.train() dit à votre modèle qu'il est en phase d'entraînement. Cela active certains comportements spécifiques à l'entraînement pour certaines couches, comme :

  • Dropout : Une technique de régularisation qui désactive aléatoirement des neurones pour éviter le sur-apprentissage.
  • BatchNorm (Batch Normalization) : Une technique qui normalise les activations entre les couches.

Inversement, lorsque nous voudrons évaluer notre modèle, nous appellerons model.eval(). Cela désactive le dropout et fige les statistiques de la normalisation par lot, garantissant que le modèle donne des prédictions cohérentes et déterministes. Nous verrons cela en détail dans la prochaine leçon.


5. Une Note sur les Appareils (CPU/GPU)

Bien que nous n'ayons pas encore abordé l'entraînement sur GPU, il est bon de savoir que l'intégration dans la boucle d'entraînement est simple. Il suffit de s'assurer que le modèle ET les données de chaque lot se trouvent sur le même appareil (par exemple, le GPU).


Conclusion

Félicitations ! Vous avez maintenant assemblé le moteur complet d'un réseau de neurones. En combinant un modèle, des données, une fonction de perte et un optimiseur dans une boucle d'entraînement structurée, vous pouvez faire apprendre un modèle à partir de données.

Points clés à retenir :

  • La boucle d'entraînement est composée d'une boucle externe sur les époques et d'une boucle interne sur les lots.
  • Le cœur de l'apprentissage pour chaque lot se déroule en cinq étapes :
    1. Passe avant (model(X)) pour obtenir les prédictions.
    2. Calcul de la perte (loss_fn(...)) pour mesurer l'erreur.
    3. Mise à zéro des gradients (optimizer.zero_grad()) pour repartir à zéro.
    4. Passe arrière (loss.backward()) pour calculer les gradients.
    5. Mise à jour des poids (optimizer.step()) pour ajuster le modèle.
  • Il est crucial d'utiliser model.train() pour s'assurer que les couches comme le Dropout se comportent correctement pendant l'entraînement.

Nous savons maintenant comment entraîner un modèle. Mais comment savoir s'il apprend bien ? Et comment évaluer sa performance sur des données qu'il n'a jamais vues ? C'est l'objet de notre prochaine leçon, où nous mettrons en œuvre une boucle d'évaluation et entraînerons notre premier réseau de neurones complet de bout en bout.