Views
No views yet
mlx_lm convert -q quantization. It is a
QAT-lattice-aligned 4-bit conversion of
google/gemma-4-26B-A4B-it-qat-q4_0-unquantized
that recovers Google's original QAT scales from the weights instead of
re-deriving them from min/max statistics. Standard MLX affine layout
(group_size=32, 4-bit), loadable by stock mlx-lm — no patches needed.
52 GB → 15 GB (~5.0 bits/weight). Text-only (the vision tower is not included).qat-q4_0-unquantized checkpoints store weights already snapped to a
symmetric int4 lattice (codes ∈ [-8, 7], one scale per 32 input-dim elements).
For the MoE experts, dense MLPs, and the router, those scales are learned
during QAT and cannot be recovered from weight min/max statistics: only
26–38% of blocks touch an extreme code. Any quantizer that re-derives scales —
llama.cpp's naive Q4_0 (d = extreme/-8) and MLX's default affine mode
(scale = (max-min)/15, group_size 64) — re-snaps ~60–74% of MoE/MLP blocks
onto a misaligned grid. (This is the same effect Unsloth documented for
llama.cpp in their Gemma 4 QAT analysis.)absmax/k + least-squares refinement) and
emits it as standard MLX affine parameters (scale = s, bias = -8·s).
The MoE router (router.proj) is kept in bf16 (~20 MB): top-8-of-128 expert
selection is the most perturbation-sensitive spot, and llama.cpp MoE GGUFs
never quantize ffn_gate_inp either.default mlx_lm convert -q (affine, gs=64) | this conversion | |
|---|---|---|
| relRMSE | 7.0–8.6% | 0.18–0.23% (bf16 storage noise floor) |
| variant | mean KL | top-1 agreement |
|---|---|---|
default mlx_lm convert -q (gs=64) | 0.277 | 82.7% |
| MLX affine gs=32 | 0.353 | 80.1% |
| this conversion | 0.090 | 90.3% |
| control: bf16 + matched random noise σ=0.185% (no quantization) | 0.151 | 87.7% |
pip install mlx-lm1from mlx_lm import load, generate
2
3model, tokenizer = load("mlx-community/gemma-4-26B-A4B-it-qat-q4_0-mlx-aligned")
4prompt = tokenizer.apply_chat_template(
5 [{"role": "user", "content": "Explain quantization-aware training in two sentences."}],
6 add_generation_prompt=True,
7)
8print(generate(model, tokenizer, prompt=prompt, max_tokens=256))conversion/:qat_q4_recover.py — lattice-scale recovery quantizer
(recover_grid_step: per-32-block k-sweep with residual tolerance 0.075
to absorb bf16 rounding of code × scale products, then least-squares refit)convert_aligned.py — end-to-end converter producing this repo's format
(run against the qat-q4_0-unquantized checkpoint; --quantize-router
to also quantize the router)-8·scale here. Redundant but required by the
current kernels.