feat(model): Nemotron-H hybrid (Mamba2 + attention + MoE) on the CPU #86
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/nemotronh-cpu"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 4 of
docs/hybrid-state-cache.md:internal/model/nemotronhimplements NVIDIA's Nemotron-H (NemotronHForCausalLM), a Mamba2 / attention / MoE hybrid, on the CPU backend. It is validated against transformers, and the real Nemotron 3 Nano 30B-A3B NVFP4 checkpoint loads. The PR has two commits.1.
refactor(model): share the sigmoid MoE routerGLM's routing has four steps: sigmoid scores;
e_score_correction_biasused only to pick experts; group-limited top-k; the unbiased scores renormalized and scaled. It lived inside GLM's per-token MoE. Nemotron-H routes the same way, so the router moves tointernal/model/moeasmoe.SigmoidRouter, and GLM now calls it.routed_scaling_factornow means 1 (HF's default) where GLM would have multiplied every routed expert by 0. Real GLM configs set it, so their output is unchanged.2.
feat(model): Nemotron-H on the CPUEach layer is an RMSNorm plus one mixer, chosen by the layer pattern:
Min_proj, thenCausalConv1D,SSMScanandGatedRMSNorm, thenout_proj. The state lives in the engine's slots (#84), and the ops come from #85*E-Supporting changes:
quantformat.Loader.NVFP4Rowsloads a row range of a packed weight. The range carries its own codes and block scales plus the whole weight's global scale, and the split is exact. The checkpoint'sin_projemits[z | xBC | dt]in one row, and nothing downstream takes a strided view, so the weight is split by output block at load, asmistral4does for its fused projections. Full-precisionin_projweights, which the real checkpoint has in six excluded layers, are split the same way.configreads transformers 5'slayers_block_typelist, with both current and legacy names. When transformers re-saves a Nemotron-H config it dropshybrid_override_pattern; without the list, such a config would read as all attention, sizing KV for every layer and running no Mamba layers. If a config carries both and they disagree, it is an error.configalso reads the Nemotron-H fields the model needs:layer_norm_epsilon, the shared-expert width, activations and bias flags.Newrefuses variants it doesn't implement (moe_latent_size, other activations, projection biases) instead of running them wrong.cmd/gllm.Validation
testdata/generate_nemotronh.pybuilds a tiny hybrid (patternMEM*E-M, every layer kind) in transformers'NemotronHForCausalLMand writes it out under the real checkpoint's tensor names, as two fixtures:tinyin full precisiontiny-nvfp4in the modelopt NVFP4 dialect the real checkpoint ships. The experts and two Mamba layers' projections are quantized and a third is left full precision, as the real checkpoint excludes some. Its goldens are computed from the dequantized weights.These all match within 2e-4:
Reference quirk: the goldens run transformers' scan as a single chunk. Without
mamba_ssm, transformers falls back to a pure-torch chunked scan that is off at the first token of every later chunk. Against a plain sequential recurrence, a single chunk agrees to ~1e-8, while 8-token chunks are ~1e-3 off at exactly positions 8 and 16. gllm's scan is that sequential recurrence, and the real model runsmamba_ssm's kernels, not the fallback. The generator comment and a new AGENTS.md gotcha record this so a later fixture doesn't bake the fallback's error into its goldens.The generator also passes
use_cache=False, because transformers cannot build a cache for a pattern containing an MLP layer (KeyError: 'mlp').Real checkpoint:
gllm planon NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4 runs the real loader against the counting backend. It accepts every tensor name and shape and reports 20.0 GiB of resident weights plus 16 × 47.6 MiB of recurrent state. This needs no GPU.go build,go vetandgo test ./...pass. No CUDA code changed.Not in this PR
ErrNotImplementedon CUDA (phase 5), so serving there fails on the first forward with a 501.hf_quant_config.json, which nothing reads, sogllm planand status reportnone. This is unchanged here and already true of GLM.🤖 Generated with Claude Code
Automated review by pr-reviewer v0.52.3 | Safety Check | Mistral Small | tracking id
r-b9096f-e4650bThis is an AI-generated review and may contain mistakes.
Status: ❌ Failed
This review couldn't be completed: that model isn't loaded on the inference service right now, and no alternate model was able to review it either. Consider splitting this PR into smaller changes. Tracking id
r-b9096f-e4650b.Comment
@pr-reviewer-bot retryto try again.