INT4 Quantization › Group-Wise Quantization

Michael BrenndoerferJanuary 12, 202611 min read
Language AI HandbookAll chapters
Part 42 of 67Inference Optimization
Chapter 319INT4 Quantization › Group-Wise Quantization
Reading section 3 of 11 · Group-Wise Quantization · 11 minSection 3 of 11 · 11 min

Book contents

Section 3 of 11 · 11 min read

INT4 Quantization › Group-Wise QuantizationOne section · Sections in this chapter3 / 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

The solution to the outlier problem at 4-bit precision is group-wise quantization, also called block-wise quantization. This technique represents a major change in how we approach the quantization problem: instead of asking "what single scale factor best represents this entire weight matrix?" we ask "what scale factor best represents each small neighborhood of weights?" Instead of using a single scale factor for an entire tensor or even a channel, we divide weights into small groups and compute separate scale factors for each group. This localized approach isolates the impact of outliers, preventing them from corrupting the quantization of the entire weight matrix.

Think of group-wise quantization as applying a different "zoom level" to different regions of the weight matrix. The parts of the matrix that contain mostly small, closely clustered weights get a fine-grained zoom that can distinguish subtle differences. The parts that happen to contain outliers get a wider zoom to accommodate them, but only those parts are affected. The rest of the matrix continues to benefit from fine resolution, oblivious to the outliers elsewhere.

This approach works because of a key statistical fact about how outliers appear in trained neural networks. Outlier weights do not distribute themselves uniformly throughout a weight matrix. They appear sporadically, concentrated in particular rows, columns, or small regions. When they do appear, they are surrounded by many non-outlier weights that have similar small magnitudes. By working with groups of 32, 64, or 128 consecutive weights, we ensure that any given group likely contains either zero or a small number of outliers. The groups containing outliers sacrifice precision for range; the groups without outliers maintain fine-grained precision. This spatial separation of outlier influence is the core mechanism that makes group-wise quantization so effective.

The memory overhead of group-wise quantization is modest and well-controlled. Each group requires one additional scale factor, typically stored in FP16 (16 bits). For a group of 128 weights, this adds 16 bits amortized over 128 four-bit weights, which is 16/128=0.12516/128 = 0.125 extra bits per weight. The total effective bit rate becomes 4+0.125=4.1254 + 0.125 = 4.125 bits per weight, barely above the nominal INT4 rate. As we will see with double quantization later in this chapter, even this small overhead can be further reduced.

The Group Quantization Concept

To understand why group-wise quantization works so effectively, consider the spatial distribution of outliers within a weight tensor. In trained neural networks, outliers typically do not occur uniformly throughout a tensor. Instead, they appear sporadically, concentrated in certain locations while leaving large regions of the tensor relatively outlier-free. By using small groups, an outlier affects only the quantization of its own group, not the entire layer. The remaining groups, free from outlier contamination, can use their 16 quantization levels efficiently to represent their local weight distribution with high fidelity.

For a weight tensor, group-wise quantization proceeds through the following steps:

  1. Divide the weights into groups of size gg, where common choices are 32, 64, or 128 elements per group
  2. Compute a separate scale factor (and optionally zero-point) for each group based only on the weights within that group
  3. Quantize each group independently using its own scale factor

The key insight is statistical: within any sufficiently small group of weights, the probability of encountering an extreme outlier is much lower than across the entire tensor. When an outlier does appear in a group, it affects only the gg weights in that group, leaving the thousands or millions of other weights in the tensor unaffected. Containing outlier damage makes 4-bit quantization practical.

The formal description of group-wise symmetric quantization is straightforward. For a group of weights wg=[w1,w2,…,wg]\mathbf{w}_g = [w_1, w_2, \ldots, w_g], we compute:

sg=max⁡(∣wg∣)qmax⁡s_g = \frac{\max(|\mathbf{w}_g|)}{q_{\max}} qi=clip ⁣(round ⁣(wisg),−qmax⁡,qmax⁡)q_i = \text{clip}\!\left(\text{round}\!\left(\frac{w_i}{s_g}\right), -q_{\max}, q_{\max}\right)

where:

  • sgs_g: the scale factor for group gg, computed from the weights in that group only
  • max⁡(∣wg∣)\max(|\mathbf{w}_g|): the maximum absolute weight value within the group
  • qmax⁡q_{\max}: the maximum representable integer (7 for signed INT4 with range -7 to 7)
  • qiq_i: the quantized integer representation of weight wiw_i
  • round(⋅)\text{round}(\cdot): rounding to the nearest integer
  • clip(⋅,−qmax⁡,qmax⁡)\text{clip}(\cdot, -q_{\max}, q_{\max}): clamping to the valid integer range

Reconstruction is simply the inverse operation: w^i=qi⋅sg\hat{w}_i = q_i \cdot s_g. The stored representation is the collection of integers {qi}\{q_i\} along with the per-group scale factors {sg}\{s_g\}.

Group Size Trade-off

Smaller groups provide better quantization accuracy because outliers affect fewer weights. However, smaller groups require storing more scale factors, increasing memory overhead. A group size of 128 is common, adding roughly 0.125 bits per weight in overhead (one FP16 scale per 128 4-bit weights). Choosing a group size involves balancing accuracy and storage. The best choice depends on the model and memory constraints, though 64 or 128 has emerged as the practical sweet spot for most LLM deployments.

In[12]:
Code
def group_quantize_int4(weights, group_size=128):
    """
    Group-wise INT4 quantization.
    Returns quantized values and scale factors per group.
    """
    # Flatten weights for grouping
    flat_weights = weights.flatten()
    n = len(flat_weights)

    # Pad to multiple of group_size
    pad_len = (group_size - n % group_size) % group_size
    if pad_len > 0:
        flat_weights = np.concatenate([flat_weights, np.zeros(pad_len)])

    # Reshape into groups
    n_groups = len(flat_weights) // group_size
    groups = flat_weights.reshape(n_groups, group_size)

    # Compute scale per group (symmetric quantization)
    max_vals = np.max(np.abs(groups), axis=1, keepdims=True)
    scales = max_vals / 7.0  # Symmetric INT4: -7 to +7
    scales = np.where(scales == 0, 1.0, scales)  # Avoid division by zero

    # Quantize each group
    q_groups = np.round(groups / scales).astype(np.int8)
    q_groups = np.clip(q_groups, -8, 7)

    return q_groups.flatten()[:n], scales.flatten()


def group_dequantize_int4(q_weights, scales, group_size=128):
    """Dequantize group-wise quantized weights."""
    n = len(q_weights)
    pad_len = (group_size - n % group_size) % group_size

    if pad_len > 0:
        q_weights = np.concatenate([q_weights, np.zeros(pad_len)])

    n_groups = len(q_weights) // group_size
    q_groups = q_weights.reshape(n_groups, group_size)
    scales = scales.reshape(-1, 1)

    deq_groups = q_groups * scales
    return deq_groups.flatten()[:n]

Now let's compare uniform quantization versus group-wise quantization on our weights with outliers. This comparison will demonstrate the dramatic improvement that group-wise quantization provides when outliers are present in the data.

In[13]:
Code
# Compare quantization approaches
group_size = 32  # Small groups for this example

# Group-wise quantization
q_grouped, scales = group_quantize_int4(weights_with_outliers, group_size)
reconstructed_grouped = group_dequantize_int4(q_grouped, scales, group_size)

# Calculate errors for comparison
mse_uniform = np.mean((weights_with_outliers - reconstructed) ** 2)
mse_grouped = np.mean((weights_with_outliers - reconstructed_grouped) ** 2)
Out[14]:
Console
Quantization Error Comparison:
  Uniform INT4 MSE:     0.00039370
  Group-wise INT4 MSE:  0.00000743
  Error reduction:      98.1%

Number of scale factors stored: 32
Out[15]:
Visualization
Overlaid histograms of quantization errors comparing group-wise (blue, narrow peak near zero) and uniform (orange, wide spread) INT4 quantization methods.
Error distribution comparison between uniform and group-wise INT4 quantization. Group-wise quantization (blue) concentrates errors tightly near zero, while uniform quantization (orange) yields a broad error distribution because outliers inflate the step size for all weights.
Out[16]:
Visualization
Scatter plot of original versus reconstructed weight values, with group-wise quantization points clustering along the diagonal and uniform quantization points scattered farther from it.
Reconstruction accuracy scatter plot comparing uniform and group-wise quantization. Group-wise reconstructed weights (blue) cluster tightly along the perfect reconstruction diagonal, while uniform quantization (orange) scatters points widely, especially for small-magnitude weights near zero.

Group-wise quantization substantially reduces quantization error because outliers no longer dominate the scale factor for the majority of weights. Each group operates with a scale factor tailored to its local distribution. This keeps the 16 available quantization levels are deployed where they provide the most benefit for that particular subset of weights. The scatter plot makes this concrete: with uniform quantization, many near-zero weights collapse to the same reconstructed value, appearing as horizontal bands on the plot. With group-wise quantization, the reconstruction tracks the original faithfully even for tiny weights.

Optimal Group Size Selection

The choice of group size balances accuracy against memory overhead, and understanding this trade-off is needed for making informed deployment decisions. Smaller groups provide finer-grained adaptation to local weight statistics, but each group requires its own scale factor. These scale factors, typically stored in FP16 format (16 bits each), add to the overall memory footprint. Let's examine this trade-off quantitatively:

In[17]:
Code
# Test different group sizes
group_size_rng = np.random.default_rng(42)
test_weights = group_size_rng.standard_normal(4096) * 0.02
# Add some outliers
test_weights[100] = 0.5
test_weights[200] = -0.4
test_weights[1500] = 0.45

group_sizes = [16, 32, 64, 128, 256, 512]
results = []

for gs in group_sizes:
    q, s = group_quantize_int4(test_weights, gs)
    recon = group_dequantize_int4(q, s, gs)
    mse = np.mean((test_weights - recon) ** 2)

    # Calculate effective bits per weight
    n_scales = len(s)
    scale_bits = n_scales * 16  # FP16 scales
    weight_bits = len(test_weights) * 4  # INT4 weights
    total_bits = scale_bits + weight_bits
    effective_bpw = total_bits / len(test_weights)

    results.append(
        {
            "group_size": gs,
            "mse": mse,
            "effective_bpw": effective_bpw,
            "n_scales": n_scales,
        }
    )
Out[18]:
Console
Group Size vs. Accuracy Trade-off:
-------------------------------------------------------
  Group Size            MSE  Bits/Weight     Scales
-------------------------------------------------------
          16     0.00000579        5.000        256
          32     0.00000980        4.500        128
          64     0.00001644        4.250         64
         128     0.00002944        4.125         32
         256     0.00004181        4.062         16
         512     0.00007915        4.031          8
Out[19]:
Visualization
Dual-axis line chart showing quantization MSE increasing while effective bits per weight decrease as group size grows from 16 to 512, with a practical trade-off around group size 64 to 128.
Group size trade-off in INT4 quantization. Smaller groups reduce quantization error (left axis, blue line) because each scale factor covers fewer weights, limiting outlier damage. Larger groups reduce scale-factor overhead (right axis, orange line), so effective bits per weight approach the 4.0-bit minimum as group size grows.

Group sizes of 32-128 typically offer the best balance between quantization fidelity and storage efficiency. Smaller groups provide diminishing returns in accuracy while significantly increasing the number of scale factors to store. The sweet spot depends on the specific model and hardware constraints, but 64 or 128 elements per group has emerged as a common choice in practice. This provides good accuracy with manageable overhead. Beyond group size 128, the memory savings from a larger group become marginal because scale factor overhead was already small, while accuracy continues to degrade as more diverse weights share a single scale.