CERIN AMROTH · ML systems

How can I run a Mixture-of-Experts model larger than my GPU's VRAM?

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[train]"

Primary route: host-RAM training 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 (bitsandbytes 4-bit) · Requires: bitsandbytes>=0.43; torch>=2.2; transformers>=5.0 for the streaming loader ([train] extra)

This is the decision page: choose by workload and by the memory tier that ran out. Model-level integration — the loaders and the experts4bit-qlora engines that bind pinned host RAM or an NVMe arena to a real model — is offload-moe-experts-to-cpu-or-nvme.md; the arena bake, reader and tier primitives themselves are the kernel package's page, stream-moe-experts-from-host-or-nvme.md.

Start from what ran out. If the 4-bit experts exceed VRAM, load_moe_4bit_streaming(..., offload=True) homes them in pinned host RAM and streams one layer at a time; if the dense side does not fit, enable_dense_offload; if the experts do not fit host RAM either, an NVMe arena; if you have spare VRAM to trade at serve time, enable_pipelined_residency with hot sets chosen from a routing profile.

Four things to hold before choosing. Fitting is not a speed claim. Layer-granular host streaming is the capacity floor — it is what makes the model run at all. Profiled or paged residency is the serving-performance path. Absolute throughput is host-specific: only ratios travel between hosts (claim e4b.host.ratios-travel-absolutes-do-not).

First choose the workload

workloadstart withescalate to
QLoRA trainingpinned-host 4-bit expert streaming (offload=True; OFFLOAD_EXPERTS=1 in the trainer)NVMe training residency (enable_nvme_train_residency) when host RAM also runs out
Serving / generationstreaming, or profiled residency (hot_sets_from_profile + enable_pipelined_residency)the NVMe arena (enable_nvme_residency, enable_mxfp4_nvme_residency) when the expert store exceeds host RAM

What fits, from the register

Training and serving are different workloads measured on different hardware; the two examples below share nothing but the register, and neither implies a throughput.

VRAM overflow
  -> experts:       pinned-host expert streaming
  -> dense side:    dense offload
  -> spare VRAM:    profile-ranked hot residency
Host-RAM overflow
  -> expert store:  NVMe arena -> serving residency / training residency + checkpointing

Symptoms

Why it happens

A MoE's weights are mostly experts, and each token touches only its top-k of them, so the whole model never has to be resident: the dense side plus one layer's experts is enough for a forward, and at decode only the routed rows are needed. What ran out decides where the rest lives — pinned host RAM across PCIe, or an on-disk arena read at the device link — and frozen 4-bit storage keeps the bytes that move small.

Which project solves it

experts4bit-qlora decides which expert bytes are where: the streaming loader with offload=True, enable_dense_offload for the non-expert weights, hot_sets_from_profile for choosing resident experts, enable_pipelined_residency for the serve-time hot/cold split, and the enable_nvme_* engines. grouped-nf4-gemm (GitHub, PyPI) supplies the fused grouped GEMM the pipelined engine runs on and the arena reader and tier the NVMe engines bind to; [fast] is the seam.

what ran outcallneeds
the experts do not fit VRAMload_moe_4bit_streaming(..., offload=True) (OFFLOAD_EXPERTS=1 in the CLIs)[train]
the dense side does not fitenable_dense_offload(model, "cuda"); DenseDiskSource(path) when host RAM cannot hold it either
the experts do not fit host RAM, servingenable_nvme_residency(...) / enable_mxfp4_nvme_residency(...)[fast] + arena
the experts do not fit host RAM, trainingenable_nvme_train_residency(...)[fast] + arena + grad ckpt
serving, spare VRAM to tradeenable_pipelined_residency(model, hot_sets, k_slots=k)[fast]
small GPU, strong CPUenable_cold_engine(model, hot_sets, dequant="auto")

Install

pip install "experts4bit-qlora[train]"   # host-RAM training route: loader + pinned-host expert streaming; no kernel package needed
pip install "experts4bit-qlora[fast]"    # residency/NVMe/fast-kernel route: + grouped-nf4-gemm for the residency engines and NVMe arenas

Smallest correct example

Needs: GPU + network + model download.

import torch
from experts4bit_qlora import load_moe_4bit_streaming, verify_moe_4bit

model, config = load_moe_4bit_streaming(
    "Qwen/Qwen3-30B-A3B", "cuda", torch.bfloat16, r=8, alpha=16, quant_type="nf4",
    offload=True, pin=True, prefetch=True,   # prefetch: next layer's H2D copy overlaps this layer (no_grad only)
)
verify_moe_4bit(model, strict=True)
model.eval()
# do NOT call model.to("cuda"): the experts live in pinned host RAM by design

Or from the CLI: OFFLOAD_EXPERTS=1 BENCH_TOKENS=128 python -m experts4bit_qlora.infer. Dense side too large too? enable_dense_offload(model, "cuda") composes with it.

Expected result

verify_moe_4bit(model, strict=True) returns without raising, and torch.cuda.max_memory_allocated() during a forward stays near one layer's experts plus the dense side rather than the whole model. enable_dense_offload returns a non-empty list of per-layer handles, described by dense_offload_report(handles). Every enable_* returns a count or a non-empty handle list, or raises — assert it.

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/run-moe-larger-than-vram.md at commit 0c2a256dcdc2 (sha256 7cd2e077e6d40730…). 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