Quant Menu II (v0.53.0)

The full advanced-quantization surface. v0.53.0 shipped the closed allowlists and validators; v0.53.1 lit up every writer — 3-stage llama.cpp imatrix pipeline, TorchAO PTQ export, and single-shot BNB-4bit merge. See v0.53.1 live writers for the live-wiring detail.

Unsloth Dynamic 2.0 GGUF ladder

14-entry closed allowlist:

UD-Q8_K_XL · UD-Q6_K_XL · UD-Q5_K_XL · UD-Q4_K_XL · UD-Q3_K_XL · UD-Q2_K_XL · UD-IQ4_XS · UD-IQ3_M · UD-IQ3_XXS · UD-IQ2_M · UD-IQ2_XS · UD-IQ2_XXS · UD-IQ1_M · UD-IQ1_S

validate_ud_gguf_format is case-insensitive with canonical normalisation.

bash
soup export --model ./output --format gguf-ud --gguf-flavour UD-Q4_K_XL --calibration-data calib.jsonl --output ./model.gguf

Every UD rung and the low-bit IQ rungs (IQ1_S, IQ1_M, IQ2_XXS, IQ2_XS, IQ2_S, IQ2_M, IQ3_XXS, IQ3_XS) need --calibration-data, because the quantizer builds an importance matrix from it; without it the export fails with flavour ... requires --calibration-data <jsonl>. The Apple/ARM flavours and the other four IQ rungs (IQ3_S, IQ3_M, IQ4_NL, IQ4_XS) do not.

IQ + Apple/ARM GGUF flavours

  • 12-entry IQ family (IQ1/2/3/4 — including IQ4_NL non-linear)
  • 10-entry Apple/ARM-friendly set (Q4_0_4_4, Q4_NL, Q5_K_M, etc.)

Both wrapped in MappingProxyType metadata.

KV cache types

yaml
training:
  kv_cache_type: q8_0   # q8_0 | bf16 | f16 are servable; fp8 is accepted here and refused at serve time

training.kv_cache_type is accepted by the schema, but nothing in v0.75.2 reads it. The cache type is chosen when you serve, with soup serve --model ./output --kv-cache-type q8_0 (q8_0 needs the hqq package; bf16 and f16 set the cache dtype).

FP8 is accepted by the schema and unreachable at serve time: the transformers backend has no fp8 KV-cache path and raises, and the vLLM / SGLang routing that would provide one is not wired, so it raises earlier still. The Hopper capability check only decides which of the two error messages you get; a Hopper card does not make it work today.

FP8 attention, NVFP4, native unsloth_bnb_4bit

Three separate configs, because the requirements in those comments are enforced at config load and no single config satisfies all three.

yaml
training:
  quantization_aware: fp8   # required, or fp8_attention is refused
  fp8_attention: true       # non-MLX only
yaml
training:
  nvfp4: true               # CUDA + text only; Blackwell, SM 10.0 or newer (runtime check)
yaml
backend: unsloth
training:
  quantization: 4bit
  unsloth_bnb_4bit: true    # both of the above are required

LF / Axolotl parity

yaml
task: ppo
training:
  quantization: 4bit
  bnb_4bit_use_double_quant: true   # explicit true requires quantization='4bit'
  quantize_reward_model: true       # PPO + reward_model tasks

quantize_ref_model is accepted by the schema (it requires a task that has a reference model) but nothing in v0.75.2 reads it, so it does not quantize the reference model: the DPO trainer builds no reference model and the PPO trainer passes ref_model=None, rather than loading a second model. quantize_reward_model is honoured by the PPO and reward-model trainers.

yaml
training:
  quantization: 8bit
  llm_int8: true                    # asserts quantization='8bit', so it cannot
                                    # share a config with the 4-bit block above

bnb_4bit_use_double_quant was read by nothing until v0.73.3. Every place Soup built a 4-bit config hardcoded double-quantization on, so setting the key to false changed your config fingerprint and changed nothing about the run. It is honoured now at the three call sites Soup owns: the resident loader, the layer-streaming path (which reads it once and hands the same value to both the sharder and the meta skeleton, so streamed-versus-resident bit-exactness cannot drift), and the 4-bit save path. Two things travel with that. The field is now tri-state: unset means the shipped default, which is double-quant on, so a config that never set it trains identically; only an explicit true triggers the "requires quantization: 4bit" refusal. And because the resolved config now carries the field, a previously-unset config shows a one-time fingerprint drift in soup ship --evidence provenance and soup lock check with the numerics unchanged. The Unsloth loader cannot honour it — FastLanguageModel builds its own quantization config internally with double-quant hardcoded on and exposes no override.

Advanced save formats

bash
soup merge --adapter ./output --save-format 4bit          # | 4bit_forced
soup merge --adapter ./output --save-format 4bit --no-double-quant   # v0.73.3; ignored for fp16
soup export --model ./output --format torchao --quant-config quant.yaml

save-format 4bit_forced writes a single BNB-4bit merged checkpoint without a dequant / merge / requant cycle.

--quant-config accepts a closed TorchAO allowlist: Int4WeightOnly / Int8DynActInt4 / Float8DynActFloat8 / NVFP4.

Stats

  • Net +157 tests (7,453 → 7,610 across 179 files)
  • 154 tests in test_v0530.py
  • 5 review agents ran in parallel; every CRITICAL / HIGH / MEDIUM / LOW finding fixed or documented

See also

Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.