Lab Note / Quantization & Serving

37,152 tensors, one flat landscape, one wrong index

A 320B model that does not fit on two DGX Sparks, a week spent squeezing it to 4 bits and measuring what that costs on every one of its 37,152 expert tensors, and a corruption bug that turned out to live in one line of vLLM rather than anywhere in the weights. Every number here traces to a project log; other people’s numbers are labeled as theirs.

2026-09-02 · Ramchand Kumaresan (Murai Labs)

Here is the problem in one number. GLM-5.3-Flash, the mixture-of-experts model Z.ai released on August 26 under MIT, has about 320 billion parameters (18B active per token). Stored the way it was trained, in 16-bit floats, the weights come to 598.5 GiB. A DGX Spark has 128 GB. Two of them have 256. The model does not fit, and it does not fit by a lot.

That gap is what quantization is for, and the idea is almost embarrassingly simple. A weight is a number like 0.0137, and you do not need all sixteen bits of it. Take a small group of weights, pick one scale for the group, divide each weight by that scale, round to the nearest point on a coarse grid, and store the grid index. At 4 bits the grid has sixteen points. Multiply back later and you get 0.0134 or 0.0141 instead of 0.0137, and the model mostly does not notice. Four bits per weight instead of sixteen turns 598 GiB into 181 GiB, and 181 GiB serves comfortably across two Sparks with room for a long context window.

The catch is the word mostly. A model squeezed too far does not crash. It keeps producing fluent, confident text while the meaning quietly comes unstuck from reality; the grammar survives longer than the sense. So the real question was never “can we make it fit” but “what did we lose, and how would we know?” This note is the answer we measured: the damage is spread flat across all 37,152 expert tensors, so protecting “fragile” experts buys nothing; a better scale search does 23% less damage but only helps behavior once one side effect is removed; and the worst thing happening to the output was not in the weights at all. It was one wrong index in vLLM, now reported upstream as vllm#54150.

Measurement conditions, stated once. Quantization from the BF16 master (598.5 GiB, 120 shards; verified bytewise to be the master, not an upcast of the FP8 release) with ModelOpt 0.45.0, CPU-only shard streaming, data-free. Serving on image radixark/vllm-glm53-flash:sm121-v8 (vLLM 0.1.dev20051+g487ecf187, FlashInfer 0.6.18.dev20260819), TP=2, marlin MoE backend, fp8_e4m3 KV cache (672,606-token pool), --max-model-len 262144, --block-size 2304, --gpu-memory-utilization 0.85, MTP speculative decode with num_speculative_tokens=4. Behavioral gates at temp 1.0 / top_p 0.95, n=2–3 per probe unless noted; the corruption probe at temp 0, non-streaming.

The sensitivity map: 37,152 instruments, one flat landscape

We did not invent the recipe we started from. LibertAI published it, and our baseline (P1) is an exact replica of their card: ModelOpt 0.45.0, weight-only NVFP4, blocks of sixteen, 4-bit E2M1 weights with an FP8 scale per block and one FP32 scale per tensor, no calibration data, weights streamed shard by shard through CPU memory. Only the routed-expert feed-forward weights get squeezed — 37,152 tensors, about 97% of the parameters. The other 1,618 tensors stay in BF16: attention, the shared experts, the routers, the embeddings, the output head, the norms, the MTP head, and the first dense layers. That matches LibertAI’s 43-entry ignore list exactly. The one thing we added was a tap: every tensor, as it was quantized, wrote down how far the round trip moved it — cosine, relative error, and what happened to its scales — to a JSONL file.

Everyone who quantizes a mixture-of-experts model has the same instinct, and we had it too: some experts must be fragile, some layers must be sensitive, and the clever recipe keeps those in higher precision. We expected the tap to tell us which tensors to protect. It told us not to bother.

If the scale-and-grid mechanics behind those block scales are new to you — per-tensor versus per-channel versus group-wise scales, and why the bucket size is a tradeoff rather than a setting — Chapter 27 of Under The Hood builds them from scratch and is free to read.

Per-layer quantization fidelity across the 43 quantized layers under the stock recipe. Top panel: mean and minimum per-tensor cosine by layer with a fitted trend over layers 3 to 44 whose slope is 9.1e-7 per layer. Bottom panel: mean rel-squared by layer, holding between 0.83 and 0.85 percent for layers 3 to 44 and stepping to 0.90 percent at the MTP layer 45.
Figure 1. Per-layer round-trip fidelity for all 37,152 quantized tensors, stock recipe (P1), measured against the BF16 master. Layers 3–44 sit in a 0.832–0.853% rel² band; the fitted slope over those layers is +9.1e-7 cosine per layer. Layer 45 (MTP) is a uniform level shift, not a tail.

The error landscape is flat. Not roughly flat — flat in a way that made us re-run the analysis looking for a bug. What the map does not show:

Cumulative share of total rel-squared versus the fraction of tensors excluded, over all 37,152 tensors from the stock-recipe instrumentation. The observed curve lies on the y equals x diagonal of a uniform error distribution; the annotation marks the worst one percent of tensors at 1.06 percent of total error.
Figure 2. Error mass accumulates almost linearly in the number of exempted tensors — there is no tail worth exempting. Computed from the P1 instrumentation over all 37,152 tensors; the same statistic on the shipped P3 checkpoint is 1.10%.

One genuine structural signal survived: MTP layer 45 is a uniform mild outlier (1.057× mean error, a level shift with no tail), and all 245 scale clamps in the model live there. Whether layer 45 deserves BF16 experts is a functional question (MTP acceptance), not a weight-space one. On our serving stack MTP acceptance measured 51.6% of draft tokens, accept length 2.07/4 (cumulative /metrics over the gate runs), with layer-45 experts quantized and the head in BF16.

A footnote that cost us an afternoon. LibertAI’s card reports round-trip rel err 0.0925 and cosine 0.99665. Our replica matched their error (0.0921). But those two numbers cannot both be per-tensor averages: for this statistic rel² ≈ 2(1−cos), and 0.0925 implies cos ≈ 0.99575, not 0.99665. We spent that afternoon assuming our quantizer was wrong. It was not; their cosine was aggregated some other way. Our P1 pair (0.0921 / 0.99576) satisfies the identity.

The recipe decision: fix the quantizer, not the exemption list

If there is no tail to protect, the only place left to find error is in the quantizer itself. So we rewrote ModelOpt’s stock quantizer in plain code, checked it produced bit-identical output before changing anything, and tried levers on a stratified sample of 872 tensors: the worst 372, 400 at random, and 100 from layer 45.

variantmean rel²Δ vs stockcost
stock (ModelOpt 0.45.0)0.8755%
s46 (4/6 adaptive block scaling, arXiv:2512.02010, paper-exact)0.7238%−17.3%
mse (per-block scale search, 9-point grid 0.70–1.75)0.6659%−23.9%8.7×
s46_mse0.6685%−23.6%8.7×

Table 1. Lever bake-off on the n=872 stratified sample, all variants data-free and CPU-only. The per-block MSE scale search won on 872/872 tensors (Wilcoxon p ≈ 1e-144), tail included (worst tensor rel_l2 0.09562 → 0.08488).

The winner is boring and old: instead of setting each block’s scale from its largest value, try nine multipliers on that scale and keep whichever reconstructs the block best. We built the full checkpoint with it (P2) and the numbers were lovely: mean rel² 0.8487% → 0.6335% (−25.4%), with 37,152 of 37,152 tensors improved. Not most of them. All of them.

Then we ran the behavioral gates and the model was worse. The gates are a small battery of things we actually use this model for. One hands it a codebase and asks for every call site of a function across files, at 32k and again at 100k tokens of context; the stock checkpoint went 6/6 on both, and P2 went 0/4. A tool-call probe went 0/2. A quarter less weight error, and the model got worse at its job. We had written down before building P2 that this was the outcome we were most afraid of — that better weights would not mean better behavior — but predicting a failure is not the same as understanding it.

The explanation came from looking at what the scale search was doing to individual blocks. Multipliers below 1.0 shrink a block’s scale, which makes the grid finer for the bulk of its values (hence the lower error) but leaves the block’s largest weights off the grid, clipped. On this model the clipping was diffuse — ~4% of L2 mass at ≤20% depth — but present on 8.8–20.5% of blocks in every tensor. A little deep clipping, everywhere. The objective was delighted to trade a few large weights for many precise small ones. The model was not. So P3 forbids multipliers below 1.0 (mse_ge1): no clipping is possible by construction, at a priced cost of ~2.5 points of the gain. This was the experiment we had pre-registered to decide the question: if the gates came back, clipping was the cause.

checkpointmean rel²mean rel_l2mean cosnotes
Murai-P1 (stock replication)0.849%0.09210.99576matches LibertAI’s published rel err
Murai-P2 (MSE scale search)0.634%0.07960.996829−25.4% rel²; gates regressed
Murai-P3 (mse_ge1 — shipped)0.655%0.08090.996727zero deep clipping; 245 clamps, 0 zeroed blocks

Table 2. Full-model weight-space aggregates, all 37,152 quantized tensors, round-trip vs the BF16 master. P3 keeps nearly all of P2’s gain with zero deep clipping. The deep-clipping hypothesis was supported when lc-01 recovered 0/4 → 2/3 + 2/3 and tc-10 0/2 → 1/3 on the gates.

The corruption bug: one global scale for a two-scale GEMM

If you read one section of this note, read this one. Serve this family of checkpoints on an unpatched vLLM and the model will, now and then, emit U+FFFD — the Unicode replacement character, the little diamond with a question mark in it — and tool calls come back malformed. It happens mostly in Korean, Tamil, Japanese, anything that takes more than one byte per character, and almost never in English, which is exactly why an English smoke test passes. The server logs nothing. The probe that catches it is tonyd2wild’s, who first measured the pattern on this model family: 4/9/8 events across three passes on ModelOpt-packed checkpoints versus 0/0/0 on a compressed-tensors control. On our own P3, unpatched: 8 U+FFFD events across 6 passes (Korean tool-call probe 1/3/4; English code+JSON 0/0/0).

The cause is one line. Remember the two-level scales from the top of this note, one per block and one per tensor? Each expert has a “gate” projection and an “up” projection, and vLLM fuses the two into one matrix for speed. When ModelOptNvFP4FusedMoE.process_weights_after_loading (in vllm/model_executor/layers/quantization/modelopt.py, ~line 1527) repacks that fused matrix for the marlin GEMM, it grabs the global scale like this:

w13_weight_scale_2 = layer.w13_weight_scale_2[:, 0]

Index zero. The gate (w1) projection’s scale, applied to both halves. vLLM even knows this might be wrong — a warning_once fires when the scales differ (“w1_weight_scale_2 must match w3_weight_scale_2. Accuracy may be affected.”) — and then carries on. The up (w3) half is dequantized mis-scaled by ws2_gate / ws2_up, separately for every expert. Whether the two scales happen to match is a property of the checkpoint, not the code, and you can count it without touching a GPU:

checkpointproducergate/up expert pairspairs sharing one scale
Murai-P3 (ours)ModelOpt 0.45.0, per-tensor amax, W4A1612,384 (43 layers incl. MTP 45)30.9% — mismatch on 69.1%; ratio mean 1.1454, median 1.077, p90 1.286, max 10.0
RedHatAI/GLM-5.3-Flash-NVFP4llm-compressor (compressed-tensors, W4A4, group-16)12,096 (42 layers, no MTP)100.0% — shared by construction

Table 3. Gate/up global-scale census, measured with a plain-python safetensors header reader over both checkpoints; no GPU involved. Any ModelOpt NVFP4 checkpoint quantized with separate gate/up projections and per-tensor amax scaling hits this bug — including the LibertAI checkpoint.

Numeric proof, local (BF16 master + ModelOpt dequant, no serving), layer 10 expert 0 (gate/up scale ratio 1.23), up-proj round-trip rel_l2:

conditionup-proj rel_l2
correct dequant0.0929
single-gscale repack (unpatched vLLM behavior)0.2549
single-gscale + compensation (the fix)0.0929 — restored exactly

Table 4. The mis-scaling is worth ~2.7× the up-projection’s quantization error on this expert. Controls behaved as predicted: expert 2 (ratio 0.93): 0.1124 → 0.0924 compensated; expert 1 (ratio 1.0): unaffected.

The fix is a ~20-line hunk ahead of the repack: rescale the up half’s FP8 block scales by ws2_up / ws2_gate, clamped to the FP8-E4M3 max (448). It ships as patches/modelopt_gscale_fixed.py, bind-mounted over the image’s modelopt.py — no image rebuild. Boot-time stats on our checkpoint: compensation ratio mean 1.0197 / max 2.0000 per layer, clamps ≈ 0.03% of up-half scales. Two implementation notes from getting it right: the fused scale tensor is 3-D and TP-sharded, so the half-split must be rank-aware (our first version failed on exactly this), and requantizing both halves to max(ws2_gate, ws2_up) is an untested alternative.

Bar chart of U+FFFD events per probe pass on the P3 checkpoint. Stock vLLM: 8 events across 6 passes. With the gscale fix: 0 events across 6 passes. With the fix on the 46-pass battery of roughly 90,000 tokens: 0 events.
Figure 3. U+FFFD corruption on the ModelOpt marlin path before and after the gscale compensation, temp 0, non-streaming. Same checkpoint, same probe; only the repack numerics changed. Probe method after tonyd2wild.
measurementunpatchedpatched
U+FFFD events, Korean ×3 + English ×3 (temp 0)8 / 6 passes0 / 6 passes
Full battery: 46 passes / ~90k tokens0 U+FFFD, 0 surrogates, 0 control chars, 0 repetition loops

Table 5. The battery covered the original Korean and English probes at 10 passes each, eight additional script families (Japanese, Chinese, Tamil, Arabic, Russian, Hindi, emoji-heavy, code-switching) at 3 passes each, and two 8,000-token Korean long-form generations (2/2 clean). Post-fix outputs were verified non-vacuous — real structured Korean, coherence intact.

Zero is easy to fake with an empty string, so we read the outputs; the post-fix Korean is real, structured, coherent Korean. And because changing the numerics could have changed behavior, we re-ran the gates on the patched build against the same probes, budgets, and grader: lc-01-32k 2/3 → 3/3, lc-01-100k 2/3 → 1/3 (noise at temp 1.0 with three samples), tc-10 1/3 → 2/3, ce-01 0/2 → 0/2. Neutral to slightly better, with the corruption gone. The patch is on by default.

We reported the root cause, both proofs, and the fix upstream in vllm#54150, answering the reporter’s open question — damaged weights (A) versus a wrong vLLM ModelOpt path (B) — with (B). The original report was on SM120 (4× RTX PRO 6000, same vLLM dev commit, 86 U+FFFD/6 runs on the LibertAI checkpoint and 94/6 on dealignai’s, 0/6 on RedHatAI); our comment extends it to SM121. The mechanism is not GB10-specific — the mis-scaling happens at repack, before any kernel runs — but Blackwell marlin NVFP4 MoE is where this code path executes.

Ecosystem context

None of this happened alone, and the people whose work we leaned on deserve a straight account of how ours compares.

probe (max arm, temp 1.0)LibertAI NVFP4Murai-P2Murai-P3EXL3 TR3-4bpw
lc-01-32k (long-code)6/60/42/32/3
lc-01-100k (long-code)6/60/42/31/3
tc-10 (tool-call)pass0/21/32/3
ce-01 (code-exec)starved2/20/2 †0/2
dj-01 (diorama @64k)0/5only working scene ever rendered0/3 (1 unrenderable)0/3 (zero-content ×3)

Table 6. Behavioral gates head-to-head, same probes, budgets, and grader; n=2–3 per cell. † Under the hardened grader (zero-content-at-cap check), Murai-P3’s ce-01 cell revises from a historical 1/2 to 0/2 — the pass was a zero-content artifact. EXL3’s dj-01 failures were total reasoning starvation: >200k characters of reasoning, zero content, at both temp 1.0 and 0.7. EXL3’s real edges are operational (DFlash2 decode speed, 1M context, 1.75M-token KV pool), not output quality on this battery.

What we are taking away from the week

The map was worth building because it killed the plan. We instrumented 37,152 tensors expecting to find the ones to protect, and the answer was that there are none. That is a cheap thing to learn from a JSONL file and an expensive thing to learn by shipping a checkpoint with an exemption list that buys nothing.

P2 improved every tensor in the model and got worse at the job. The quantizer optimizes weight error because weight error is what it can compute, and that works right up until the lever you pull has a side effect the number cannot see. The behavioral gates are not a check you run after the quantization. They are the experiment.

And we spent most of the week inside the quantizer while the biggest quality problem on the whole path was [:, 0] in a function nobody here had read. The single most useful thing we did was run someone else’s probe against our own checkpoint and take the answer seriously when it came back dirty.

What you can take home today: a 181.3 GiB W4A16 checkpoint that serves TP=2 on 2× DGX Spark with the corruption fixed (0 U+FFFD across 46 passes / ~90k tokens where the unpatched stack emitted 8 in 6), weight-space error 22.8% below the stock recipe with zero deep clipping, MTP intact, and a regression probe in the repo so you can check all of it on your own stack in minutes.

What we are not claiming

This is not a benchmark suite. Gate cells are n=2–3 at temp 1.0 and single-cell differences are sampling noise.

Code execution is at risk on this checkpoint. ce-01-class code execution is 0/2 on P3 under the hardened grader; P2 passed 2/2 there.

Very long single-shot generation is unproven on P3. The only checkpoint that ever rendered the 64k diorama scene was P2, once.

Historical gate numbers predate the runtime hardening and cannot be retro-attributed.

We have no KLD measurement of our own. Every KLD number above is a third party’s, in their lane.

This W4A16 checkpoint carries no activation scales. It is validated only on the marlin MoE backend and will degenerate on the default flashinfer NVFP4 path. Do not drop --moe-backend marlin.

If you serve any ModelOpt-packed NVFP4 MoE checkpoint on vLLM — ours, LibertAI’s, anyone’s — run the probe. If it comes back dirty, the patch is 20 lines and the mechanism, the proof, and the upstream trail are all in the repo.

Artifacts

Weights

Hugging Face · 181.3 GiB, 120 shards

Patch, probe, launcher

GitHub · recipe and upstream writeups

Upstream report

vllm-project/vllm#54150