Quantization and digit recall · Phases 1–2

Qwen 3.5 Digit Recall

47 builds of Qwen 3.5 4B and 9B, from BF16 down to 2.9 bits per weight, tested on reciting π from memory, copying digits from the prompt, and choosing between the two.

Kiryl Dubarenka · llama.cpp bed0a8566 · SYCL · Arc Pro B60 · greedy decoding · 5,070 scored requests + 188 teacher-forced passes

Verdicts

Hypothesis4B9BDeciding evidence
H1 · Recall holds to ~4 bits, then falls off a cliffRejectedCloser4B π recall declines from 6.6 bits per weight; the 9B holds 1,000+ digits to 5.8 and halves between 5.0 and 4.1
H2 · Errors start where margins are thinnestConfirmedConfirmedRecall stops sit at the 14th (4B) and 6th (9B) percentile of BF16's margins
H3 · Copying is robust; failures are skips in runsConfirmedConfirmedVerbatim copy near-perfect to 4.3 bpw (4B) and 3.4 bpw (9B); errors are deletions inside runs, even at BF16
H4 · Scheme matters at equal sizeConfirmedConfirmedK beats Q4_0; unsloth's recipe beats stock builds; a digit-free imatrix doesn't protect π
H5 · Bigger models hold out to fewer bitsPer bit, not per byteThe 9B's cliff is 1.7 bits lower, but at equal file size the 4B at 8 bits recalls more
H6 · Low bits revert to memory over the promptReversedNo conflictLow-bit 4B builds copy a planted digit more faithfully; the 9B copies it at every working build
Q8_0 is freeSame answers as BF16 on every task at both sizes, 1.7–1.9× faster decode, about half the VRAM.
Exact recall wants many bits4B: stay at Q6_K or above. 9B: Q5_K_M keeps 1,000+ digits of π, and Q4_K_M keeps 700–790.
Per gigabyte, small and 8-bit winsAt ~4.5 GB the 4B at Q8_0 recites 1,000 digits; the 9B at Q3_K_M recites 129.
Copying survives to Q3And to Q2_K with an imatrix, at both sizes. Plain Q2_K breaks at both.
Pick unsloth over stockSame type, same size, less noise on π; on the 9B, 10× less generic noise at Q4_K_M.
Avoid IQ types on SYCLIQ3_XXS decodes slower than BF16 on both models.
H5 · model size

The 9B has 1.7 bits more to spare

The same plain ladder, built the same way, on both models. The cliff is the highest-bit build whose π recall falls below half the reference: Q5_K_M (5.9 bits per weight) on the 4B, Q3_K_M (4.1) on the 9B.

Four panels comparing the 4B and 9B: π digits against bits per weight and against file size, and KL on π and on random digits against bits per weight.
Blue: 4B. Orange: 9B. Lines join the plain ladder, diamonds are unsloth's files. Top right: the same recall plotted against file size, where the order reverses.
Plain ladder4B π digits4B KL on π9B π digits9B KL on π
BF16 (reference)1000011800
Q8_010000.0001312000.000007
Q6_K8130.001110560.00010
Q5_K_M4280.007510900.00039
Q4_K_M1230.0397880.0058
Q3_K_M880.351290.13
Q2_K02.3002.39
Phase 2 · Qwen 3.5 9B

The 9B: no knife-edges, sturdier copying

19 builds: unsloth's BF16 reference and 12 published files, our plain ladder from Q6_K to Q2_K, and Q2_K with our imatrix.

Correct digits of π against bits per weight for the 19 9B variants.
9B: digits of π recited before the first error, median of 3 phrasings (whiskers min–max).
9B BF16 margin along the 1000 digits of π, and for each variant the positions where its teacher-forced top-1 digit is wrong.
9B, top: BF16 margin along π. Bottom: a dot wherever a variant's top-1 digit is wrong under teacher forcing. Builds down to Q5_K_M make no errors at all.
9B variantFile GBVRAM GiBDecode tok/sPrompt tok/sπ digits
BF16 (reference)17.9215.7225931180
unsloth Q8_09.538.7421,9641200
Q6_K7.366.9501,9871056
Q5_K_M6.476.2532,0671090
Q4_K_M5.635.5601,821788
unsloth IQ4_XS5.175.1381,318576
Q3_K_M4.624.7571,461129
unsloth UD-IQ3_XXS4.024.120982129
Q2_K3.834.1611,6690

VRAM sits below file size + 770 MiB on the 9B. Its token-embedding table is separate from the output layer, and llama.cpp keeps it in host memory. Q4_K_M fits an 8 GB card with an 8k context. Decode flattens below about 5 bits, as on the 4B, at 1.5–1.7× slower. The full 9B write-up is in REPORT_9B.md.

Phase 1 · 4B · H1 · free recall

Recall declines early and steadily

The sections below are the 4B in detail. BF16 recites 1,000 correct digits of π (and stops there because the prompt asks for 1,000), 192 of e and 105 of √2. π has the runway, so it carries H1. Every stock quant below Q8_0 loses digits, and there is no plateau down to 4 bits: recall falls below half the reference at Q5_K_M (5.9 bits per weight).

Correct digits of π against bits per weight for all 28 variants; the stock ladder falls from 1000 at Q8_0 to 813, 428, 123, 88 and 0.
Digits of π recited before the first error, median of 3 phrasings (whiskers min–max). Dashed line: BF16 reference. The line joins the stock ladder built without an imatrix.
Stock ladderBits/weightπ digitsKL on πKL on random digits
BF16 (reference)16.0100000
Q8_08.510000.000130.00008
Q6_K6.68130.00110.0012
Q5_K_M5.94280.00750.0012
Q4_K_M5.21230.0390.0033
Q3_K_M4.3880.350.0059
Q2_K3.602.300.045

Memorized digits are far more fragile than ordinary digit prediction. At Q4_K_M the noise on π is 12× the noise on random digits, and it grows 7.6× per bit removed against 3.2× for random digits. Bits per weight are measured from each file; they run 0.3–0.4 above the usual labels because this model's large vocabulary layers stay at higher precision.

Phase 1 · 4B · H2 · teacher-forced margins

Errors start at a few knife-edge digits

Feeding the true digits and reading the logits gives each position a margin: how far the right digit leads its best competitor. BF16 gets all 1,000 positions right with a median margin of 9.2 nats, but a handful of digits are nearly tied. Those are where recall breaks first.

π decimals 320–332 · BF16 margin in nats
47.4320
813.1321
811.3322
19.5323
58.8324
28.6325
01.3326
911.2327
28.6328
08.6329
912.2330
68.8331
27.9332
π decimals 807–819
49.2807
510.0808
56.6809
311.8810
415.1811
68.8812
90.1813
06.0814
810.9815
34.0816
05.0817
28.4818
69.8819

Position n is the digit after n correct decimals, so a recall that stops at position 326 recited 326 digits.

BF16 margin along the 1000 digits of π, and for each variant the positions where its teacher-forced top-1 digit is wrong.
Top: BF16 margin along π. Bottom: one row per variant, a dot wherever its top-1 digit is wrong under teacher forcing. Dots appear first in the low-margin columns (326, 813) and fill in as bits drop.
Mean KL divergence from BF16 on the π text against bits per weight, on a log scale.
Noise added by quantization (mean KL divergence from BF16 on the π text, log scale). It rises smoothly; the steps in recall come from the digits, not from a threshold in bits.
Phase 1 · 4B · H3 · copying from the prompt

Copying holds; its failures are skips inside runs

Verbatim copy (E3). 40 random strings of 100 to 1,000 digits. BF16 makes 3 errors in 17,000 digits. Every build at 4.3 bits per weight or more copies 1,000-digit strings perfectly. Only three builds break, and the same quant types survive when built differently:

VariantBits/weightErrors / 100 digitsOn 1,000-digit stringsFailure
Q2_K, no imatrix3.65999Gives up; mostly deletions
IQ3_XXS, our imatrix3.69.036Drops whole chunks
IQ2_XS, our imatrix3.11239Mixed
Q2_K + our imatrix3.60.060Works
unsloth UD-IQ3_XXS3.70.030Works

Spelling as words (E4b). Even BF16 drops digits inside runs: run-heavy strings get 2.2 errors per 100 digits against 0.3 on plain random ones, and 75 of BF16's 80 errors are deletions inside runs of 3 or more equal digits ("eight eight eight eight eight", written one short). That stays unchanged down to about 4.3 bits per weight. Below it, failures turn into insertions, with the model looping on a word. Substitutions stay rare except in builds that collapse.

Spell-as-words errors per variant, split into substitutions, insertions and deletions.
Spell-as-words errors per variant, stacked by type. Low-bit builds fail by repeating words (insertions); plain Q2_K stops writing (deletions).

The original E4 prompt ("write each digit as an English word") failed as a test: the model read it as "say this number" and answered "twenty trillion two hundred…", even at BF16. Those records are kept; E4b adds one example ("507 becomes: five zero seven") and replaces it in the results.

Phase 1 · 4B · H4 · scheme, imatrix, recipe

The recipe matters more than the calibration

Each quant type was built several ways: stock with no imatrix, with our digit-free imatrix, with unsloth's imatrix, and unsloth's published file.

For each quant type, π digits and KL on π for each build: stock, own imatrix, unsloth imatrix, unsloth file.
One row per quant type, one marker per build. Left: digits of π recited (dashed line, BF16). Right: noise on the π text, log scale.
Build of Q4_K_Mπ digitsKL on πKL on random
Stock, no imatrix1230.0390.0033
+ our digit-free imatrix1230.0490.0016
+ unsloth's imatrix2200.0410.0024
unsloth's file3260.0220.0019
unsloth UD-Q4_K_XL (+6% size)3260.0140.0013
Phase 1 · 4B · H6 · prompt vs memory

Low bits weaken memory's pull

E5 asks for a copy of π or e with one digit changed. Only π position 10 separates the variants: BF16 and almost every build at 4.6 bits per weight or more "correct" it to the memorized digit, while almost every build at 4.4 or below copies the prompt faithfully. This is the opposite of H6, and consistent with H1: quantization erodes memorized content, so it competes less with the prompt.

For each variant, how many of 8 planted-error sequences copied the prompt, wrote the memorized digit, or something else.
Outcome at the planted digit, 8 sequences per variant. Exceptions to the trend: our Q4_K_M_imat and unsloth Q4_0 copy at ~5 bpw; our IQ3_XXS and unsloth UD-IQ3_XXS revert; plain Q2_K fails to copy at all.
Phase 1 · 4B · Resources and speed

What it takes to run each build

One Arc Pro B60 (24 GB), llama.cpp SYCL, 8,192-token f16 KV cache. Decode speed is the median the server reported on the test requests (within 0–5% of llama-bench tg128). Prompt speed is llama-bench at 4,096 tokens, since SYCL's per-request overhead makes short-prompt timings misleading.

VariantFile GBVRAM GiBDecode tok/sPrompt tok/sPower W
BF16 (reference)8.428.6381,087120
Q8_04.484.9652,964127
Q6_K3.464.0762,984147
Q5_K_M3.073.6813,092142
Q4_K_M2.713.3892,807138
unsloth UD-Q4_K_XL2.913.5782,838132
IQ4_XS2.513.1652,223130
Q3_K_M2.262.9852,332139
unsloth UD-IQ3_XXS1.952.6351,674113
Q2_K1.922.5912,617136
unsloth UD-IQ2_XXS1.522.2531,693129
Digits of π and KL on π against VRAM used, for all variants.
Quality against the VRAM each build needs. Left: digits of π. Right: noise on π, log scale.
Decode and prompt processing speed against bits per weight.
Decode during the tests (left) and prompt processing at 4,096 tokens (right).
Method notes

Problems found along the way

IssueHandling
E4 prompt read as "say this number"Kept as recorded; E4b with an explicit example rerun on all 28 builds
E6 (recall π as words): even BF16 counts "zero one two three…"Reported as a capability floor for the 4B
"e to 1000 decimal places: 2." answered with 1,200 zeros at BF16Real model behaviour; results reported per constant, π carries H1
Server host-RAM prompt cacheDisabled and the whole study rerun
Peak VRAM caught ~750 MiB spikes while a model loadsVRAM is reported as the steady median during the tests, for both models
E6 at the floor for the 9B tooReported; candidate to drop

Determinism checks passed on BF16 and Q8_0: identical bytes on repeated requests, after a server restart, and across both GPUs. Our Q8_0 and unsloth's Q8_0 agree to KL 0.00013, so the pipeline adds no error of its own.


Next