GGUF quantizations of Kimi K3 (2.8T params / 104B active / 896 experts, top-16), both verified running on a single 8×H100 + 2TB RAM node, with a full same-methodology perplexity comparison.
Tier
Size
Experts
PPL
Shards
IQ1_S-XS
539.7 GiB
896
1.9193 ± 0.0469
34
IQ1_S-XXS-832e
503.3 GiB
832
1.9634 ± 0.0490
11
IQ1_S-XXS-832e is, as far as we know, the first K3 quantization to fit under 512 GiB while
remaining usable. It gets there by pruning 7.1% of the routed experts — the quality difference
against IQ1_S-XS is 0.65σ, not statistically significant. See "Getting under 512 GiB".
The main type follows the expert layers (IQ1_S), which hold 92.7% of the weight and therefore
determine both size and quality. The suffixes mark our own allocation variants:
-XS — non-expert layers pushed to IQ4_XS rather than left at Q8_0, trading speed for a
smaller file. Directly comparable to unsloth's UD-IQ1_S, which uses the same expert type with
a different non-expert allocation.
-XXS-832e — same bit-width allocation as -XS, plus 64 of the 896 routed experts removed
(832e = 832 experts remain). This is the only tier here that changes the parameter count
rather than just the bit-width.
What this repo provides
A same-methodology perplexity comparison (table below). As far as we know this is the first public release of K3 quantization tiers measured under identical conditions, usable for tier selection.
A sub-512 GiB tier that still works, with the measurement to back it up rather than a size claim alone.
A different bit-width allocation: experts at IQ1_S, router kept at F32 (unquantized), attention and KDA at IQ4_XS, shared experts at Q5_K.
Full reproduction path and measured data: build commands, quantization parameters, pruning method, calibration corpus mix, single-node run configuration and measured throughput.
Perplexity comparison
Unified methodology: wikitext-2-raw/wiki.test.raw, 12 chunks, n_ctx=512, --n-cpu-moe 93, same machine and same llama.cpp build.
Quant
Size
Experts
PPL
vs Q8
UD-Q8_K_XL (lossless reference)
1453.9 GiB
896
1.3453 ± 0.0420
—
UD-IQ1_S
553.2 GiB
896
1.8824 ± 0.0446
+0.5371
IQ1_S-XS (this repo)
539.7 GiB
896
1.9193 ± 0.0469
+0.5740
IQ1_S-XXS-832e (this repo)
503.3 GiB
832
1.9634 ± 0.0490
+0.6181
Three caveats:
All four rows were measured by us under identical conditions and are only comparable to each other. PPL figures published elsewhere for K3 quants generally omit the measurement setup (corpus, chunk count, n_ctx) and cannot be cross-compared with this table.
IQ1_S-XS and UD-IQ1_S have overlapping error bars, so the quality difference is not statistically significant; size is 2.4% smaller, generation speed 6.8 vs 11.5 tok/s.
IQ1_S-XXS-832e vs IQ1_S-XS: the difference is +0.0441 ± 0.0678, i.e. 0.65σ — well below the 1.96σ significance threshold, with heavily overlapping error bars. Removing those 64 experts bought 36.4 GiB at no measurable quality cost.
Q8_K_XL works as a lossless reference because K3 ships natively in MXFP4 (QAT from the SFT stage onward); Q8_K_XL copies the MoE layers verbatim as MXFP4 and keeps everything else at BF16.
Bit-width allocation
Tensor
Type
Rationale
ffn_{gate,down,up}_exps
IQ1_S
92.7% of total size (1347 GiB / 2723B params)
ffn_gate_inp (router)
F32, unquantized
Determines expert selection; quantizing it causes routing errors
In the actual artifact, 10 layers of ffn_down_exps were automatically promoted to Q2_K by llama.cpp's built-in IQ1_S safeguards — this accounts for the 14 GiB overshoot against our budget.
Getting under 512 GiB
512 GiB is a meaningful threshold (it fits machines with 512 GB of RAM). Two findings, in order.
Pure quantization cannot get there
IQ1_S (1.5625 bpw) is the practical floor for post-training quantization of the expert layers in llama.cpp. Holding experts at IQ1_S and compressing the non-expert layers from Q8_0 down to IQ4_XS:
Configuration
Size
Experts IQ1_S + non-experts Q8_0
553.1 GiB
Experts IQ1_S + non-experts Q6_K
538.7 GiB
Experts IQ1_S + non-experts IQ4_XS
525.3 GiB (budgeted)
Compressing the non-expert layers saves at most ~28 GiB against a 41 GiB gap. Finer per-category allocation (separate bit-widths for attn / shexp / ssm) landed at 527–532 GiB instead, indicating IQ4_XS is already near the sensible floor for those layers.
Do not use Q1_0 (1.125 bpw) or Q2_0. We tried: size does drop to 466.7 GiB, but PPL explodes to 5×10⁵ and the model is unusable. Reading the implementation in ggml/src/ggml-quants.c explains why — quantize_row_q1_0_ref is pure sign quantization: it stores only the sign of each weight, with 128 elements sharing one scale (the block's mean absolute value), discarding all magnitude information. Q2_0 uses the block amax as its scale, so a single outlier flattens the whole block. Both types are designed for models trained natively at low bit-width (BitNet-style), not for post-training quantization.
Light expert pruning does — IQ1_S-XXS-832e
Crossing 512 GiB requires reducing the parameter count itself. K3's routing turns out to be extremely uneven: in layer 40 the coldest expert is routed 52 times against the hottest at 57,948 — a factor of 1114. That skew is what makes pruning cheap here.
Importance comes from the imatrix we already had. llama.cpp accumulates activation statistics per expert for MoE tensors (e.counts[ex]++ in tools/imatrix/imatrix.cpp), so every MoE layer in the imatrix file carries counts[896] (tokens routed to each expert) and in_sum2[896, N] (per-expert input activation energy). We score each expert by the total activation energy flowing through it:
ffn_down_exps is the right tensor to read: its input is the expert's intermediate activation, and it projects straight back into the residual stream, so its energy is a reasonable proxy for how strongly that expert contributes.
Experts pruned
Remaining
Energy lost (median)
Worst layer
Size
3.6%
864
0.211%
0.793%
523.8 GiB
5.4%
848
0.398%
1.362%
514.7 GiB
7.1%
832
0.625%
1.999%
503.3 GiB
10.7%
800
1.197%
3.442%
487.3 GiB
We picked 832 rather than the just-barely-passing 840, to leave headroom for llama.cpp's automatic Q2_K promotion of some ffn_down_exps layers (that rule is what pushed IQ1_S-XS 14 GiB over its budget).
Honest limitation of this scoring.Cerebras REAP scores experts by gate_weight × ||expert_output||. We do not have the gate weight term and substitute routing frequency, which is correlated but not equivalent. In exchange the whole thing runs in hours instead of days and needs no extra calibration pass. The justification is empirical, not theoretical: PPL moved 0.65σ, and generation quality held up on spot checks (Chinese reasoning, code). If you need the stronger criterion, REAP is the tool.
Five tensor families must be pruned together, per layer — missing any one produces a broken model:
Tensor
Shape
Note
ffn_{gate,up,down}_exps.weight
[…, 896]
expert weights, stacked on the last dim
ffn_gate_inp.weight
[7168, 896]
router projection
exp_probs_b.bias
[896]
router per-expert bias — easiest to overlook
plus the KV kimi-k3.expert_count. K3 sets no expert_group_count, so llama.cpp's n_expert % n_expert_groups == 0 assertion does not apply and any expert count is legal.
One convenience worth noting: pruning can be applied directly to the already-quantized GGUF, no requantization needed. IQ1_S blocks are 256 elements and the expert row lengths (3584 / 3072) are both divisible by 256, so a block never straddles an expert boundary — quantize-then-prune and prune-then-quantize are byte-identical.
Note the trailing dash in --include "*IQ1_S-XS-*" — without it the pattern also matches the
XXS files.
Point --model at shard 1 only; llama.cpp derives the remaining shards from its filename, so keep all shards in one directory under their published names.
--n-cpu-moe 93 places all MoE layers in CPU memory. Memory is the hard gate: you need roughly RAM+VRAM combined of 515 GB (XXS-832e) or 550 GB (XS). 8×H100 (640 GB HBM) cannot hold either alone and must be paired with large system RAM; our setup is 8×H100 + 2TB RAM, where mmap keeps resident memory at ~22 GB and the rest in page cache.
For vision, use llama-mtmd-cli with --mmproj Kimi-K3-mmproj-BF16.gguf.
Measured environment and speed
Hardware
8× H100 80GB (sm_90) + 2TB RAM + NVMe RAID0
Build
unsloth fork efc8bc38f, CUDA 12.6
Generation
6.8 tok/s (IQ1_S-XS)
Prompt
3.0 t/s
Load time
~4.5 min (first load, cold mmap)
Slower than UD-IQ1_S's 11.5 tok/s on the same machine, because non-expert layers use IQ4_XS rather than Q8_0 and carry higher dequantization cost — this is the price paid for the smaller size. IQ1_S-XXS-832e runs in the same range; pruning removes weight but does not change the per-token expert count (still top-16), so it is not proportionally faster.
Known issue: during warmup, _exps selection does not bypass top-k, so only 16 of 896 experts are loaded, making the first load slow (see the PR #26185 discussion). Work around it with --no-warmup.
Reproducing the quantization
Using unsloth's UD-Q8_K_XL (1.5 TiB, lossless) as the source:
bash
1# imatrix: 238 chunks on the 1.5TiB model takes ~11 hours2# Time estimate: chunks/4 × 708 seconds3./llama-imatrix -m Kimi-K3-UD-Q8_K_XL-00001-of-00034.gguf \4 -f calib_mix.txt -o imatrix_k3.gguf -ngl 99 --n-cpu-moe 93 -c 51256# Quantization: ~2h40m with 96 threads7# --allow-requantize is required: the expert layers in Q8_K_XL are MXFP4,8# and llama.cpp refuses to requantize from an already-quantized type by default9./llama-quantize --allow-requantize --imatrix imatrix_k3.gguf \10 --tensor-type ffn_gate_inp=f32 \11 --tensor-type attn_q=iq4_xs --tensor-type attn_k=iq4_xs \12 --tensor-type attn_v=iq4_xs --tensor-type attn_output=iq4_xs \13 --tensor-type attn_gate=iq4_xs --tensor-type attn_q_a=iq4_xs \14 --tensor-type attn_q_b=iq4_xs --tensor-type attn_kv_a=iq4_xs \15 --tensor-type attn_kv_b=iq4_xs \16 --tensor-type shexp=q5_K \17 --tensor-type ssm_g=iq4_xs --tensor-type ssm_f_a=iq4_xs \18 --tensor-type ssm_f_b=iq4_xs --tensor-type ssm_beta=iq4_xs \19 --tensor-type routed_down=iq4_xs --tensor-type routed_up=iq4_xs \20 --token-embedding-type q6_K --output-tensor-type q6_K --keep-split \21 Kimi-K3-UD-Q8_K_XL-00001-of-00034.gguf Kimi-K3-IQ1_S-XS.gguf iq1_s 96
Two easy mistakes: the global type argument does not automatically spare the non-expert layers, so you must write one --tensor-type per category for attn_* / shexp / ssm_*, or attention and shared experts get pushed to 1-bit along with the experts. And --keep-split appends its own -00001-of-000NN suffix, so do not include one in the output filename.
One more, easy to miss: general.quantized_by and general.repo_url are inherited from the source
GGUF and are not rewritten by llama-quantize, so a quant made from someone else's file will
carry their attribution. Likewise quantize.imatrix.file records the --imatrix path verbatim —
pass a bare filename from the working directory, or the absolute server path ends up published.
Check with gguf_dump.py --no-tensors before uploading.
Validate any plan change with --dry-run first — it computes the exact quant size in a few hundred milliseconds.
Reproducing the pruning (IQ1_S-XXS-832e)
Applied to the finished IQ1_S-XS GGUF, not to the 1.5 TiB source:
Read counts and in_sum2 per expert out of the imatrix (they are already there — see "Getting under 512 GiB"), and score each expert by total activation energy through ffn_down_exps.
Per layer, keep the top 832 by score, sorted ascending to preserve relative order.
Slice all five tensor families on the expert dimension. In gguf-py's numpy view the expert dimension is the first axis ((896, rows, row_bytes)), so this is plain data[keep].
Rewrite kimi-k3.expert_count to 832.
Two things to watch when rewriting a GGUF by hand:
The data section starts at the tensor-info end rounded up to general.alignment. Changing the KV section length moves that absolute position, so padding must be recomputed — copying the original padding silently misaligns every tensor offset.
GGUFWriter in split mode requires add_tensor_info for all tensors first, then write_header_to_file → write_kv_data_to_file → write_ti_data_to_file → write_tensor_data per tensor, in registration order. Skipping write_ti_data_to_file fails with Expected output file to contain tensor info or weights, got WriterState.KV_DATA.
Calibration corpus
Code 35% / English 30% / Chinese 25% / other 10%. The Chinese share is deliberately higher than the common setting (~15%): in a highly sparse MoE, content under-represented in the corpus gets silently sacrificed during calibration. Code is drawn from the llama.cpp source tree (mixed C/C++/CUDA/Python/CMake), English from wikitext-2, Chinese from wikimedia/wikipedia 20231101.zh. Corpus blocks are interleaved and shuffled rather than concatenated by category — imatrix accumulates activation statistics per chunk, and concatenation would concentrate each category into a few chunks, skewing the weight distribution.
The imatrix run reports 99.89% coverage: roughly 1 of 896 experts was never routed to within the 238 chunks. This is expected for top-16/896 sparsity; llama-quantize falls back to the default quantization for missing entries.
unsloth — UD-Q8_K_XL lossless source; full-size model fixes and the MoonViT-3d vision tower in the llama.cpp fork
pwilkin — Kimi-K3 architecture support in llama.cpp
License
Inherits the Kimi K3 License (near-MIT; explicitly permits modification, distribution, sublicensing and derivative works. Obligations apply only to MaaS businesses with over $20M annual revenue, and to products with over 100M MAU or $20M monthly revenue, which must display "Kimi K3" in their UI).