Skip to content

Commit 2bf5e05

Browse files
HJSangclaude
andcommitted
Fallback cross entropy: one copy, then in place as Megatron's kernel
The shift, exp and gradient each allocated another full-size fp32 logits buffer, so a run without Megatron could OOM where the fused kernel fits. One copy keeps the caller's logits untouched; the rest works in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent f23f5f1 commit 2bf5e05

1 file changed

Lines changed: 7 additions & 5 deletions

File tree

‎miles/backends/training_utils/loss/hub/math_utils.py‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,19 @@ class _VocabParallelCrossEntropy(torch.autograd.Function):
338338
@staticmethod
339339
def forward(ctx, logits: torch.Tensor, target: torch.Tensor, group: dist.ProcessGroup | None) -> torch.Tensor:
340340
ctx.input_dtype = logits.dtype
341-
logits = logits.float()
341+
# The one full-size copy: it keeps the caller's logits untouched, and the rest works on it in
342+
# place as Megatron's kernel does, so the fallback needs no more memory than that kernel.
343+
logits = logits.to(torch.float32, copy=True)
342344
logits_max = logits.max(dim=-1)[0]
343345
dist.all_reduce(logits_max, op=dist.ReduceOp.MAX, group=group)
344-
logits = logits - logits_max.unsqueeze(-1)
346+
logits.sub_(logits_max.unsqueeze(-1))
345347

346348
partition_vocab_size = logits.size(-1)
347349
vocab_start = dist.get_rank(group) * partition_vocab_size
348350
target_mask = (target < vocab_start) | (target >= vocab_start + partition_vocab_size)
349351
masked_target = (target - vocab_start).masked_fill(target_mask, 0)
350352
predicted_logits = logits.gather(-1, masked_target.unsqueeze(-1)).squeeze(-1).masked_fill(target_mask, 0.0)
351-
exp_logits = logits.exp()
353+
exp_logits = logits.exp_()
352354
sum_exp_logits = exp_logits.sum(dim=-1)
353355

354356
# One all-reduce for both, as Megatron batches them.
@@ -364,8 +366,8 @@ def forward(ctx, logits: torch.Tensor, target: torch.Tensor, group: dist.Process
364366
def backward(ctx, grad_output: torch.Tensor):
365367
softmax, target_mask, masked_target = ctx.saved_tensors
366368
# d(loss)/d(logits) = softmax - one_hot(target), the one-hot only on the rank owning the target.
367-
grad_input = softmax.scatter_add(-1, masked_target.unsqueeze(-1), -(~target_mask).unsqueeze(-1).float())
368-
return (grad_input * grad_output.unsqueeze(-1)).to(ctx.input_dtype), None, None
369+
grad_input = softmax.scatter_add_(-1, masked_target.unsqueeze(-1), -(~target_mask).unsqueeze(-1).float())
370+
return grad_input.mul_(grad_output.unsqueeze(-1)).to(ctx.input_dtype), None, None
369371

370372

371373
def vocab_parallel_cross_entropy(logits: torch.Tensor, target: torch.Tensor, group: dist.ProcessGroup | None):

0 commit comments

Comments
 (0)