torch-directml: F.cross_entropy(reduction='none') silently produces zero gradient on backward (0.2.5.dev240914 / torch 2.4.1+cpu)
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 55/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- python, pytorch
- Ambito
- backend, machine-learning
Direzione di ricerca
Start with the minimal reproduction described in the issue and compare F.cross_entropy with reduction='none' against the manual log_softmax+gather and reduction='mean' controls. Review diag_gradflow_repro.py and the fused cross-entropy forward/backward path on the DirectML device. Done means reduction='none' produces a nonzero upstream gradient and the regression is covered by a focused test.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
On torch-directml==0.2.5.dev240914 (torch 2.4.1+cpu), calling
torch.nn.functional.cross_entropy(logits, targets, reduction='none')
and then combining/reducing the per-element losses manually (e.g. to
apply a custom per-token weighting before backward) produces a normal
forward pass, a normal-looking loss value, and a normal-looking autograd
graph — but .backward() on the resulting loss yields an all-zero
gradient on every upstream parameter. No error, warning, or NaN is
raised. Training appears to proceed normally (loss values printed each
step look plausible) while no parameter is actually being updated.
We hit this in a real LoRA fine-tuning run: after ~450 optimizer steps
with a per-token-weighted cross-entropy loss built on
reduction='none', none of the LoRA adapter's trainable tensors had
moved from their initial values, despite the run completing without
error and reporting a steadily-changing loss curve throughout.
Environment
torch==2.4.1+cputorch-directml==0.2.5.dev240914transformers==4.46.3,peft==0.20.0,accelerate==1.15.0- Windows, AMD Radeon 8060S-class integrated GPU, DirectML device
Minimal repro
The following isolates the bug to specifically the fused
reduction='none' kernel, by comparing three mathematically-equivalent
ways of computing the same per-token loss on the same model, inputs,
and device, and checking the gradient norm on a known trainable tensor
after a single backward() call:
import torch
import torch.nn.functional as F
import torch_directml
device = torch_directml.device()
# ... set up a small model with a trainable parameter (e.g. a linear
# layer or a LoRA adapter tensor) on `device`, and a batch of
# logits/targets on the same device ...
# 1. Fused kernel, reduction='none', then manual mean:
loss_a = F.cross_entropy(logits, targets, reduction='none').mean()
loss_a.backward()
print("fused reduction='none':", param.grad.norm().item()) # -> 0.0
# reset gradients, recompute logits fresh, then:
# 2. Manual unfused equivalent:
log_probs = F.log_softmax(logits, dim=-1)
per_token_loss = -log_probs.gather(-1, targets.unsqueeze(-1)).squeeze(-1)
loss_b = per_token_loss.mean()
loss_b.backward()
print("manual log_softmax+gather:", param.grad.norm().item()) # -> nonzero
# reset gradients, recompute logits fresh, then:
# 3. Control: built-in mean-reduction path (as used internally by
# Hugging Face Transformers' default loss computation):
loss_c = F.cross_entropy(logits, targets, reduction='mean')
loss_c.backward()
print("built-in reduction='mean':", param.grad.norm().item()) # -> nonzero
In our real repro (diag_gradflow_repro.py, run against a live LoRA
adapter tensor on a real training batch), the three gradient norms were:
| Path | grad_norm |
|---|---|
F.cross_entropy(reduction='none'), fused |
0.0 |
Manual log_softmax + gather |
0.0534 |
F.cross_entropy(reduction='mean') (HF-internal control) |
0.0699 |
Only the fused reduction='none' path returns a zero gradient. The
manual unfused equivalent and the built-in reduction='mean' control
both produce a real, nonzero gradient on the identical batch, model, and
device — which rules out the model, the data, or the optimizer as the
cause and isolates the problem to the fused kernel's backward pass under
reduction='none' specifically.
We're happy to share a fully self-contained, minimal script (no
transformers/peft dependency) reproducing this on a plain
nn.Linear if that's more useful for triage — let us know.
Why this is worth flagging as high-severity
There is no error, warning, or NaN anywhere in this failure. The loss
value printed at each step looks plausible and can even appear to
decrease over a run (if the reduction happens elsewhere in the
pipeline), so this is not something a user would discover by watching
training logs — only by explicitly checking that trainable parameters
actually changed after training. We only found it because we added a
pre-flight gradient-flow assertion as a general safety practice. We'd
guess this could silently affect anyone using a custom/per-token loss
built on reduction='none' on this backend, not just our specific
LoRA use case.
What we're NOT reporting here
This is unrelated to #702 (masked_fill uint8 overflow during
gpt-neo inference) — we hit that one separately during the same
project and worked around it, but it's a different bug in a different
op with a different (loud, exception-raising) failure mode. Filing this
as a new issue rather than a comment on #702 since the root cause,
affected op, and failure signature are all different.
- Lingua principale
- C++
- Stelle
- 2.6k
- Fork
- 338
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/DirectML
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 68/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 62/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 45/100
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
Tutte le issue di microsoft/DirectML
Issue simili
-
prio:medium status:idea type:feat
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
-
tests
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
-
kind/bug needs-sig needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
drogonframework/drogon#2605 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
ArthurSonzogni/FTXUI#1363 ·