Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

glm53-flash-mlx

MLX (Apple Silicon) runtime and quantization tooling for zai-org/GLM-5.3-Flash — 320B-A18B: 34 Kimi-Delta linear-attention layers interleaved with 11 DeepSeek-sparse-attention layers (NoPE MLA behind a lightning indexer), 288-expert MoE, manifold-constrained hyper-connections (model_type: glm5_next).

Published builds: pipenetwork/GLM-5.3-Flash MLX (8-bit, 6-bit, mixed 4/8-bit, 4-bit; see Measurements).

Why this exists

mlx-vlm merged glm5_next on 2026年08月26日 (PR #2030); no release carries it. Reviewing that port line by line against transformers 5.16, and then checking it numerically at tiny scale, found the hyper-connection, KDA, MLA and indexer math all correct — and two things wrong that fluent output would never reveal:

reference mlx-vlm main consequence
swiglu_limit every text MLP clamps gate ≤ 10, up ∈ [−10, 10] before silu(gate)·up (routed experts, shared expert, dense layers 0–2) inherits DeepSeek's unclamped SwitchGLU / DeepseekMLP; the config value is parsed and never read formula mismatch on all 45 FFN blocks
mHC base/scale dtype float32 (_keep_in_fp32_modules_strict) mlx_vlm.convert casts them to bf16; the Metal kernel then reads base through a float4 pointer the Sinkhorn comb matrix is off by ~0.5 on every layer of a converted checkpoint (verified at real dims: kernel vs ops agree to 1e-7 in fp32, differ by 0.53 with bf16 base)

Plus two epsilon mismatches (MLA low-rank norms use rms_norm_eps = 1e-5, not 1e-6; the indexer LayerNorm uses 1e-6, not 1e-5) and bf16 router logits where the reference uses float32.

glm53_flash_mlx/glm5_next/ is the mlx-vlm package with those fixed: a ClampedSwiGLU for the routed experts, shared expert and dense layers; a float32 router; the epsilons; a cast_predicate that protects the mHC arrays and KDA A_log/dt_bias, and a sanitize that heals checkpoints where they were already cast.

Validation

./scripts/run_tests.sh

A random tiny model with swiglu_limit low enough to actually clip, randomised mHC arrays and KDA decay parameters, a sequence longer than index_topk so the indexer selects, and more experts than top-k, checked against transformers:

[1] full forward (KDA x3 + DSA with live indexer, mHC, clamped MoE)
 logits max|delta| 9.239e-07 (scale 5.863e-01) OK
[2] short sequence (T <= index_topk: dense-MLA bypass)
 logits max|delta| 8.643e-07 OK
[3] sanitize is idempotent; bf16-cast hc/KDA scalars come back as float32 OK
[4] control: unclamped SwiGLU -> logits move by 5.865e-01 OK
[5] token-by-token decode == single forward
 incremental vs single-shot max|delta| 2.086e-07 OK
 chunked prefill 5+7 vs single-shot max|delta| 0.000e+00 OK

tests/debug_modules.py is the teacher-forced, module-by-module comparison that located the clamp: every other block matched to ~1e-7 before the fix.

Building the quants

python scripts/quantize_stream.py --src GLM-5.3-Flash-BF16-src --dst out/GLM-5.3-Flash-MLX-4bit --bits 4
python scripts/quantize_stream.py --src ... --dst out/...-mixed-4_8bit --bits 4 --other-bits 8

One decoder layer at a time: the release stores each layer's 288 experts as separate tensors and the runtime stacks them, so the natural streaming unit is the layer (14.5 GB of experts in bf16), gathered lazily from whichever shards hold it. The 643 GB source never needs to fit. The FP8 release would also work (the port dequantizes weight_scale_inv blocks); the bf16 release was used so nothing is lost before quantization.

Recipe: routed experts (304B, 97%) at --bits; everything else quantizable (~9B) at --other-bits — uniformly, because the runtime fuses the six KDA input projections into one matmul at load and they must share a width; the lightning-indexer projections always at 8-bit; the router, mHC arrays (fp32), KDA A_log/dt_bias (fp32), convolutions and norms as stored; the vision tower in bf16; the MTP layer dropped.

Measurements

scripts/ppl_corpus.py tokenizes wikitext-2 (test) once; scripts/ppl_large.py teacher-forces every build on the same 141 windows of 2048 tokens; scripts/ppl_compare.py does the paired bootstrap. The bf16 model does not fit a 512 GB machine, so 8-bit is the anchor.

build size perplexity ΔNLL/token vs 8-bit [95% CI] windows worse
8bit 334.1 GB 3.4607
6bit 255.9 GB 3.4646 +0.0011 [−0.0017, +0.0038] 89/141
mixed-4_8bit 181.9 GB 3.5705 +0.0312 [+0.0271, +0.0355] 131/141
4bit 177.6 GB 3.7549 +0.0816 [+0.0755, +0.0879] 140/141
REAP25-mixed-4_8bit 139.1 GB 4.2249 +0.1995 [+0.1657, +0.2377] 139/141
REAP37-mixed-4_8bit 118.3 GB 4.8752 +0.3427 [+0.2968, +0.3929] 141/141
REAP50-mixed-4_8bit 96.3 GB 6.0757 +0.5628 [+0.5071, +0.6219] 141/141
REAP25-4bit 134.7 GB 4.4361 +0.2483 [+0.2135, +0.2873] 141/141
REAP37-4bit 113.9 GB 5.1057 +0.3889 [+0.3424, +0.4393] 141/141
REAP50-4bit 91.9 GB 6.3840 +0.6123 [+0.5552, +0.6722] 141/141

REAP-pruned builds (scripts/reap_calibrate_full.py, scripts/prune_build.py): experts ranked by mean router_weight ×ばつ ‖expert_output‖ over 65,536 calibration tokens (wikitext-2 train, ten languages of Wikipedia, code; zero 32-gram overlap with the eval set), collected by running the full 8-bit build; two disjoint halves of the calibration set pick the same kept set 86.6% of the time at keep-50%. Pruning is applied to the already-quantized mixed 4/8-bit build. Cost on this model is steep and roughly linear in experts removed: REAP25 +18%, REAP37 +37%, REAP50 +70% perplexity over the unpruned mixed build — published because 96–139 GB is what fits a 128 GB Mac, with the numbers on the card. The uniform-4-bit prunes were built and measured too (4.4361 / 5.1057 / 6.3840) and are not published: the mixed prunes beat them at every ratio for ~4 GB more.

Layout

path what
glm53_flash_mlx/glm5_next/ the runtime (mlx-vlm glm5_next + fixes)
glm53_flash_mlx/load.py loader that replays per-module quantization from config.json
scripts/quantize_stream.py per-layer streaming quantizer
scripts/ppl_corpus.py, ppl_large.py, ppl_compare.py, ppl_table.py evaluation
scripts/smoke_generate.py greedy generation, collapse detector
scripts/upload.py, make_collection.py publishing, card rendered from the measurements
tests/test_parity.py, tests/debug_modules.py validation
docs/upstream-notes.md findings written up for mlx-vlm

About

MLX (Apple Silicon) runtime + quantization for GLM-5.3-Flash, validated against transformers

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /