← ContentsChapter 3 of 7
Chapter 03

Importance matrices and codebooks

Two ideas that make everything below 4 bits possible: measure which weights matter, and stop quantizing them one at a time.

Minimise output error, not weight error

Every scheme so far has silently optimised the same thing: Σ (w − ŵ)². Make the weights close to the originals. That treats all weights as equally consequential — and they are emphatically not.

What matters is what the layer outputs. For a linear layer y = Wx:

Δy = (W − Ŵ) x

// for one output neuron:
Δy_k = Σ_i (w_ki − ŵ_ki) · x_i

// take the expectation over real input data:
E[Δy_k²] ≈ Σ_i (w_ki − ŵ_ki)² · E[x_i²]the importance matrix

There it is. A weight's contribution to output error is scaled by the mean squared magnitude of the activation it multiplies. A weight in a column that always sees near-zero activations can be mangled freely. One that sees huge activations must be preserved.

llama.cpp's llama-imatrix does exactly this: run a calibration corpus through the full-precision model, accumulate E[xᵢ²] for every input channel of every linear layer, write it to a file. The quantizer then minimises the weighted objective:

minimise  Σ imp_i · (w_i − ŵ_i)²   instead of   Σ (w_i − ŵ_i)²

which changes both the chosen block scale and, for codebook formats, which codeword each group gets.

Same weights, two objectives
where the scale lands
unweighted grid weighted grid bar height = weight, opacity = importance
Plain MSE
Weighted
Output error ↓

The calibration data is part of the model now

This is the uncomfortable consequence. The imatrix depends on what you fed the model. Calibrate on Wikipedia and you've optimised for Wikipedia-shaped activations. Then benchmark on Wikipedia perplexity and — surprise — you look excellent. Unsloth flags this directly: quantizers calibrated on Wikipedia-derived data naturally score better on KL-divergence benchmarks that also use Wikipedia.

There's a sharper trap: instruct models have chat templates. A base-model text corpus never emits <|im_start|> or the special role tokens, so the activation statistics around those tokens — the exact tokens gating every conversational turn — come from data that doesn't contain them. Unsloth's calibration set is deliberately built from chat- and code-shaped data, 1.5M+ tokens, hand-curated, applied per model.

Practical consequence. Two files both labelled IQ2_XXS, same size, same format, can differ substantially in quality depending on whose corpus produced the importance matrix. The format name tells you the bit budget; it doesn't tell you how well the bits were spent.

Below three bits, scalar rounding stops working

Do the arithmetic on a 2-bit scalar grid. Four levels. Per 32-weight block you still need a scale, so you're at 2.5 bpw before doing anything smart, and reconstructing a Gaussian onto 4 levels gives a signal-to-noise ratio around 14 dB. Quality falls off a cliff.

The escape is vector quantization. Stop quantizing weights one at a time. Take groups of 8 weights, treat each group as a point in 8-dimensional space, and store the index of the nearest entry in a shared codebook.

This wins for two reasons, both easier to see in 2D than 8D:

Scalar grid vs. learned codebook
2 bits/weight · 16 codes for 2 weights
weight pairs scalar 4×4 grid Lloyd-optimised centroids
Mean distortion
vs scalar
Storage
4 bits / pair

Same bit budget. Same number of codes. Strictly lower error, just from letting the codes move. In 8 dimensions the gap is much larger — this is Shannon's sphere-packing gain, and it grows with dimension.

How llama.cpp's I-quants use this

The IQ family are codebook formats. Roughly:

This is why the format table marks IQ quants as requiring an imatrix. There's no sensible way to pick "nearest codeword" without knowing which coordinates matter — unweighted nearest-neighbour in 8D at 2 bits produces garbage. The codebook and the calibration statistics are a package deal.

1-bit, 1.58-bit, and what those names mean

NameReal bpwWhat it is
Binary {−1,+1}1.0Theoretical. Destroys post-hoc quantized models — cannot represent zero.
Ternary {−1,0,+1}log₂3 = 1.585Where the "1.58-bit" name comes from: three states, 1.585 bits of entropy each. Zero is representable, which matters enormously.
IQ1_S (GGUF)1.5625Codebook + superblock scale, imatrix-driven. Post-training.
BitNet b1.58~1.58Not post-training — the model is trained ternary from scratch.
The critical distinction. BitNet is quantization-aware training: the network learns weights that are already ternary, so there's no approximation error at all. IQ1_S is post-training compression of a model trained in BF16 that never agreed to be ternary. Both land near 1.58 bpw; only one is lossless with respect to its own training. The next chapter is about the second, harder case.