Quantization Quality Tradeoffs
Shrinking weights to 4-bit saves memory, but the same rounding noise matters more for close calls. Code, long chats, and weaker languages feel it first.
Why Does This Exist?
A 70 billion parameter model at 16 bits needs about 140 GB just for weights: 70 billion times 2 bytes. At 4 bits it needs about 35 GB: 70 billion times 0.5 bytes. That gap decides whether the model fits on your cards. Because decoding reloads the weights for every token, it also decides how many bytes you move per token. So you quantize. The saving is real and you will want it.
Here is the failure this page exists to prevent. A team ships a coding and multilingual assistant at 4-bit after a quick English check looks fine. Short answers still look fine. Then long support chats drift, code comes back subtly broken, and quality drops hardest once customers switch away from English. Nothing in that first check predicted it, because the check measured the one condition quantization harms least.
This page is about that uneven cost: why identical numeric noise barely touches some tasks and breaks others, and how to test so you catch it before your users do. The rounding mechanism itself lives in post-training quantization. The way to keep 4-bit weights and still adapt the model lives in LoRA and QLoRA.
You need one idea first. A bit width is a budget for distinct values. With bits you can name levels, so 4-bit names 16 levels. Quantization picks a range from real calibration text, spaces those levels evenly, and rounds each weight to the closest mark. If you want the full arithmetic, read the mechanism page above first, then come back. You only need one fact here: each weight lands within half a step of the truth, and that step is the same wherever the weight gets used.
Think of It Like This
Glasses with one thin scratch
You put on glasses with one thin scratch across each lens. A big headline still reads fine. The scratch crosses the letters, but the letters are so large and distinct that you never hesitate. A dense page of small print is slower, and by page ten you are filling in gaps. Sheet music is worse: a note one line higher is a different note, so the same scratch changes the meaning instead of just slowing you down. A script you read less fluently is worst of all, because you were already using context to guess strokes the scratch now hides.
The scratch is the rounding noise: one fixed distortion applied everywhere. What changes is how much each kind of reading depended on fine detail. Headlines are wide margin calls. Sheet music and unfamiliar scripts are close calls.
Where it stops: glasses blur light the same way whatever you read, and you cannot recalibrate them. Quantization noise depends on the ranges you set from calibration text, and much of it averages away across thousands of products in a large layer. Pick better ranges and the scratch gets thinner. The analogy shows why damage is uneven. It does not show how you shrink the damage.
How It Actually Works
Quantization rounds every weight to the closest of a few evenly spaced marks. That adds a small error to nearly every multiplication you do. The error size is about the same everywhere. Whether it changes the answer depends on how close the decision was. That is the whole story, and the rest of this section puts numbers on it.
The noise is the same everywhere
Call the bit width , so the count of levels is . At 4-bit, . From calibration text you read the smallest and largest values you will keep, and . In plain words, the range you refuse to clip. The scale is the spacing between neighboring marks:
Each original weight becomes an integer code , the index of the closest mark, and reads back at inference time as a reconstructed weight . The error obeys : never more than half a step. That bound is the same for a weight used in English chat, code, or another language. The task does not set the noise. It only decides what the noise can break.
Why close calls flip first
Look at one next token choice. Call the top score and the runner up . The decision margin says how close the call was. Large means a confident pick. Small means a coin toss.
Quantization shifts each score by a small amount , where is at most the weight error times the activation it multiplies. The activation is the input number that weight multiplies, so with the shift equals the weight error itself. In the worst case the top score drops by while the rival rises by , so the gap can shrink by . If is larger than that swing, the winner cannot change. If is smaller, it can. Same noise, different margins, different outcomes. That is why code breaks while short factual answers survive.
Worked example with two margins
Take a channel whose calibration range runs to at . Then and . In plain words, marks every 0.2. A trained weight gives , which rounds to code and reads back as . The error is , inside the bound . Those are the numbers in the diagram above.
Feed that weight with activation , so each affected score can move up to . The gap can swing up to .
Say the short answer scores the correct token at and its rival at . Then . Even if the gap loses the full , it stays at . Still positive. The answer cannot flip.
Say the code choice scores the correct bracket at and the wrong one at . Then . A swing covers that easily: . The wrong token can move ahead. One token changes, and in code one token can flip a comparison or break a call. You felt the same in both cases. Only the second case had a margin thin enough to lose.
Why long context and weaker languages suffer more
Long context follows the same logic across steps instead of inside one step. Each new token attends over what came before, and the model feeds its own output back as the next input. The per step noise does not grow, but the number of close calls does. A short answer risks one flip. A fifty thousand token run risks thousands, and an early wobble can steer everything after it. You will not see that drift in a five prompt check because you have not given it enough chances to happen. That compounding is also why context degradation deserves its own read.
Languages follow the same rule through redundancy. English has the most training text and the most overlapping ways to say the same thing, so many decisions start with wide margins. A lower resource language has thinner coverage, so more decisions sit close together before you quantize. The same shift flips more of them. You did not break that language on purpose. You applied uniform noise to an uneven starting point.
The trade is plain. You buy roughly a quarter of the memory and movement: a 70 billion parameter model moves from about 140 GB toward about 35 GB, since and . You pay uneven quality. Do not pay it when your ship workload is mostly exact code, long chats where errors feed back, or weaker languages, unless you have measured those buckets and the drop is one you can live with. The method assumes errors stay small, bounded, and partly average away across wide layers. That assumption fails on thin margins, on calibration text that mismatches real prompts, and on tiny models with little to average over.
Code
The check in a few lines: rebuild the worked error, then ask which margins survive a worst case swing of twice that error.
s: float = 0.2 # 3.0 / 15, the 4-bit step for range 0 to 3w_true: float = 2.72q: int = int(round(w_true / s))w_hat: float = q * se: float = round(w_hat - w_true, 2)
print(q) # -> 14print(round(w_hat, 2)) # -> 2.8print(e) # -> 0.08print(1.20 > 2 * 0.08) # -> Trueprint(0.05 > 2 * 0.08) # -> FalseThe codes match the worked example, and the two booleans tell the story: the 1.20 margin survives the 0.16 swing while the 0.05 margin does not. Use this shape on your own eval: group prompts by bucket, compute the drop per bucket, and gate the ship decision on the worst bucket rather than the average.
Watch Out For
Shipping on a short English check alone
Symptom: five quick prompts look fine, so the 4-bit build ships. Weeks later long chats drift, code answers fail in narrow cases, and another language is clearly worse.
That check measures the condition quantization harms least: short, high margin, dominant language decisions. Passing it proves the model still runs. It says nothing about compounding over a real conversation, exact syntax with no slack, or thinner language coverage.
Fix: rerun the eval in ship conditions before you decide: long conversations at real lengths, genuine code tasks that must run, and each target language. Report accuracy per bucket and block the release on the worst bucket, not the mean.
Calibrating on friendly text, then meeting real prompts
Symptom: quality on the calibration sample looks acceptable, but production prompts fall apart. Outlier activations land outside the fitted range and get clipped, which hurts far more than rounding.
The ranges were fit to the wrong distribution. Short clean text sets tight ranges, while real chats, code, and other languages produce values the quantizer never budgeted for.
Fix: recalibrate on a representative mix of what you will actually serve, set ranges per channel or per small group rather than one global range, and remeasure per bucket after every range change. Never carry a 4-bit win from one model, task, or language to the next without remeasuring it there.
The Quick Version
- Quantization adds the same small rounding noise everywhere, bounded by half a step. On range 0 to 3 at 4-bit, that bound is 0.1.
- Whether noise changes an answer depends on the decision margin, the gap between the top two scores for the next token.
- A 0.08 shift leaves a 1.20 margin short answer safe and can flip a 0.05 margin code token, because the gap can swing up to 0.16.
- Long runs and weaker languages break first because they hold more close calls, not because their per weight noise is larger.
- A 70 billion parameter model drops from about 140 GB toward about 35 GB at 4-bit, which is the saving that makes the uneven cost worth weighing.
- Test quantized builds in ship conditions and gate on the worst bucket, never on a short English check alone.