Repository navigation
SFT learner fixes: training mode for gradient checkpointing, Ascend OOM detection, real-loss autobatch probe - #331
Merged
Merged
Conversation
michaellchung
force-pushed
the
sft-chunked-ce
branch
from
November 3, 2026 02:05
94414e6 to
848aaac
Compare
michaellchung
force-pushed
the
sft-night-fixes
branch
from
November 3, 2026 02:05
1ecb4af to
445e492
Compare
michaellchung
force-pushed
the
sft-chunked-ce
branch
2 times, most recently
from
November 3, 2026 04:09
f96f591 to
5b677b5
Compare
michaellchung
force-pushed
the
sft-night-fixes
branch
from
November 3, 2026 04:09
57fea72 to
3d1a644
Compare
michaellchung
force-pushed
the
sft-chunked-ce
branch
from
November 3, 2026 04:53
5b677b5 to
128e1ef
Compare
michaellchung
force-pushed
the
sft-night-fixes
branch
from
November 3, 2026 04:53
3d1a644 to
c85f814
Compare
`torch.npu` exports no `OutOfMemoryError`, so `oom_error(device)` fell back
to `torch.cuda.OutOfMemoryError` and `except oom_error(device)` never matched
Ascend's refusal, which arrives as a bare `RuntimeError("NPU out of memory.
Tried to allocate ...")`. Both memory probes -- the causal-LM auto-batch
resolver and the diffusion one -- were therefore not probes at all on Ascend:
the first size that did not fit killed the run instead of fixing the ceiling.
Observed on a 910B3 with Qwen3-0.6B at 8k tokens: `--micro-batch-size auto
--grad-accum 8` died in `_probe_once` with "Tried to allocate 37.09 GiB"
rather than settling on a smaller micro-batch.
`accel.is_oom(exc, device)` answers the question the probes actually have --
is this the allocator refusing an allocation -- across the vendor class,
torch's generic `OutOfMemoryError`, and the message-only form. The probes
re-raise anything else, so an aborted collective still fails loudly instead
of being mistaken for a memory ceiling. The existing test suite missed this
because its fake npu module defines an `OutOfMemoryError` that the real
torch_npu does not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_probe_once` approximated the native loss with `logits[:, :-1].float().sum()` on the stated assumption that "the shifted f32 logits copy dominates". It does not: `sft_loss` hands that copy to `F.cross_entropy`, which keeps its log_softmax output -- a second tensor of the same full (B, T-1, V) shape -- alive for the backward. The probe therefore reported a ceiling about one logits-sized f32 tensor too high. On a 910B3 with Qwen3-0.6B (151936-token vocabulary) at `--seq-len 8192 --grad-accum 8`, the probe admitted micro-batch 4 and the first real training step died asking for another 18.54 GiB. Running the real loss in the probe -- with all-ones weights, the worst case over packed blocks -- makes the probe answer the question it claims to answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The recipe line mapped every non-liger backend to torch-cross-entropy, so --kernel-backend chunked runs were archived as native-loss runs. Map each backend explicitly and reject unknown values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`from_pretrained` returns an eval-mode module and the learner never left it.
Transformers' GradientCheckpointingLayer only recomputes when `self.training`
is true, so `--gradient-checkpointing on` was accepted, logged, and silently
ineffective: every activation stayed alive for backward. Dropout, LoRA's
included, was inert for the same reason.
Measured on one Ascend 910B3 card with Qwen3-0.6B, LoRA all-linear, one
sequence per micro-batch, activation peak per sequence length:
tokens | eval mode | train mode
4096 | 21.5 GB | 9.3 GB
8192 | 33.9 GB | 9.6 GB
16384 | 59.0 GB | 10.1 GB
28000 | OOM | 10.9 GB
Which is the difference between OOM and training at long context: a 28k-token
SFT run on a 64 GB card is impossible in eval mode and comfortable in train
mode. The checkpointing setup moves into `enter_training_mode`, where the
ordering is stated once and covered by tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first logging window included the one-off first-step cost (streaming dataset build, compilation, collective setup). On a 2.9 GB dataset that reported 25 s/step for a run whose steady state is 4.75 s/step. Log the first step's duration on its own and start the first window after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
michaellchung
force-pushed
the
sft-night-fixes
branch
from
November 3, 2026 06:24
c85f814 to
0d8b68c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
from_pretrainedreturns an eval-mode module and the learner never calledmodel.train(). Transformers'GradientCheckpointingLayeronly recomputes whenself.trainingis true, so--gradient-checkpointing onwas accepted and logged but silently ineffective, and dropout (LoRA's included) was inert. Setup now lives inenter_training_mode, with the ordering covered by tests. On one Ascend 910B3 with Qwen3-0.6B LoRA, activation peak at 16k tokens drops from 59.0 GB to 10.1 GB, and 28k tokens goes from OOM to 10.9 GB.torch.npuexports noOutOfMemoryError, so Ascend's bareRuntimeError("NPU out of memory ...")escaped both memory probes (causal-LM auto-batch and diffusion).accel.is_oom(exc, device)matches the vendor class, torch's genericOutOfMemoryErrorand the message-only form; anything else is re-raised.sft_loss. The stand-inlogits[:, :-1].float().sum()ignored the log-softmax tensor thatF.cross_entropykeeps for backward, so the probe admitted a micro-batch about one f32 logits tensor too large. The probe now runs the native loss with all-ones weights.Tests
tests/test_learner_units.py,tests/test_accel.py,tests/test_autobatch.py,tests/test_chunked_ce.py.🤖 Generated with Claude Code