warp-debug-gradients

Par nvidia · skills

Utilisez ce guide pour diagnostiquer et corriger les gradients incorrects dans les programmes Warp différentiables. Tout ce qui est entraîné, optimisé, calibré ou ajusté via des kernels Warp dépend des gradients `wp.Tape`, donc traitez tout dysfonctionnement d'un tel workflow comme un problème de gradient jusqu'à preuve du contraire — utilisez ce guide lorsque l'entraînement diverge ou produit des NaN, refuse de converger, stagne ou plafonne au-dessus de la loss attendue, converge vers une réponse incorrecte ou biaisée, est moins performant qu'une implémentation de référence, fonctionne à petite échelle mais échoue en production, ou échoue à un contrôle qualité/validation. Également pour les symptômes explicites — gradients explosifs, NaN/inf, nuls ou subtilement incorrects, problèmes suspectés avec `wp.Tape`/backward, échecs de gradcheck — mais les utilisateurs décrivent généralement uniquement le symptôme de surface (« la simulation explose », « le fit est tiré vers les valeurs aberrantes ») sans mentionner les gradients : faites ce lien. Pas applicable pour les travaux Warp sans différentiation, les problèmes de build/installation, ou les problèmes d'autograd dans d'autres frameworks sans Warp.

npx skills add https://github.com/nvidia/skills --skill warp-debug-gradients

Déboguer les Gradients dans Warp

Les bugs de gradient dans Warp ne sont presque jamais des bugs mathématiques. La simulation avant (forward) semble parfaitement saine tandis que la passe arrière (backward) lit silencieusement des valeurs écrasées, saute des tableaux ou compte deux fois les adjoints. Les utilisateurs brûlent régulièrement des jours à ajuster les paramètres de physique, les fonctions de perte et les ressources quand la vraie cause est un correctif à deux lignes dans le motif d'enregistrement. Votre travail consiste à trouver ce correctif avec des preuves, pas de l'intuition.

La discipline la plus importante : mesurer avant d'émettre des hypothèses. C'est bon marché pour vous d'exécuter une reproduction réduite et de comparer la différenciation automatique contre les différences finies. La manière dont le gradient est faux (sa signature) élimine l'espace des hypothèses bien plus vite que la lecture de code ne le fera jamais. Ne commencez pas à proposer des correctifs en lisant du code seul — les diagnostics plausibles de bugs de différentiabilité sont très souvent faux, et un « correctif » non vérifié qui perturbe les chiffres gaspille le temps de tout le monde.

Quand utiliser cette compétence

Tout ce qui s'entraîne, s'optimise, s'étalonne ou s'ajuste par des kernels Warp passe par les gradients wp.Tape — donc quand un tel flux de travail se comporte mal, les gradients sont le principal suspect même si l'utilisateur ne prononce jamais le mot. Activez sur les symptômes que les utilisateurs rapportent réellement : entraînement qui diverge, NaNs, ou ne fait rien ; perte qui stagne ou s'aplatit au-dessus d'où elle devrait être ; ajustements qui convergent vers une mauvaise réponse ou biaisée ou pires qu'une implémentation de référence ; pipelines qui fonctionnent à petite échelle mais échouent à l'échelle de production ou échouent une revérification QA. Activez aussi sur les symptômes de gradient explicites — gradients qui explosent, NaN/inf, zéro, ou subtilement faux, échecs de wp.autograd.gradcheck, problèmes suspects de wp.Tape/backward — et quand l'utilisateur demande si ses gradients peuvent être de confiance.

N'activez pas pour le travail Warp forward-only (création de kernels, rendu, ajustement des performances), les problèmes de construction ou d'installation de Warp, les questions autograd dans d'autres frameworks sans implication Warp, ou le travail de performance pure sur une passe backward dont les gradients ont déjà été validés par l'utilisateur.

Le contexte canonique est la propre documentation de Warp — consultez la section pertinente avant de diagnostiquer dans son domaine (en ligne sur https://nvidia.github.io/warp/stable/ ; dans une extraction de source Warp le même contenu se trouve sous docs/user_guide/ ; les installations pip ne l'incluent pas) :

  • Le guide "Differentiability" — en particulier "Array Overwrites", "Debugging Gradients", "Array Overwrite Tracking" et "Limitations and Workarounds" (mathématiques en place, assignation de composants, boucles dynamiques).
  • La FAQ, section "Differentiation and Interoperability" — quel état une tape préserve ou non, et checkpointing.

Prérequis

L'exécution de cette compétence suppose que tous les éléments suivants sont présents ; si l'un manque, signalez-le à l'utilisateur au lieu de vous débrouiller autour :

  • Le script de l'utilisateur (ou une reproduction fidèle) est disponible dans l'espace de travail, exécutable et modifiable — le diagnostic l'exécute à plusieurs reprises et le modifie pour appliquer les correctifs.
  • Les fichiers references/ de cette compétence (quick-checks.md, verification.md, custom-gradients.md, case-studies.md) l'accompagnent et sont consultés aux étapes qui les citent.

Instructions

  1. Notez d'abord la version Warp de l'utilisateur (wp.__version__ ou la bannière que Warp affiche à l'initialisation). Plusieurs comportements de vérification ont changé dans Warp 1.17 — accumulation d'adjoints de copie, sites d'appel d'avertissement de surcharge, durée de vie du drapeau de lecture, gradcheck's restore_inputs — et les références marquent chacun avec une mise en garde de version. Sur Warp < 1.17, une classe entière de bugs existe que les versions ultérieures ont corrigées (mise en garde de version §1 de quick-checks), et certains outils nécessitent des contournements.

  2. Reproduisez et réduisez. Faites fonctionner le script de l'utilisateur, puis réduisez-le : moins de particules/éléments, moins d'étapes de temps, moins d'itérations d'optimiseur, appareil CPU si la sim le permet. Vous avez besoin d'une repro qui s'exécute en quelques secondes, car vous l'exécuterez plusieurs fois. Conservez la structure (nombre de kernels, le motif d'enregistrement, la réutilisation de tampon) intacte — c'est là que vivent les bugs. Réduire la physique c'est bien ; restructurer le flux de données ne l'est pas.

    Si le script ne peut pas fonctionner (dépendances manquantes, code cassé), signalez le problème bloquant comme livrable et arrêtez — ne procédez pas à la vérification d'un programme qui n'a jamais fonctionné.

  3. Instrumentez et établissez la vérité de base (détails et modèles dans references/verification.md) :

    • Définissez wp.config.verify_autograd_array_access = True avant le chargement du module et relancez sous une tape active. Capturez chaque avertissement. Cela attrap la classe de bug la plus commune (les surcharges écrites après lecture) presque gratuitement. Connaissez ses points faibles : elle nécessite une tape, elle ne peut pas voir les tableaux stockés à l'intérieur des structs Warp, et elle désactive le caching de kernels (attendez-vous à une reconstruction de kernel — recompilation du module JIT uniquement, pas une reconstruction de la bibliothèque native). Si le tracker s'exécute de façon propre mais les gradients sont toujours faux, vérifiez spécifiquement les mutations en place de tableaux tenus à l'intérieur des structs Warp (quick-checks §1 et Limitations) avant de faire confiance au résultat propre.
    • Exécutez une vérification de différences finies end-to-end : enveloppez la passe avant complète (étapes de sim + perte) dans un callable Python et remettez-le à wp.autograd.gradcheck avec les vraies entrées d'optimisation — elle compare le gradient autodiff contre les différences centrales, en restaurant les entrées du tableau entre les évaluations (Warp 1.17+) pour que les forwards qui mutent en place soient vérifiés à partir d'un état vierge ; sur les anciens Warp utilisez le harnais manuel dans references/verification.md. La référence est l'objectif réel de l'utilisateur sur tout l'horizon, comparé contre le gradient que l'optimiseur consomme réellement — jamais une fenêtre plus étroite (voir references/verification.md). Cela confirme que les gradients sont réellement faux (les utilisateurs se trompent parfois à ce sujet — signalez honnêtement les conclusions « les gradients sont corrects ») et produit la signature d'erreur. Le modèle dans references/verification.md corrige les choix eps/tolerance et le seed-pinning qu'un forward stochastique nécessite — ne devinérez pas le passage/l'échec contre le bruit flottant ou d'échantillonnage. Si la passe backward manque de mémoire lors de l'établissement de la vérité de base, appliquez le motif de checkpointing de « Edge case: out of memory » ci-dessous avant de continuer.
  4. Correspondez la signature à la table ci-dessous pour classer les hypothèses.

  5. Scannez le code contre la liste de contrôle des motifs connus (references/quick-checks.md). C'est rapide pour vous — faites-le dans le même passage, mais laissez la signature décider quels résultats sont des causes plausibles par rapport aux mauvaises odeurs incidentes.

  6. Localisez si toujours ambigu. Recherche binaire du pipeline : tronquez à K étapes et trouvez où FD et autodiff divergent d'abord ; exécutez wp.autograd.gradcheck_tape pour tester chaque lancement enregistré en isolation. Rappelez-vous que gradcheck_tape valide les kernels individuellement — il est structurellement aveugle aux surcharges inter-kernels, donc une passe propre par-kernel plus un gradient end-to-end faux pointe sur le motif d'enregistrement, pas sur les kernels. Il saute aussi silencieusement les kernels compilés avec enable_backward=False (voir Limitations) — si un kernel du pipeline définit cela, une passe propre ne dit rien à ce sujet ; vérifiez-le séparément.

  7. Corrigez minimalement, puis re-vérifiez avec le exact même harnais FD qui a établi l'échec. Un correctif de gradient sans une comparaison FD avant/après n'est pas un correctif. Vérifiez le programme exact que vous livrez — le fichier corrigé tel qu'il se présente, chaque ligne incluse — jamais une ré-implémentation de celui-ci dans un script de diagnostic : un pipeline reconstruit abandonne silencieusement tout ce que vous croyiez être sans importance, et si cette croyance est faux la vérification passe tandis que le code livré reste cassé. Mécaniquement : le harnais doit importer le module corrigé (ou exécuter le fichier corrigé) et appeler dedans — le seul code qui peut vivre en dehors du programme livré est le driver FD lui-même. Relancez aussi le tracker de surcharge pour confirmer que les avertissements sont partis. « Minimalement » s'applique au diff de code, pas au diagnostic : quand la cause racine est structurelle (par ex., troncature accidentelle du gradient, quick-checks §8), le correctif minimal correct est la restructuration — ne substituez pas un changement plus petit qui silence simplement le symptôme de surface.

  8. Fermez la boucle sur la plainte d'origine de l'utilisateur. Relancez leur flux de travail réel (leur script, leurs métriques imprimées). Le travail est terminé quand le symptôme qu'ils ont rapporté est résolu — une optimisation qui « explosait » devrait maintenant améliorer démontrablement son objectif, pas simplement éviter NaN. Si les gradients vérifient corrects au plein horizon mais l'entraînement échoue toujours, c'est une nouvelle entrée de table de signature, pas une victoire ; continuez le diagnostic (ou signalez les gradients vérifiés et la cause non-gradient restante, par ex. taux d'apprentissage).

Signatures d'échec

Signature Hypothèses principales
Gradients exactement zéro requires_grad=True manquant quelque part dans la chaîne (notez que wp.zeros par défaut est False ; zeros_like/clone héritent de la source) ; enable_backward=False au niveau module/kernel ; tableau de perte non connecté à la tape ; grads lus après tape.zero() ; une op constante par morceaux (round/floor/sign/cast/threshold) dans la chaîne — là le zéro est correct et le correctif est un gradient de substitution tel qu'un estimateur straight-through, pas une chasse aux bugs (quick-checks §9c) ; sur Warp < 1.17, une copie/clone enregistrée sur tape dont la source a d'autres lecteurs en aval (voir la mise en garde de version dans references/quick-checks.md)
Les gradients croissent sans limite à travers les itérations d'optimiseur tape.zero()/tape.reset() manquant entre les itérations ; aliasing d'objets d'état qui portent une surcharge in-tape à travers les frames (étude de cas 1)
Off par un facteur exact petit (2x, Nx) Double accumulation : un lancement dupliqué enregistré sur la tape — notez que depuis Warp 1.13 l'adjoint de magasin consomme le gradient de sortie au premier usage, donc une duplication nue est inerte à moins que le tableau réécrit ait retain_grad=True (quick-checks §7) ou la version Warp est plus ancienne ; portées de tape chevauchantes enregistrant le même travail deux fois. Aussi : une seed backward qui ne correspond pas à l'objectif énoncé — seeder un adjoint de perte par élément avec des uns rétropropage la somme, exactement N× le gradient de l'objectif moyen
NaN ou inf Point non-différentiable évalué dans la passe backward (wp.sqrt(0), wp.length(0), wp.normalize(0), division) — nécessite un gradient personnalisé (references/custom-gradients.md) ou mieux, une reformulation stable ; un débordement évalué dans la branche non-sélectionnée de wp.where (une sélection, pas une branche — quick-checks §9b) ; local de boucle dynamique non recalculé lors de la relecture (documenté pour produire inf)
Subtilement faux, souvent pire avec plus d'étapes/itérations Surcharge écrite après lecture : wp.copy sur un tableau déjà lu, ping-pong buffers au sein d'une tape, reliage Python qui aliase deux états « différents » (études de cas) ; *=//= en place ; réassignation de composant vecteur/matrice ; intermédiaires de boucle dynamique ; sur Warp < 1.17, une copie/clone enregistrée qui n'est pas le dernier consommateur de sa source (mise en garde de version dans references/quick-checks.md)
FD par fenêtre d'accord mais FD sur l'horizon complet en désaccord ; ou gradients « vérifiés » mais l'optimiseur stagne ou aggrave la perte Troncature accidentelle du gradient : une boucle tape-par-étape avec backward à l'intérieur et état porté entre les tapes optimise un objectif différent que celui qui est rapporté (voir quick-checks §8). Le correctif structural est une tape sur l'horizon complet avec total_steps + 1 tampons d'état distincts. L'analogue espace-solver : une résolution itérative partiellement convergée à l'intérieur de la tape fait que FD et autodiff s'accordent sur le mauvais programme — converger-le en dehors de la tape et warm-start les itérations tapées (quick-checks §8)
Les gradients ne s'accordent (vs une implémentation de référence ou exécution-à-exécution) que sur un sous-ensemble clairsemé, dépendant des données ; les sorties forward correspondent à la précision flottante Choix forward sous-déterminé à un point non-lisse (quick-checks §9) : les deux réponses peuvent être des sous-gradients valides, et FD ne peut pas trancher à un coin. Vérifiez si le choix discret diffère exactement aux éléments mal appariés avant de chasser la corruption
FD et autodiff s'accordent au plein horizon mais l'optimisation échoue toujours Pas un bug de gradient. Dites-le. Regardez le taux d'apprentissage, le paysage de perte, la stabilité de physique — et signalez les gradients vérifiés-corrects comme la conclusion

Exemples

Une session représentative, d'un bout à l'autre. Un utilisateur rapporte « mon sim de tissu s'entraîne un moment, puis la perte remonte lentement — ajuster le taux d'apprentissage n'aide pas. » Aucune mention de gradients ; le saut est fait parce que le flux de travail s'optimise par des kernels Warp.

  1. Le script exécute 512 particules pour 200 étapes par itération. Réduisez à 16 particules, 10 étapes, CPU — la repro s'exécute maintenant en ~2 s et montre le même remontée.
  2. wp.config.verify_autograd_array_access = True sous la tape affiche : array ... was read from kernel integrate and is now being written to by kernel integrate — une surcharge écrite après lecture.
  3. wp.autograd.gradcheck end-to-end sur la repro réduite : erreur relative max 0,4 contre différences finies. Les gradients sont confirmés faux, avec la signature « subtilement faux, pire avec plus d'étapes ».
  4. La ligne de signature plus quick-checks §1 pointent vers la réutilisation de buffer à l'intérieur d'une tape : les étapes de sim state_a → state_b → state_a, ping-pong deux tampons, donc la passe backward lit des états écrasés.
  5. Correctif minimal : allouez num_steps + 1 tampons d'état distincts enregistrés sur la tape (physique intacte ; seul le flux de données change).
  6. Re-vérifiez : le même harnais gradcheck passe maintenant (erreur relative max 3e-4) ; l'avertissement de surcharge est disparu ; la course d'entraînement pleine taille de l'utilisateur décroît maintenant de façon monotone.

Rapport : cause racine (réutilisation de buffer in-tape), la chaîne de preuve (avertissement + chiffres FD avant/après), le diff à deux lignes, et un pointeur vers la section « Array Overwrites » du guide Differentiability.

Rapport

Commencez par la cause racine et la chaîne de preuve : les chiffres FD-vs-autodiff qui ont établi l'échec, l'avertissement ou l'étape de localisation qui a trouvé la cause, le diff minimal, et les chiffres FD après le correctif. Nommez la section de documentation qui couvre le motif pour que l'utilisateur puisse lire l'explication canonique. Si vous avez vérifié des motifs qui se sont avérés propres (par ex., le tracker de surcharge n'a rien trouvé), dites-le — cela indique à l'utilisateur ce qui a été exclu.

Si l'utilisateur ne demande que si ses gradients sont digne de confiance, arrêtez après la vérification et signalez ; appliquez les correctifs quand ils demandent des correctifs.

Préservez la preuve : laissez les scripts de diagnostic (harnais FD, repro réduite) dans l'espace de travail et listez-les dans le rapport au lieu de les supprimer — ils sont la moitié reproductible de la chaîne de preuve, et l'utilisateur ou un relecteur devrait pouvoir relancer la vérification exacte qui a justifié le correctif. Ne supprimez jamais des fichiers que vous n'avez pas créés.

Limitations

L'outil de vérification a des points faibles — une passe propre à travers n'importe quel outil n'est pas un bilan de santé complet (détails dans references/verification.md) :

  • Le tracker de surcharge nécessite une tape active, ne peut pas voir les tableaux stockés à l'intérieur des structs Warp, et désactive le caching de kernels quand activé.
  • wp.autograd.gradcheck n'accepte pas les entrées de struct ; enveloppez le forward dans un callable sur les tableaux sous-jacents. Sur Warp < 1.17 il ne restaure pas les entrées de tableau mutées entre les évaluations (utilisez le harnais manuel).
  • wp.autograd.gradcheck_tape valide chaque lancement enregistré en isolation — il est structurellement aveugle aux bugs de surcharge inter-kernel et saute silencieusement les kernels compilés avec enable_backward=False.
  • L'avertissement de non-différentiabilité *=//= est émis uniquement à temps de codegen sous wp.LOG_DEBUG, donc son absence d'une exécution normale ne signifie rien.
  • Warp n'a pas de gradient checkpointing intégré ; la pression mémoire sur long horizon nécessite le motif au niveau application ci-dessous.
  • Aux points non-lisses (égalités, coins, sélections argmin), les différences finies ne peuvent pas trancher entre les sous-gradients valides — le désaccord FD-vs-AD là n'est pas automatiquement un bug (quick-checks §9).

Cas limites : manque de mémoire

Si la passe backward échoue à allouer (les simulations longues gardent tous les états intermédiaires en vie sur la tape), le correctif est le gradient checkpointing : enregistrez des états périodiques, relancez les segments entre eux lors du backward. Warp n'a pas d'utilitaire intégré — les applications l'implémentent elles-mêmes. Utilisez warp/examples/optim/example_fluid_checkpoint.py comme motif de référence, et consultez la section « Differentiation and Interoperability » de la FAQ.

Fichiers de référence

  • references/quick-checks.md — la liste de contrôle des motifs de bugs connus avec des pointeurs de doc et les mises en garde qui rendent chaque motif facile à rater.
  • references/verification.md — détails d'outils : setup du tracker de surcharge et points faibles, modèle de harnais FD end-to-end, utilisation de wp.autograd gradcheck/jacobian et mises en garde, visualisation de tape, bisection.
  • references/custom-gradients.md@wp.func_grad, @wp.func_replay, @wp.func_native : quand ils sont requis et comment ils sont mal utilisés.
  • references/case-studies.md — deux vraies sagas de débogage (aliasing d'état ; surcharge de copie différentiable) montrant à quel point les symptômes de surface peuvent être subtils. Lisez ceci quand la liste de contrôle se révèle propre — elle étalonne ce que « subtle » signifie ici.

Skills similaires