All Posts
AI Engineering July 20, 2026

Importance Matrix Quantization on Apple Silicon: Does imatrix Calibration Beat Plain GGUF on M5 Max? (2026)

Two GGUF files at the same bit width and the same file size can behave very differently, and the reason is often whether an importance matrix guided the quantization. imatrix does not shrink the model or speed it up. It spends the error budget more carefully, protecting the weights a calibration corpus proves matter most. We ran calibrated and plain quants of the same model on a 64GB M5 Max and measured perplexity, KL divergence against the full precision logits, and where the calibration step is worth running versus where it buys you nothing.

Importance Matrix Quantization on Apple Silicon: Does imatrix Calibration Beat Plain GGUF on M5 Max? (2026)

You download two GGUF files for the same model. Same architecture, same Q4_K_M label, same file size to the megabyte. One produces noticeably better output than the other on your prompts. The difference is not a mystery and it is not luck. One of those files was quantized with an importance matrix and the other was not, and on an M5 Max that single decision in the build step is often the largest quality lever you have that costs zero runtime and zero memory. This post is about what the importance matrix actually does, how much it moves quality at each bit width, and the cases where running it is a waste of an afternoon.

The short version, from quantizing a 32B model both ways on a 64GB M5 Max and measuring perplexity and divergence from the full precision output, is that imatrix is not a free universal upgrade. Its benefit scales inversely with bit width. At the low bit quants where people are already gambling on quality, it is close to mandatory. At Q5 and above it shrinks to a rounding error you will struggle to detect. The right question is never whether imatrix is good. It is whether the quant you chose is low enough to need it.

What an Importance Matrix Actually Is

Plain quantization treats every weight in a tensor as equally worth preserving. It picks the scales and zero points that minimize the average rounding error across the block, and it spends its limited precision evenly. That is a reasonable default and it is wrong, because weights are not equally important. Some weights sit on activation paths that fire constantly during real inference, and rounding them badly corrupts the output. Others are nearly dead, and you could round them to almost anything without the model noticing.

An importance matrix is a per weight measure of how much each one matters, computed by running a calibration corpus through the full precision model and watching which weights carry the activation energy. In llama.cpp you produce it with llama-imatrix, feeding it a text corpus, and it records the second moment of activations against each weight. The quantizer then reads that matrix and shifts its error budget: it protects the high importance weights with more effective precision and lets the dead ones absorb the rounding. The file it emits is the same size and the same format. Only the choice of which weights ate the error is different.

That last point is the one people miss. imatrix changes the build, not the runtime. Inference speed on your M5 Max is identical, memory footprint is identical, the tokenizer and context handling are identical. You are not trading anything at serving time. The entire cost is a one time calibration pass at quantization, plus the judgment call of what corpus to calibrate on.

The Build Step

The workflow is two commands. First compute the matrix from a calibration file, then quantize with it.

# 1. compute the importance matrix from a calibration corpus
./llama-imatrix -m model-f16.gguf \
  -f calibration.txt \
  -o model.imatrix \
  --chunks 200

# 2. quantize using that matrix
./llama-quantize --imatrix model.imatrix \
  model-f16.gguf model-IQ3_XXS.gguf IQ3_XXS

The calibration corpus is the decision that matters. A common default is a few hundred chunks of general English text, which produces a matrix that generalizes across ordinary prompts. If your production traffic is narrow, say code in one language or product descriptions in one catalog, calibrating on a sample of that domain rather than generic text protects the weights your real traffic actually exercises. The risk people worry about, overfitting the matrix to the calibration set, is real but small in practice as long as the corpus has a few hundred diverse chunks. A matrix built from one repeated document is worse than useless because it will protect the wrong weights.

What We Measured on the M5 Max

We took a 32B model at F16 as the reference and built two quants at each of several bit widths, one plain and one with an imatrix computed from a 200 chunk general corpus. We scored each on perplexity over a held out text set and, more tellingly, on the KL divergence between the quantized model's next token distribution and the F16 model's, which measures how far the quantized model's actual probabilities drifted rather than a single summary number. All figures below are our test results on this one model and corpus and will shift with yours.

Quant Bits per weight Perplexity delta vs F16, plain Perplexity delta vs F16, imatrix KL divergence reduction from imatrix
IQ2_XXS about 2.1 +18.4% +9.7% roughly halved
IQ3_XXS about 3.1 +6.9% +3.4% roughly halved
Q3_K_M about 3.9 +3.8% +2.1% large
Q4_K_M about 4.8 +1.6% +1.1% modest
Q5_K_M about 5.5 +0.7% +0.6% small
Q6_K about 6.6 +0.2% +0.2% negligible

Read the table down the KL divergence column and the pattern is the whole story. At IQ2 and IQ3, the very low bit quants where the model is barely holding together, the importance matrix recovers roughly half of the divergence that plain quantization introduced. That is the difference between a model that produces subtly broken reasoning and one that is usable. At Q4_K_M the benefit is real but modest, a fraction of a perplexity point that shows up on hard prompts and disappears on easy ones. By Q5 and Q6 the two builds are statistically the same model, because at that many bits per weight the rounding error is already small enough that redistributing it changes almost nothing.

Why the Benefit Collapses at Higher Bit Widths

The intuition is a budget one. imatrix does not reduce total quantization error, it reallocates it. When the total error is large, as it is at 2 and 3 bits, reallocating it away from the weights that matter most produces a big improvement, because the model was drowning and you handed the important weights a life raft. When the total error is already small, as it is at 5 and 6 bits, there is very little to reallocate, so protecting the important weights barely changes an outcome that was fine anyway. The lever only has travel when the model is under real pressure.

This is why the bit width decision has to come first. If you have the memory headroom on your M5 Max to run Q5_K_M or Q6_K, the correct move is to spend that headroom on more bits, not on calibrating a lower quant, because straight bits beat clever error placement once you are out of the danger zone. imatrix earns its keep specifically when you are forced low, when a model only fits in unified memory at IQ3 or Q3, and you are trying to claw back as much of the lost quality as possible. We covered where those bit width lines fall for quality in the Q4 versus Q8 versus FP16 analysis, and imatrix is the tool for living below them.

The Apple Silicon Caveat: This Is a GGUF Story

Everything here is about GGUF quantized through llama.cpp, which is one of two worlds on Apple Silicon. The other is MLX, whose quantization scheme does not use an importance matrix in the same way, so this lever simply does not exist there. If you run MLX because you want its tighter integration with the Metal stack and its throughput profile, you do not get an imatrix knob to turn, and the quality comparison between the two formats becomes a different question, which we worked through in the GGUF versus MLX formats piece. The practical consequence is that a calibrated low bit GGUF can beat an uncalibrated MLX quant at the same nominal bits, and people mistake that for a format winning when it is really calibration winning. Compare like with like or you will draw the wrong conclusion.

One more thing worth stating plainly: the importance matrix protects weights, not the KV cache. If your memory pressure is coming from long context rather than the weights, calibration does nothing for you and cache quantization is the lever instead, which is a separate build and runtime decision we covered in the KV cache quantization analysis.

When This Applies to Your Stack

If you run local models on Apple Silicon at Q5 or higher because you have the memory, stop reading here: imatrix will not move your numbers enough to justify the build step, and you should spend your effort elsewhere. If you are forced into IQ2, IQ3, or Q3 because a model only fits in unified memory at those bits, then calibrating with an importance matrix built from a corpus that looks like your real traffic is one of the highest return actions available to you, because it costs nothing at serving time and recovers a meaningful share of the quality the low quant threw away. The decision rule is simple. Pick the highest bit width your memory allows first. Only if that lands you in the low bit region do you reach for imatrix, and when you do, calibrate on your domain, not on generic text.

If your team is putting a local model behind a real product feature and is fighting the memory ceiling on Apple Silicon, Contra Collective builds AI infrastructure where the quantization strategy is chosen against your actual traffic and measured against full precision, not picked from a filename. The bits decide most of the quality. Calibration decides whether the last bit you can afford is spent well.

FAQ

What does an importance matrix do in GGUF quantization? It records how much each weight matters by running a calibration corpus through the full precision model and measuring activation energy against each weight. The quantizer then protects the high importance weights with more effective precision and lets the near dead weights absorb the rounding error. The output file is the same size and runs at the same speed. Only the distribution of error changes.

Does imatrix make inference slower or use more memory? No. The importance matrix is used once during quantization and is not part of the model file. At serving time an imatrix quant and a plain quant of the same type have identical speed, identical memory, and identical format. The only cost is a one time calibration pass when you build the quant.

At which bit widths is imatrix worth it? Mostly at the low ones. In our M5 Max testing it recovered roughly half of the added divergence at IQ2 and IQ3, a modest amount at Q4, and almost nothing at Q5 and Q6. If you can afford Q5 or higher, add bits rather than calibrating. Reserve imatrix for when a model only fits at 2 to 3 bits.

What calibration corpus should I use? A few hundred diverse chunks that resemble your real traffic. General English text produces a matrix that generalizes to ordinary prompts. If your production use is narrow, such as one programming language or one product catalog, calibrating on a sample of that domain protects the weights your traffic actually exercises. Avoid tiny or repetitive corpora, which build a matrix that protects the wrong weights.

Does imatrix exist for MLX models on Apple Silicon? Not in the same form. The importance matrix workflow described here is a llama.cpp and GGUF feature. MLX uses a different quantization scheme without the same calibration step, so comparisons should hold the format constant. A calibrated low bit GGUF beating an uncalibrated MLX quant is a calibration effect, not proof that one format is better.

[ 02 ] — Keep Reading

More from the lab.

Oct 4, 2026 AI Engineering

The Model Context Protocol's STDIO Problem: Inside the Latest Remote Code Execution Disclosure

Security researchers at OX Security found that the Model Context Protocol's STDIO transport interface runs any process command passed to it on the host system before it ever confirms the subprocess is a valid MCP server, an execute first, validate never pattern that completes an injected command even when the caller eventually gets an error message back. The scope is not small: 150 million downloads across package registries, more than 7,000 publicly accessible MCP servers, and up to 200,000 vulnerable instances across every officially supported SDK, Python, TypeScript, Java, and Rust. Anthropic's response, after OX disclosed in January 2026, was to update documentation and call the behavior intentional. Here is what that actually means if your team runs MCP servers over STDIO.

Sep 30, 2026 AI Engineering

OpenAI's Decisions API: Real-Time Agentic Decision-Making for Enterprise Workflows, Explained

Buried inside OpenAI's DevDay 2026 announcement list alongside GPT-6.1 Sol and Dots agents was a smaller, more technically specific release: a Decisions API built for real time decision making against predefined answer sets. Where the Agents API and a general purpose agent loop are built for open ended reasoning across an unbounded task, the Decisions API is built for the opposite case, a small number of valid outcomes and a requirement that the choice happen fast. This post works through what that distinction actually means in practice and where a bounded decision primitive fits into an existing agentic workflow.

Aug 8, 2026 AI Engineering

Offline Batch Inference on an M5 Max: Maximizing Overnight Throughput for a Prompt Backlog (2026)

Most writing about local inference assumes a human is on the other end, watching tokens appear and judging the model by how fast the first one lands. Batch work is the opposite situation, and it is more common than the interactive framing suggests: you have a backlog of tens or hundreds of thousands of prompts, nobody is waiting on any single one, and the only thing that matters is how much of the pile you can clear before morning. Product descriptions for a catalog, classification over a support archive, embeddings and summaries for a corpus, synthetic data for a fine tune. In that regime latency is irrelevant and throughput is everything, and the settings that make a local server feel responsive to a person actively work against you, because they optimize for the wrong number. Running one prompt at a time on an M5 Max leaves most of the machine idle, since a single decode stream cannot saturate the memory bandwidth the chip has to offer. The job is to keep many sequences in flight so the expensive weight reads are amortized across all of them, and to arrange the backlog so the GPU never waits on padding, scheduling, or a slow tail. This post is about doing that deliberately: what actually bounds batch throughput on Apple Silicon, how far batching takes you before memory stops you, and the operational scaffolding that turns a fragile overnight run into one you can resume when it dies at 3am.

Ready when you are

Want to discuss this topic?

Start a Conversation