Experts trainability: bit-exact recompute-in-backward + ExpertsNbit (8/16-bit storage)#1
Merged
pjordanandrsn merged 2 commits intoJul 3, 2026
Conversation
There was a problem hiding this comment.
An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.
Once the cap resets or is raised, push a new commit or reopen this pull request to trigger a review.
…rage
Refactor the fused-MoE expert storage module into a generic ExpertsNbit
parametrized by quant_type, with Experts4bit kept as a fully back-compatible
fixed-4-bit subclass (same API, same state_dict layout, same tests):
- "nf4" / "fp4": the existing 4-bit blockwise path, unchanged.
- "int8" / "fp8": 8-bit blockwise via quantize_blockwise — one codebook
index per byte, 2x compression at much higher fidelity than 4-bit
("int8" = the blockwise dynamic map, not LLM.int8() vectorwise; "fp8" =
an e4m3 codebook via create_fp8_map).
- "bf16" / "fp16": unquantized passthrough storage (no codebook/absmax) as
a reference baseline and per-layer opt-out; skips the blocksize check.
Tests: new tests/test_experts_nbit.py (roundtrip tolerances per scheme,
int8-beats-nf4 fidelity ordering, forward/backward vs reference, strict
state_dict roundtrip incl. absent absmax keys for passthrough, frozen-base
LoRA training on int8, subclass quant_type restriction).
Also folds in a one-line ruff-format fix to the bitsandbytes-foundation#1849 regression test so
`pre-commit run --all-files` passes on the branch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g them Route every expert projection through _FrozenLinearRecomputeBackward, a small autograd Function whose forward computes dequantize + F.linear directly and whose backward re-dequantizes the frozen weight to form grad_output @ W: - Bit-exact by construction, in and out of grad mode, on every device: recomputation changes what is saved, never what is computed. (An earlier iteration routed through bnb.matmul_4bit instead; it was dropped because the fused gemm_4bit kernel it dispatches to for small token batches differs from dequantize+linear by accumulation order.) - The dequantized [out, in] expert weight never enters autograd's saved-tensor storage, so training activation memory stays independent of the number of experts held between forward and backward — and the mechanism is scheme-agnostic (nf4/fp4/int8/fp8/passthrough), no per-scheme kernel needed. - Cost: one extra dequantize per projection in backward; subsumed by full gradient checkpointing when that is enabled. Tests: test_experts4bit_forward_is_bit_exact_dequantize_linear pins forward == plain dequantize+linear at rtol=0/atol=0 (grad and no_grad); test_experts4bit_backward_saves_no_dequantized_weight uses saved_tensors_hooks to assert nothing weight-shaped (either orientation) is saved while a plain dequantize+linear control does save it, and that gradients match the control exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pjordanandrsn
force-pushed
the
feature/experts-4bit-training
branch
from
July 3, 2026 22:26
1a87db3 to
0315ebf
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.
Stages the precision-generalization + trainability work into
feature/experts-4bit(the bitsandbytes-foundation#1965 branch), as two commits.1.
feat:generalizeExperts4bit→ExpertsNbit(8-bit + 16-bit storage)One module parametrized by
quant_type;Experts4bitstays as a fully back-compatible fixed-4-bit subclass (same constructor,from_float, state_dict layout — all pre-existing tests pass unmodified).quant_typenf4/fp4int8quantize_blockwise; not LLM.int8() vectorwise (that scheme doesn't map onto the fused 3D stack — possible follow-up discussion)fp8create_fp8_map(True, 4, 3, 8))bf16/fp16Fidelity: int8 blockwise mean-abs reconstruction error is >4× lower than nf4 on the same weights — locked in by
test_expertsnbit_fidelity_orderingso a codebook/blocksize regression fails loudly. (This commit also folds in a one-line ruff-format fix to the base branch's bitsandbytes-foundation#1849 regression test; CI's format check fails without it.)2.
mem:re-dequantize frozen expert weights in backward instead of saving themEach expert projection goes through a small autograd
Function(_FrozenLinearRecomputeBackward) whose forward is dequantize +F.linearand whose backward re-dequantizes the frozen weight to computegrad_output @ W. Since the base is frozen, no weight gradient exists, so the dequantized[out, in]weight never enters autograd's saved-tensor storage — activation memory stays independent of how many experts sit between forward and backward.bnb.matmul_4bitand was rejected: the fusedgemm_4bitkernel it dispatches to for small token batches differs from dequantize+linear by accumulation order.)test_experts4bit_forward_is_bit_exact_dequantize_linearpins the equivalence atrtol=0/atol=0._dequantize_expert, so int8/fp8/passthrough get the same training-memory behavior — no per-scheme kernel needed.test_experts4bit_backward_saves_no_dequantized_weightlocks the memory contract viasaved_tensors_hooks: nothing weight-shaped (either orientation) is saved by the module, while a plain dequantize+linear control does save it — and gradients match that control exactly.Measurements (RTX A2000 12GB)
Matched A/B at OLMoE-1B-7B expert dims — 64 experts, hidden 2048, intermediate 1024, top-8, bf16, 512 tokens, a stack of L frozen
Experts4bitlayers with gradients flowing to the input activations (the QLoRA regime). The only difference between arms is_project: eager dequantize+linear (weight saved by autograd) vs the recompute Function.The eager arm's overhead is exactly the saved dequantized weights (~825 MB/layer at these dims) and grows linearly with depth; the Function arm's ~56 MB/layer is activations only. With ~6 GB free on the card, the eager arm OOMs at L=4 while the Function arm runs. Forward outputs were bit-identical between arms in every run. Step-time differences between the two mechanisms were within run-to-run noise on the (contended) bench host — repeated eager baselines of the same config varied 231–391 ms while the Function arm measured 389–470 ms, with the inference-forward comparison flipping sign between runs — so no reliable wall-clock penalty was resolvable; the memory numbers are allocator-exact and reproduced identically across runs.
Verification
tests/test_experts4bit.py+tests/test_experts_nbit.py: CPU 42/42 passed on Linux/Python 3.11 and Windows 11/Python 3.13 (both CPU-only torch, no native build); CUDA (RTX A2000, sm_86, self-built dev.so): 34/34-cudavariants pass, including the Failed to quant MoE models with fused expert weights in transformers v5 bitsandbytes-foundation/bitsandbytes#1849 regression/shape tests from this branch's base and the new bit-exactness/saved-tensors tests.pre-commit run --all-files: all hooks pass.🤖 Generated with Claude Code