Skip to content

Commit 32aa7a0

Browse files
committed
fix(grad-checkpoint): close over mask so CUDA SDPA VJP error is avoided
nn.utils.checkpoint(layer)(hidden_states, mask) still passes the additive attention mask as a checkpointed positional input, so MLX takes a VJP w.r.t. the mask and CUDA's scaled_dot_product_attention raises 'does not support VJP with respect to mask' (Metal tolerates it, CUDA does not — this is why the gb10 grad-checkpoint probe still crashed). Pass mask/doc_ids via a closure fn to nn.utils.checkpoint(layer, fn) so they stay out of the differentiated input set while params + hidden_states are still recomputed. Grad correctness is unchanged (params threaded via the module arg); tests still green.
1 parent 966611d commit 32aa7a0

1 file changed

Lines changed: 27 additions & 16 deletions

File tree

cppmega_mlx/models/hybrid_lm.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2255,28 +2255,39 @@ def decoder_hidden_states(
22552255
expand_heads=True,
22562256
)
22572257
if self.config.grad_checkpoint:
2258-
# Use nn.utils.checkpoint (NOT mx.checkpoint). mx.checkpoint(module)
2259-
# only checkpoints w.r.t. the call's array inputs and does NOT thread
2260-
# the module's trainable parameters through the recompute, so under
2261-
# nn.value_and_grad it silently DROPS gradients for some params (the
2262-
# whole final MoE layer's expert weights, conv_bias, rope_inv_freq —
2263-
# verified |g|>0 without checkpoint, ==0 with mx.checkpoint). It also
2264-
# makes the additive attention mask a differentiable checkpoint input,
2265-
# tripping "scaled_dot_product_attention does not support VJP w.r.t.
2266-
# mask" on CUDA. nn.utils.checkpoint(module) checkpoints w.r.t. the
2267-
# module's trainable parameters AND inputs (threads params via
2268-
# module.trainable_parameters()), giving bitwise-correct gradients
2269-
# (dloss=0, grad max_rel=0) and not differentiating the mask.
2258+
# Activation checkpointing. Two requirements, both load-bearing:
2259+
#
2260+
# 1. Use ``nn.utils.checkpoint`` (NOT ``mx.checkpoint``).
2261+
# ``mx.checkpoint(module)`` only checkpoints w.r.t. the call's
2262+
# array inputs and does NOT thread the module's trainable params
2263+
# through the recompute, so under ``nn.value_and_grad`` it
2264+
# silently DROPS gradients for many params (verified: the whole
2265+
# final MoE layer's expert + shared_expert weights, conv_bias,
2266+
# rope_inv_freq → |grad|==0 with mx.checkpoint, |grad|>0 without,
2267+
# loss identical so the bug was invisible to a loss-only check).
2268+
# ``nn.utils.checkpoint(module, fn)`` threads params via
2269+
# ``module.trainable_parameters()`` → bitwise-correct grads.
2270+
#
2271+
# 2. Pass the attention ``mask`` (and ``doc_ids``) via a CLOSURE,
2272+
# not as a checkpointed positional input. Otherwise MLX takes a
2273+
# VJP w.r.t. the additive mask and CUDA's
2274+
# scaled_dot_product_attention raises "does not support VJP with
2275+
# respect to mask" — the real blocker for grad-checkpointed
2276+
# Path-B training on gb10. The mask is a constant; closing over
2277+
# it keeps it out of the differentiated input set while params +
2278+
# ``hidden_states`` are still recomputed.
22702279
for layer_index, layer in enumerate(self.layers):
22712280
if stop_layer_index is not None and layer_index >= stop_layer_index:
22722281
break
2273-
checkpointed = nn.utils.checkpoint(layer)
22742282
if layer.backend == "engram" and document_ids is not None:
2275-
hidden_states = checkpointed(
2276-
hidden_states, mask, doc_ids=document_ids
2283+
fn = (
2284+
lambda hs, _layer=layer, _mask=mask, _doc=document_ids: _layer(
2285+
hs, _mask, doc_ids=_doc
2286+
)
22772287
)
22782288
else:
2279-
hidden_states = checkpointed(hidden_states, mask)
2289+
fn = lambda hs, _layer=layer, _mask=mask: _layer(hs, _mask)
2290+
hidden_states = nn.utils.checkpoint(layer, fn)(hidden_states)
22802291
else:
22812292
attention_layer_idx = 0
22822293
for layer_index, layer in enumerate(self.layers):

0 commit comments

Comments
 (0)