Séparer microbatch, passage arrière et mise à jour
Le microbatch est le groupe d’exemples traité par un passage avant sur une réplique. Le passage arrière calcule sa contribution aux gradients. La mise à jour de l’optimiseur utilise les gradients disponibles pour modifier les paramètres. Avec accumulation, plusieurs passages avant/arrière précèdent cette mise à jour ; les paramètres restent inchangés pendant le groupe.
Dans PyTorch, les gradients s’accumulent dans les tenseurs prévus à cet effet. Effacer les gradients après chaque microbatch annulerait donc l’accumulation souhaitée. À l’inverse, oublier de les remettre à zéro entre deux groupes ferait contribuer des exemples de la mise à jour précédente.
Définissez votre unité de journalisation : numéro de microbatch, mise à jour de l’optimiseur, exemples ou tokens vus. Le mot « step » seul est ambigu. Une courbe de perte ne se compare pas correctement si l’axe représente huit fois plus d’exemples dans l’une des expériences.
Sources techniques : PyTorch — accumulation et remise à zéro des gradients
Calculer le batch effectif sans compter deux fois les GPU
Notons m le nombre d’exemples par microbatch et par réplique, A le nombre de microbatches accumulés, D le nombre de répliques de parallélisme de données. Si ces tailles sont constantes et les exemples correctement répartis, le nombre d’exemples contribuant à une mise à jour globale est m × A × D.
Le facteur D ne désigne pas nécessairement toutes les cartes de la machine. Des GPU qui se partagent un même modèle par parallélisme tensoriel ou de pipeline ne deviennent pas autant de répliques de données. Inscrivez les groupes réellement configurés, pas simplement la quantité commerciale du lot.
Exemple arithmétique : deux exemples par microbatch, huit accumulations et deux répliques donnent 32 exemples par mise à jour globale. Chaque réplique traite seize exemples dans ce groupe. Le tableau compare des comptages ; il ne classe pas leur mémoire ou leur vitesse.
| m | A | D | Exemples par mise à jour globale |
|---|---|---|---|
| 2 | 8 | 2 | 32 |
| 1 | 16 | 2 | 32 |
| 4 | 4 | 2 | 32 |
| 2 | 8 | 1 | 16 |
Sources techniques : PyTorch — batch effectif et accumulation en précision mixte · PyTorch — comportement des répliques DistributedDataParallel
Normaliser la perte selon les éléments réellement évalués
Pour une perte moyenne sur des microbatches contenant le même nombre d’éléments pertinents, diviser chaque contribution par A donne la moyenne du groupe. Cette règle suppose que le framework ne fait pas déjà cette normalisation. Avec un outil qui gère l’accumulation, relisez son contrat avant d’ajouter une division manuelle.
Pour une perte moyenne par token, des longueurs différentes changent le dénominateur. Il faut rapporter la somme des pertes aux tokens effectivement supervisés du groupe, hors padding et positions ignorées. La moyenne des moyennes de microbatches ne donne pas généralement le même objectif.
Exemple théorique : un microbatch compte 512 tokens supervisés avec une perte moyenne de 2 ; un autre en compte 1 536 avec une moyenne de 4. La moyenne pondérée vaut (512 × 2 + 1 536 × 4) ÷ 2 048 = 3,5. La moyenne non pondérée vaut 3 et surpondère le petit groupe. Ces valeurs illustrent uniquement le calcul.
Sources techniques : Hugging Face Accelerate — accumulation avec exemples de tailles variables
Organiser un groupe complet d’accumulation
Préparez d’abord les frontières du groupe et son dénominateur. Pour chaque microbatch, calculez la sortie, la perte normalisée et le passage arrière sans mise à jour intermédiaire. Libérez les sorties dont vous n’avez plus besoin ; conserver des pertes attachées à leur graphe dans une liste peut prolonger la vie des allocations.
Après la dernière contribution, appliquez les opérations prévues sur le gradient complet, puis la mise à jour. Réinitialisez ensuite les gradients pour le groupe suivant. Si le parcours se termine avec moins de A microbatches, choisissez explicitement de traiter ce groupe partiel avec son vrai dénominateur ou de l’écarter ; notez les exemples concernés.
Avec une précision mixte utilisant GradScaler, le facteur d’échelle reste constant pendant l’accumulation. La remise à l’échelle réelle et un éventuel clipping interviennent après les contributions ; la mise à jour du scaler suit la tentative de pas. Les contrôles de valeurs non finies peuvent empêcher la modification des paramètres.
Le scheduler doit suivre l’unité annoncée par votre boucle. S’il est défini par mise à jour de l’optimiseur, l’appeler à chaque microbatch changerait le calendrier. Consignez séparément les tentatives et les mises à jour réellement appliquées lorsque votre système peut en sauter.
Sources techniques : PyTorch — graphes autograd et tenseurs conservés pour backward · PyTorch — accumulation, unscale, clipping et GradScaler
En multi-GPU, vérifier réduction et répartition des exemples
DistributedDataParallel synchronise les gradients entre répliques. Dans son comportement usuel, la réduction les moyenne ; une perte sommée et une perte moyennée localement n’ont donc pas la même échelle. Avec des nombres de tokens différents selon les répliques, le dénominateur global et cette réduction doivent être considérés ensemble.
Vérifiez les IDs réellement traités : dupliquer involontairement les mêmes exemples sur toutes les cartes n’augmente pas d’autant l’information du groupe. Pour retarder les communications intermédiaires, no_sync peut être employé sur les microbatches précédant la synchronisation finale ; son contexte doit couvrir aussi le passage avant.
Ne transposez pas cette règle à tous les systèmes distribués. Sharding des états, pipeline, hooks de communication et frameworks peuvent changer les opérations effectives. Commencez par la configuration prise en charge par votre outil, puis contrôlez un groupe complet sur chaque réplique.
Sources techniques : PyTorch — réduction des gradients et portée de no_sync dans DDP
Pourquoi le même batch effectif ne garantit pas la même expérience
L’égalité m × A × D est un comptage. Pour retrouver un gradient de grand batch, il faut notamment des contributions correctement pondérées, le même état des paramètres pendant le groupe et des opérations compatibles avec cette décomposition. La proximité numérique se vérifie avec une tolérance adaptée ; elle ne se déduit pas du seul produit.
BatchNorm calcule des statistiques à partir des entrées de son passage : plusieurs petits microbatches ne lui présentent pas les mêmes groupes qu’un grand batch. Les opérations aléatoires, l’ordre des calculs et les arrondis peuvent également varier. Ne promettez pas des poids finaux bit à bit identiques.
Un changement de batch global peut aussi modifier le nombre de mises à jour pour un même nombre d’exemples vus. Fixez à l’avance l’axe de comparaison et votre règle de qualité. Ne changez pas simultanément le taux d’apprentissage, le scheduler et la durée sans documenter ces nouvelles hypothèses.
Sources techniques : PyTorch — statistiques de BatchNorm1d · PyTorch — limites de reproductibilité
Contrôler la mémoire et décider de la suite
Instrumentez un groupe incluant les passages arrière et la première mise à jour, puis les groupes suivants. Une réussite au forward ne valide pas les gradients ou les états créés par l’optimiseur. Les compteurs doivent conserver leur périmètre par device. Le dossier mémoire fournit la méthode de lecture des baselines et pics.
Si un groupe échoue, réduisez le microbatch et recalculez A pour conserver le batch effectif visé lorsque ce choix reste pertinent. Cette modification ne garantit ni une division proportionnelle du pic ni une meilleure durée. Si les poids ou les états dominent, l’accumulation seule peut être insuffisante.
Avant une campagne, contrôlez une mise à jour sur un petit jeu maîtrisé : mêmes exemples, perte pondérée, gradients finis, frontière de groupe et nombre de pas. Puis évaluez la qualité avec le protocole retenu. Le petit MLP d’inférence du dossier téléchargeable n’exécute pas cette recette d’entraînement ; il ne remplace pas ce contrôle.
Votre fiche finale réunit m, A, D, les tokens supervisés, la précision, la normalisation, le traitement du dernier groupe et les pics observés. Revenez ensuite au choix de configuration et au budget d’expériences, en séparant préparation, essais et résultats réellement acceptés.
- Perte anormalement petite : rechercher une double division par l’accumulation.
- Résultat variant avec le découpage : vérifier tokens ignorés et moyenne des moyennes.
- Accumulation sans effet : contrôler zero_grad et optimizer.step.
- Mémoire croissante : rechercher les références conservées entre microbatches.
- Calendrier décalé : distinguer passages arrière et mises à jour.
Sources techniques : Hugging Face — postes mémoire d’un entraînement
Questions pratiques
Accumuler seize microbatches multiplie-t-il la mémoire par seize ?
Pas nécessairement : les contributions sont traitées successivement. Les gradients persistent, tandis que les activations inutiles peuvent être libérées. Le pic dépend cependant du modèle, des références conservées et des états d’optimiseur ; mesurez le groupe complet.
Deux GPU doublent-ils toujours le batch effectif ?
Seulement si ces GPU participent comme deux répliques de données avec le microbatch annoncé. Des cartes qui se partagent un même modèle ne constituent pas automatiquement deux répliques. Vérifiez les groupes distribués et les exemples traités.
Puis-je diviser toutes les pertes par le nombre d’accumulations ?
Cette règle simple correspond à des microbatches de poids égal, sans normalisation déjà gérée par le framework. Avec des nombres variables de tokens supervisés ou un dernier groupe incomplet, utilisez le dénominateur réel de l’objectif.