Outliers and clipping

LLMs contain a few huge values among millions of small ones, especially in activations. Clipping the range trades a little damage to the outliers for a much finer grid for everything else. Where to clip is a calibration problem.

Key idea
Heavy tails, outlier features and massive activations; clipping vs rounding error; calibration by percentile, MSE or KL
Real world
Scaling a chart of household incomes so a single billionaire fits on it squashes everyone else into one bar. Cutting the axis at a sensible point (and labelling the billionaire separately) shows the rest of the population.
In an LLM
From about 6.7 billion parameters, a handful of hidden-state channels hold values 20–100× larger than the rest, in nearly every token. Quantize those activations to int8 with one scale per token and the other 4,000 channels get almost no resolution.

Heavy tails

If weights and activations were neatly bell-shaped, a grid stretched to the largest value would be fine. They aren’t. Their distributions have heavy tails: almost all values are small, but a few are very far out. The absmax scale is set by those few, so the grid is coarse exactly where most of the values are.

There are two kinds of outliers in LLMs:

  • Weight outliers: scattered individual weights much larger than their neighbours. Small groups (granularity) contain them well.
  • Activation outliers: whole channels (the same feature dimension, in almost every token) with values tens of times larger than the rest. Dettmers et al., 2022 found they appear abruptly in models around 6.7B parameters and grow with scale. A few tokens (often the first token, or a full stop) also carry massive activations thousands of times larger than typical. Activation outliers are the main reason activations are harder to quantize than weights.

Clip, or stretch the grid?

There are two errors to balance:

  • Rounding error for values inside the range, proportional to the grid step. A wider range means a coarser step and more rounding error.
  • Clipping error for values outside the range, which are flattened to the edge. A narrower range means more clipped values.

Clipping at the very largest value has zero clipping error but maximum rounding error. Clipping too tightly does the reverse. Somewhere in between is the minimum total error.

Try it

calibration rules:
15 levels, 3.18 apart · triangles mark where two calibration rules would clip.
total error (MSE)rounding error (inside)clipping error (outside)
clipped values0.00%
total error0.6861
vs no clipping1.00×
best threshold40% of max
No clipping: the grid stretches to the single largest value, so the thousands of small values share only a few of the 15 levels. Rounding error dominates; drag the clip in.
4,000 values with a heavy tail, like LLM activations (bar heights use a square-root scale so the rare tail is visible). The grid of quantization levels (thin lines) covers only the shaded range; anything outside is clipped to its edge.
example · a chart of incomes

Plot the incomes of a town of 4,000 people where one resident is a billionaire. If the axis runs to £1 billion, everyone else is a sliver at the bottom. Cut the axis at £300,000 and mark the billionaire “off the chart”: you lose the exact figure for one person, and gain a readable picture of everyone else. Clipping makes the same trade.

example · an outlier channel

A token’s hidden state has 4,096 values. 4,090 of them are between −2 and 2; six channels are around 60. Per-token int8 with absmax sets s=60/127≈0.47s = 60 / 127 \approx 0.47. Every ordinary value now lands on one of about 9 grid points (−2 to 2 in steps of 0.47): effectively a 3-bit grid for 99.9% of the values. Clipping at 2 would destroy the six outlier channels, which turn out to matter a great deal: removing them collapses the model’s quality. So neither stretching nor clipping works, and LLM.int8() and SmoothQuant were invented for exactly this.

Calibration

For activations, the range has to be chosen in advance from sample data: run a few hundred examples through the model, record the values each layer sees, and pick a clipping range. This is calibration. Common rules:

  • Min–max: no clipping. Safe, often too coarse.
  • Percentile: clip at the 99.9th or 99.99th percentile of magnitudes.
  • MSE: choose the range that minimises the mean squared error between original and quantized values, as in the widget.
  • KL divergence: choose the range that keeps the quantized histogram closest to the original (used by TensorRT for int8).

For weights, no calibration data is needed for the range itself, since the weights are known; but the better LLM methods (GPTQ, AWQ) still use calibration data to learn which weights matter most.

Where this shows up

going deeper

The clipping trade-off

For clipping threshold cc and bb bits, with step s=c/qmax⁡s = c / q_{\max}, the expected squared error is approximately

E[(x^−x)2]≈s212 P(∣x∣≤c)⏟rounding+E[(∣x∣−c)2 ; ∣x∣>c]⏟clipping\mathbb{E}\big[(\hat x - x)^2\big] \approx \underbrace{\frac{s^2}{12}\, P(|x| \le c)}_{\text{rounding}} + \underbrace{\mathbb{E}\big[(|x| - c)^2 \,;\, |x| > c\big]}_{\text{clipping}}

The first term grows with c2c^2; the second shrinks as cc grows, faster for light-tailed distributions. For a Gaussian at 4 bits the optimal cc is about 2.5σ2.5\sigma, far below the maximum of a large sample. For heavy-tailed (Laplace or Student-t) data it is relatively even tighter compared with the max, and the gain from clipping is larger.

In papers

Related

  • granularity - One scale for a whole matrix lets a single outlier ruin everything. Smaller blocks each get their own scale and their own grid, at the cost of storing more scales. Groups of 32 to 128 weights are the sweet spot for 4-bit LLMs.
  • measuring-error - Error in one tensor is easy to measure, but what matters is how the whole model behaves. KL divergence from the original model is the most sensitive single number; real evaluations are the final word.
  • llm-int8 - Large models have a few activation channels that are huge in every token. LLM.int8() pulls them out and keeps them in 16-bit; SmoothQuant moves the problem from activations into weights, where it is easy. Both make 8-bit matrix multiplies work.