INT4 Quantization › The Challenge of 4-Bit Precision

Michael BrenndoerferJanuary 12, 202610 min read
Language AI HandbookAll chapters
Part 42 of 67Inference Optimization
Chapter 319INT4 Quantization › The Challenge of 4-Bit Precision
Reading section 2 of 11 · The Challenge of 4-Bit Precision · 10 minSection 2 of 11 · 10 min

Book contents

Section 2 of 11 · 10 min read

INT4 Quantization › The Challenge of 4-Bit PrecisionOne section · Sections in this chapter2 / 11 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Understanding why 4-bit quantization is hard requires understanding what those 16 levels mean in practice. With only 4 bits, we can represent just 16 distinct values, and every weight in the neural network must map to exactly one of these 16 values. To understand why this limitation is so severe, consider the basic mathematics at play. If we use signed integers, this gives us a range from -8 to 7 (or -7 to 7 with a symmetric scheme). Every weight in a neural network, regardless of its original floating-point precision, must map to one of these 16 values. This is an extremely coarse discretization of the original continuous space, and the consequences propagate through every matrix multiplication in the model.

Think of it this way: INT4 quantization is like trying to represent every color in a photograph using only 16 distinct shades. A grayscale photograph with 256 shades (INT8) already loses some detail, but you can still recognize faces and read text. With only 16 shades, fine details merge together and subtle distinctions disappear. For neural network weights, those subtle distinctions between similar-but-not-identical small weights are precisely what encode the model's learned knowledge, and losing them changes model behavior in unpredictable ways.

Consider what this means for a typical weight distribution. Neural network weights after training often follow an approximately normal distribution centered near zero. The majority of weights cluster around small magnitudes, with the distribution tapering off symmetrically toward the tails. With INT8, we have enough resolution to capture the shape of this distribution reasonably well, since 256 quantization levels can provide fine granularity even in the densely populated region near zero. With INT4, we are essentially creating a 16-bucket histogram to represent a continuous distribution. Each bucket must absorb a wide swath of the original weight values, collapsing potentially meaningful differences into a single quantized output.

When we quantize a value, we introduce a quantization error equal to the difference between the original value and its quantized representation. With 256 levels, the maximum error for any single weight is bounded by half the step size between adjacent levels. With only 16 levels, that step size becomes 16 times larger, and the maximum quantization error grows proportionally. This error propagates through every matrix multiplication in the network, accumulating and potentially compounding in ways that can fundamentally alter the model's behavior. The errors are not random noise that averages out: they are systematic biases that can shift the model's predictions in consistent but unexpected directions.

The key insight is that the severity of quantization error depends on how many levels are available and how well those levels match the data distribution. Most of the problems with naive 4-bit quantization stem from a mismatch: the quantization grid is designed for uniform data, but neural network weights are emphatically non-uniform. This mismatch motivates both the group-wise approach (which adapts the scale locally) and the NF4 format (which adapts the level placement globally).

In[4]:
Code
import numpy as np

# Simulate a typical neural network weight distribution deterministically
weight_rng = np.random.default_rng(42)
weights = weight_rng.standard_normal(10000) * 0.02  # Typical small weight scale

# What INT8 and INT4 quantization "see"
int8_bins = 256
int4_bins = 16
Out[5]:
Visualization
Histogram of original FP32 weight values showing a bell-shaped normal distribution centered near zero with smooth, continuous density.
Original FP32 weight distribution showing a bell-shaped density histogram centered near zero. The many narrow bins retain the continuous variation from which we must extract a compressed representation.
Out[6]:
Visualization
Histogram of weights quantized to INT8 showing 256 bins that faithfully represent the normal distribution shape with fine granularity.
INT8 quantization with 256 levels. The 256 bins provide sufficient granularity to capture the shape of the distribution, including the tails. The distribution remains visually faithful to the original FP32 version.
Out[7]:
Visualization
Histogram of weights quantized to INT4 showing only 16 bins creating a coarse step-function approximation of the original distribution.
INT4 quantization with 16 levels. The coarse 16-bin histogram fails to capture fine details, creating a step-function approximation of the continuous distribution and merging many distinct weights into the same quantized value.

The loss of precision becomes more severe when we consider outliers. Recall from our INT8 discussion that neural networks often have a small number of outlier weights with magnitudes much larger than the majority. These outliers, though rare, can be critically important for the model's computations, often encoding strong feature responses or needed biases. With INT8, we can absorb some outlier impact because we have 256 levels to work with. This provides reasonable resolution even after accommodating extreme values. With INT4, outliers become catastrophic because the limited number of levels cannot simultaneously span a wide range and maintain adequate precision in the dense central region.

The Outlier Problem Intensified

To build intuition for why outliers cause such severe problems at 4-bit precision, imagine a layer where 99% of weights fall between -0.1 and 0.1, but a few outliers reach values of 0.5 or higher. If we set our quantization range to cover the outliers, most of our 16 quantization levels will be "wasted" on the outlier range, leaving very few levels to distinguish the majority of weights near zero. This is a basic trade-off that becomes increasingly painful as the number of available levels decreases.

Think of this problem as a zoom lens on a camera. If your frame must fit both a distant mountain and a nearby flower, the flower becomes just a few indistinct pixels. The "zoom level" of your quantization scale is forced to accommodate the most extreme value, compressing everything else into a tiny slice of your available range. With 256 levels (INT8), you still have reasonable detail on the flower even when the mountain is in frame. With 16 levels (INT4), the flower becomes essentially invisible.

Consider the mathematics of this trade-off. Suppose our 16 quantization levels must span from -0.5 to 0.5 to accommodate outliers. The step size between adjacent levels is then 1.0 divided by 15 (since we have 16 levels creating 15 intervals), giving approximately 0.067 per step. For the 99% of weights living in the range of -0.1 to 0.1, this means they have access to only about 3 distinct quantization levels. The subtle differences between small weights, which are important for model computations, are lost in the coarseness imposed by the outliers.

More formally, for symmetric quantization, the scale factor ss is determined by the maximum absolute value in the tensor:

s=max⁡(∣w∣)qmax⁡s = \frac{\max(|\mathbf{w}|)}{q_{\max}}

where:

  • w\mathbf{w}: the vector of weights being quantized
  • max⁡(∣w∣)\max(|\mathbf{w}|): the maximum absolute value across all weights in the group
  • qmax⁡q_{\max}: the maximum representable integer (7 for signed INT4 with range -7 to 7)

A single outlier weight with magnitude 0.6 among thousands of weights with magnitude 0.02 forces s≈0.6/7≈0.086s \approx 0.6 / 7 \approx 0.086. For a weight of 0.02, the quantized index is round(0.02/0.086)=round(0.23)=0\text{round}(0.02 / 0.086) = \text{round}(0.23) = 0. Nearly every small weight gets mapped to integer 0, making them indistinguishable from each other and from true zeros. The entire information content of the small-weight region collapses to a single point.

In[8]:
Code
# Demonstrate outlier impact on INT4 quantization
outlier_rng = np.random.default_rng(42)

# Normal weights with a few outliers
normal_weights = outlier_rng.standard_normal(1000) * 0.02
outliers = np.array([0.5, -0.4, 0.6, -0.5])  # Just 4 outliers
weights_with_outliers = np.concatenate([normal_weights, outliers])

# Uniform quantization across full range
w_min, w_max = weights_with_outliers.min(), weights_with_outliers.max()
scale = (w_max - w_min) / 15  # 16 levels (0-15) for unsigned INT4


def quantize_uniform(weights, scale, w_min):
    """Uniform quantization to 4-bit range (0-15)"""
    q = np.round((weights - w_min) / scale).astype(int)
    q = np.clip(q, 0, 15)
    return q


def dequantize_uniform(q, scale, w_min):
    """Dequantize back to float"""
    return q * scale + w_min


q_weights = quantize_uniform(weights_with_outliers, scale, w_min)
reconstructed = dequantize_uniform(q_weights, scale, w_min)

# Calculate error metrics
mse = np.mean((weights_with_outliers - reconstructed) ** 2)
max_error = np.max(np.abs(weights_with_outliers - reconstructed))
Out[9]:
Console
Weight range: [-0.5000, 0.6000]
Scale factor: 0.073333
Mean squared error: 0.00039370
Maximum error: 0.036582

Number of unique quantized values used: 7

With a scale of approximately 0.07, each quantization step is large relative to our typical weights near zero. Most small weights get mapped to the same few quantization levels, destroying the subtle differences between them. The quantization process discards the details that distinguish small weights, even though these differences affect inference. Notice also how few unique quantized values appear: with 1004 weights and 16 possible levels, we would expect all 16 to be used, but outliers force most values into just a handful of central bins.

Out[10]:
Visualization
Histogram of weight distribution with 16 INT4 quantization boundary lines showing that outliers force a wide quantization range, leaving very few levels for the dense central region.
INT4 quantization boundaries with outliers present. The wide range required to accommodate four outliers (orange dashed lines) leaves only a few quantization levels for the dense region near zero, where most weights cluster.
Out[11]:
Visualization
Histogram of absolute quantization errors spread across the full half-step interval, with a vertical line marking the high mean error caused by outlier-dominated INT4 quantization.
Distribution of absolute quantization errors under naive INT4 quantization with outliers. The high mean error (vertical red line) shows that even typical small weights suffer large reconstruction errors because the step size is forced wide by a handful of outlier values.