CERIN AMROTH · ML systems

How do I offload MoE experts to host RAM, or serve and train them from an NVMe arena?

Solved by experts4bit-qlora · this page in the repository (pinned 0c2a256dcdc2, the source of this rendering; latest on main, unpinned; not the source of any fact rendered here)

Install routes

From docs/capabilities.json at the pinned commit.

pip install "experts4bit-qlora[fast]"

Primary route: residency/NVMe/fast-kernel route.

Alternatives:

Environment (from the capability register): OS: Linux · Python: >=3.11 tested in CI (pyproject says >=3.9; 3.9/3.10 are not tested) · Accelerator: NVIDIA CUDA GPU, sm_80 or newer (Triton kernels via grouped-nf4-gemm) · Requires: an expert arena baked with grouped-nf4-gemm (nvme_bake_nf4 for NF4, the MXFP4 relocation bake for native MXFP4); grouped-nf4-gemm>=0.30.0 ([fast] extra; the floor is pyproject.toml's, validated by CI); local NVMe or a fast block device; pinned host RAM for the hot tier; transformers>=5.0 for the streaming loader ([train] extra); triton>=3.4 (Linux-only distribution)

This is the model-level integration page: the loaders and the experts4bit-qlora engines that bind pinned host RAM or an NVMe arena to a real model. Choosing a path by workload and by the memory tier that ran out is run-moe-larger-than-vram.md, the decision/router page; the arena bake, reader and tier primitives themselves are the kernel package's page, stream-moe-experts-from-host-or-nvme.md.

For host RAM, load_moe_4bit_streaming(..., offload=True) pins each layer's frozen 4-bit experts in CPU RAM and streams one layer to the GPU at a time. When the experts do not fit host RAM either, bake them into an arena with grouped-nf4-gemm and bind it with enable_nvme_residency or enable_mxfp4_nvme_residency (serving) or enable_nvme_train_residency (training): cold rows are read from NVMe on demand while a pinned-DRAM hot tier of hot_rows absorbs repeats.

Symptoms

Why it happens

Layer-granular offload bounds VRAM but not host RAM: its homes are the full [E, ...] packed stacks for every layer at once. Top-k routing touches a small fraction of the expert set per token, so the cold tail only has to be reachable, not resident. An arena is a baked, expert-row-addressable file; reads are O_DIRECT where the platform allows, so the page cache is neither needed nor helpful and host RAM does not have to exceed the arena (../RESIDENCY-ENGINES.md).

Which project solves it

grouped-nf4-gemm (GitHub, PyPI) owns the host/NVMe primitives: the bake (nvme_bake_nf4 re-quantises a bf16 or block-FP8 source to NF4; nvme_arena.bake_expert_tensors relocates native MXFP4 bytes verbatim), the reader, nvme_residency.ColdTier, and capacity_for_bytes for sizing hot_rows from measured free RAM. experts4bit-qlora owns binding the arena to a real model: engines/nvme_experts.py replaces the frozen module's forward for serving; engines/nvme_train.py leaves the adapter's forward alone and moves only the frozen base's home from pinned RAM to the arena. [fast] is the seam.

Install

pip install "experts4bit-qlora[fast]"    # residency/NVMe/fast-kernel route: grouped-nf4-gemm's arena bake, reader, tier and kernels
pip install "experts4bit-qlora[train]"   # host-RAM training route: the streaming loader and pinned-host expert offload

Smallest correct example

Needs: GPU + network + model download + a baked NF4 arena + local NVMe.

import torch
from experts4bit_qlora import (enable_fast_train, enable_nvme_train_residency,
                               load_moe_4bit_streaming, verify_moe_4bit)

ARENA = "/nvme/qwen3-30b.nf4.arena"      # baked by grouped-nf4-gemm's nvme_bake_nf4
model, cfg = load_moe_4bit_streaming(
    "Qwen/Qwen3-30B-A3B", "cuda", torch.bfloat16, r=8, alpha=16,
    quant_type="nf4", arena=ARENA, arena_train=True,   # arena_train=True is REQUIRED for training
)
verify_moe_4bit(model, strict=True)
n_experts = getattr(cfg, "num_local_experts", None) or cfg.num_experts   # the loader's own rule
n = enable_nvme_train_residency(model, ARENA, hot_rows=n_experts)        # floor: at least num_experts
assert n > 0
model.gradient_checkpointing_enable(gradient_checkpointing_kwargs={"use_reentrant": False})  # required
n_fast = enable_fast_train(model, dgrad=True)
assert n_fast > 0

Serving a native-MXFP4 arena (DeepSeek-V4) is the other side of the seam — mxfp4-moe-training-and-residency.md. Serving an NF4 arena uses enable_nvme_residency(model, ARENA, hot_sets, hot_rows=...) on a model loaded with arena=ARENA and no arena_train, with one hot_sets entry per MoE module from hot_sets_from_profile.

Expected result

enable_nvme_train_residency returns the number of ExpertsLoRA modules moved onto the arena (one per MoE layer); arena_train_stats(model) reports the tier afterwards. Undersized hot_rows is refused at attach, not many steps in. verify_moe_4bit(model, strict=True) still passes because the experts remain Experts4bit stacks. On the serving side, enable_nvme_residency returns the number of modules patched and refuses to stamp a partial set.

Supported scope

Limitations

Use this page when…

Evidence

Register: ../claims.json.

Common wrong approaches

Source and freshness

This page is a rendering of docs/solutions/offload-moe-experts-to-cpu-or-nvme.md at commit 0c2a256dcdc2 (sha256 444e8e76ab6c70ae…). Numbers are never copied here: every measured statement cites a claim ID in docs/claims.json at that commit. Repository-relative links resolve to this site's pages where the document is published and to the pinned commit otherwise.

CURRENT · source: pjordanandrsn/experts4bit-qlora@0c2a256dcdc2 · rendered package: 0.35.3 · latest published package: 0.35.3