Visual Learn Quantization-aware training

Quantization-aware training

How a network learns to work with weights rounded to a coarse grid of numbers

The weights of a 70-billion-parameter model take about 140 GB in 16-bit form and about 35 GB at 4 bits. Rounding the weights of a finished model to 4 bits costs accuracy, and the cost grows as bits are removed. Quantization-aware training puts the rounding inside training, so gradient descent can adjust the weights to it.

9 sections · about 20 minutes · the lab trains a small network in your browser

How much memory the weights need

A trained network is mostly a long list of numbers, its weights. Large models are usually trained and served in bf16, a 16-bit floating-point format, so each weight takes 2 bytes. Stored as 4-bit integers, each weight takes half a byte, plus a small overhead for the scale factors that map the integers back to real values.With one bf16 scale for every group of 128 weights, the overhead is 16 bits ÷ 128 = 0.125 bits per weight, so “4-bit” storage costs about 4.13 bits per weight. Figure 1 multiplies this out for five model sizes.

Weight storage (parameters × bytes per weight) on a log scale, so each halving of the bit width is the same step to the left. Dashed lines: memory of three GPUs, nominal. Weights only: activations, the KV cache and runtime buffers need memory on top. At 4 bits the weights of a 70B model fit on one 80 GB H100, and those of a 27B model fit in 16 GB.

Smaller weights also move faster. When a model generates text one token at a time, each step reads every weight from memory, so the time per token is often set by memory bandwidth rather than arithmetic. Packed 4-bit weights shrink that traffic, but the speed-up arrives only if the runtime has kernels that unpack the format efficiently.

The price is accuracy. Rounding every weight to one of 15 or 16 values perturbs the network, and the perturbation grows as bits are removed. Quantization-aware training pays less of that price by training the network with the rounding in place.

Rounding a number onto a grid

A uniform quantizer replaces a real number with the nearest of a fixed set of evenly spaced levels. This page uses the symmetric version that is common for weights. Choose a clipping range [−α, α]. With b bits, place the levels s = α / qmax apart, where qmax = 2b−1 − 1. The spacing s is called the scale. Each value x is stored as an integer q and read back as x̂:At 4 bits qmax = 7, so q runs from −7 to 7: 15 levels. The sixteenth code, −8, goes unused so that the grid is symmetric about zero. Many libraries use the full range instead.

q = clamp(round(x / s), −q_max, q_max)
x̂ = s · q

The error x − x̂ comes in two kinds. Inside the range, rounding error is at most half a step, s/2. Outside it, the value is replaced by the edge level ±α, and this clipping error grows with the distance from the range.

(a) The grid · grey curve: how the weights are distributed

x = 0.437 · q = 3 · x̂ = 0.429 · error = +0.008 (rounding) · 15 levels, s = 0.143

(b) Error |x − x̂| at every x

(c) Average squared error over the weights, as α varies

A symmetric uniform quantizer. The weights in (a) and (c) are drawn from a normal distribution with standard deviation σ = 0.35. Panel (c) splits the mean squared error into its rounding part (solid grey) and clipping part (dashed grey). Shrinking α trades clipping error for rounding error. The best α is where their sum is lowest, and it moves outward as bits are added.

For these weights the best α is 1.2 standard deviations at 2 bits, 2.5 at 4 bits and 3.9 at 8 bits. With only a few levels, it pays to spend them on the dense middle of the distribution and give up the rare large values. Real weight tensors are not exactly normal, so quantization libraries choose α from the data. Common rules take the largest absolute value, a high percentile, or the α with the lowest measured error.

Activations may use an asymmetric range with a zero-point offset, because their values need not be centered on zero. After a ReLU, for example, every value is zero or positive, and a symmetric grid would waste half its levels.

One scale for many weights

A tensor stores one scale for many weights, not one per weight. With a single scale for the whole tensor, called per-tensor scaling, α has to reach the largest weight. One outlier then stretches the grid for every other weight in the tensor. Giving each row its own scale, called per-row or per-channel scaling, confines the damage to the outlier's row.

Per-tensor scale, no outlier

relative error –

Per-tensor scale, one outlier

relative error –

Per-row scales, one outlier

relative error –

Quantization error |W − Q(W)| for the same 12 × 12 slice of a simulated weight matrix (normal, σ = 0.3), at 4 bits. Darker cells have larger error, on one color scale for all three panels. The ringed cell holds the outlier, 2.6; it sets α, lands exactly on the top level and so has no error itself. Under a per-tensor scale the outlier raises the error of every other weight; with per-row scales only its own row pays.

Smaller groups isolate outliers better but store more scales. Four-bit formats for language models usually go further than per-row scales and use one scale per group of 32 to 128 consecutive weights.

Activations are harder to quantize than weights. They change with every input, and some channels carry large values on nearly every token, so low-bit activation quantization often needs finer scales, calibration data, clipping, or a transformation of the values first. This is one reason why quantizing only the weights to 4 bits is more common than quantizing weights and activations to 4 bits together, a combination written W4A4.

Putting the rounding inside training

There are two ways to arrive at a quantized model. Post-training quantization (PTQ) quantizes a finished model without further end-to-end training. Some PTQ methods use a small calibration set to choose scales; others work from the weights alone. At 8 bits PTQ often preserves quality. At 4 bits and below, the choice of quantizer and calibration matters more, and the accuracy loss can be large.

Quantization-aware training (QAT) continues training, but computes the loss with quantized weights, and sometimes quantized activations, in the forward pass. The loss then includes the damage done by rounding, and gradient descent reduces that damage along with the rest of the loss. Figure 4 shows one training step.

One QAT step for a single linear layer, simplified. Down the left, the forward pass computes the loss with the quantized weight Ŵ. Up the right, the gradient crosses the rounding step through the straight-through estimator (next section) and updates the master weight W, which is kept in fp32. The optimizer only ever changes W; Ŵ is recomputed from W at every step.

The rounding step is called fake quantization. It rounds a tensor to the grid and immediately multiplies by the scale again, so the result is still a floating-point tensor whose values lie on the grid, and training runs on ordinary floating-point kernels.Jacob et al., 2018 describe training with simulated quantization for integer-only inference. When training ends, the master weights are converted one last time, to the integer codes q and scales s that the inference runtime expects, and W is discarded.

Gradients through a staircase

Rounding breaks gradient descent. As a function of its input, round() is a staircase, flat between jumps. Its derivative is zero almost everywhere and undefined at the jumps, so backpropagation through it delivers no gradient to the master weights.

The straight-through estimator (STE) works around this.Bengio, Léonard and Courville, 2013 study the straight-through estimator for neurons with hard thresholds. In the backward pass it treats rounding as the identity, with derivative 1. The version on this page also keeps the derivative of clipping, which is 1 inside [−α, α] and 0 outside, so a clipped weight receives no gradient. Other QAT methods use other surrogate gradients, especially when they learn the scale.LSQ (Esser et al., 2020) defines a gradient for the step size s itself, so the clipping range is trained along with the weights.

Forward pass: x̂

True derivative dx̂/dx

Straight-through estimate

One quantizer with s = 1 and α = 2, drawn three ways on the same x-axis. The true derivative is 0 on every flat step and undefined at the jumps (dotted). The true derivative carries no signal. The STE passes the gradient through unchanged wherever the input lies inside the clipping range.

Why the master weight stays in full precision

An update applied to Ŵ directly would be rounded away. A weight on level 1 that is nudged by −0.03 rounds straight back to level 1, however many times it is nudged. The master weight W keeps the nudges. Each one moves W a little, and Ŵ changes only when their sum carries W across a rounding boundary, halfway between two levels.

Simulated: one master weight receiving noisy updates that average −0.03 per step, with s = 1. Blue: the master weight W. Orange: Ŵ = round(W), the value the forward pass uses. Dashed: rounding boundaries. Ŵ holds still for dozens of steps while W drifts, and moves only when W crosses a boundary.

The same mechanism has a failure mode. A master weight that sits near a boundary can flip Ŵ back and forth between two levels as noisy updates push it across and back, as happens early in Figure 6. Nagel et al. report that these oscillations hurt low-bit QAT of efficient networks such as MobileNets, and propose ways to damp them or freeze the oscillating weights.Nagel, Fournarakis, Bondarenko and Blankevoort, “Overcoming Oscillations in Quantization-Aware Training”, ICML 2022.

PTQ and QAT on a small network

The lab below trains a network in your browser and quantizes it both ways. The network is small: one input, 32 tanh hidden units and one output, 97 parameters in all. Its task is to fit sin(3x) + 0.3 cos(9x) at 64 evenly spaced inputs in [−1, 1]. Each run has three stages:Details: full-batch Adam with a cosine learning-rate decay from 0.01 to 0. Weights are quantized per tensor with the symmetric scheme above, and the scale is recomputed from the largest master weight at every step, so no weight is ever clipped. Biases and activations are not quantized. The scale is treated as constant during backpropagation, even though it is recomputed for the next step. There is no distillation.

  1. Train without quantization for 1,500 steps (the FP baseline).
  2. PTQ: round that model's weights to the grid at 2, 3 and 4 bits, and measure the loss.
  3. QAT: starting from the same unquantized model, train 2,000 more steps at each bit width with fake-quantized weights and the STE.
Random seed

2-bit weights · 3 levels

–

3-bit weights · 7 levels

–

4-bit weights · 15 levels

–

Simulated and simplified: a 97-parameter network, weights quantized, nothing else. All browser arithmetic uses JavaScript’s 64-bit floating-point numbers; FP labels the unquantized baseline, not an fp32 implementation. Upper chart of each pair: the function each model computes, against the training points (grey). Lower chart: loss during QAT, as mean squared error on a log scale shared by all three pairs; the dashed FP and PTQ losses do not change during QAT. Blue: FP baseline. Orange: PTQ. Green: QAT.

Across these eight seeds, removing bits increases PTQ loss. QAT often reduces that loss by adjusting the master weights and unquantized biases over another 2,000 updates. At 2 bits the grid has three levels, −s, 0 and +s, and the outcome swings from seed to seed. Figure 8 shows all eight seeds at once.

How many times lower QAT's final loss is than PTQ's, for each of the eight seeds, on a log scale. Right of 1×, QAT wins. The seed selected in Figure 7 is drawn in black. At 3 and 4 bits QAT wins on every seed, by 2.8× to 41×. At 2 bits it wins on seven of eight, by anything from 1.1× to 56×, and on seed 3 it ends 2.7× worse.

The lab is far simpler than QAT for a language model: weights only, per tensor, no activation quantization and no distillation. The losses are measured on the training points, so they do not measure generalization. QAT receives 2,000 extra training steps while the FP and PTQ references stay fixed; there is no equally long unquantized continuation for comparison. The experiment demonstrates the mechanism and its possible failures, but does not predict gains for a language model.

Which tensors a transformer quantizes

A transformer keeps most of its parameters in a few kinds of large matrix: the linear projections of the attention and feed-forward (MLP) blocks. In Llama 3 8B these projections hold 87% of the parameters, so they are the first thing to quantize.Computed from the published architecture: 32 layers, model dimension 4,096, feed-forward dimension 14,336 and 8 key/value heads (Llama 3 paper, Table 3), with a 128,256-token vocabulary and separate input and output embeddings (the released model configuration). Total: 8.03 billion parameters. The table shows one illustrative layout, W4A8: 4-bit weights and 8-bit activations. Published recipes differ in which tensors they quantize, at what precision, and how finely they scale them.

TensorPrecision in this exampleShare of Llama 3 8B parametersScale shared by
MLP projections W_gate, W_up, W_down4-bit70.2%a group of 32–128 weights
Attention projections W_q, W_k, W_v, W_o4-bit16.7%a group of 32–128 weights
Token embedding and output headbf1613.1%–
RMSNorm weightsbf16<0.01%–
Activations entering each projection8-bit–one token
KV cache (keys and values of earlier tokens)8-bit––
Attention scores and softmax, residual additionsbf16––

The choices in the example follow from the earlier sections. The projections are large, so 4 bits saves the most memory there, and per-group scales contain outliers. Activations change with every token, so each token gets its own scale. The KV cache grows with context length and can become a large share of memory in long conversations; this layout stores it in 8 bits, and some deployments use 4. Normalization and softmax are cheap and sensitive to rounding, and the residual path is where every layer writes its output, so this layout leaves them in bf16.

Common choices in LLM quantization-aware training

Published recipes

These five recipes differ in bit width, training signal and starting point. Each row links to the paper or official announcement that supports it.

RecipeWhat is quantizedTraining signalReported result
LLM-QAT
Liu et al., 2023
Weights 4-bit, activations 8-bit, KV cache 4- to 8-bitDistillation from the full-precision model on text the model generates itselfNeeds no access to the original training data
BitNet b1.58
Ma et al., 2024
Ternary weights {−1, 0, +1}, 8-bit activationsQAT from scratch, STE throughoutExtreme low-bit weights work when the model is trained for them from the start
EfficientQAT
Chen et al., 2024
2- to 4-bit weights, per-group scalesBlock-wise training of all parameters, then end-to-end training of the scales only2-bit Llama-2-70B trained on one A100-80GB in 41 hours
Gemma 3 QAT
Google, 2025
Weights int4 (Q4_0)About 5,000 steps with the non-quantized checkpoint's probabilities as targets27B weights shrink from 54 GB (bf16) to 14.1 GB; Google reports a 54% reduction in the perplexity degradation from Q4_0 quantization, measured with llama.cpp
ParetoQ
Liu et al., 2025
Binary, ternary, 2-, 3- and 4-bit weightsOne QAT framework with quantizers specific to each bit widthCompares accuracy against model size across these low-bit settings

A practical order follows. PTQ comes first at 8 bits and for weight-only 4-bit deployment, because it needs no retraining. QAT becomes worth its training cost as precision drops, when activations are quantized too, or when PTQ misses the quality target. How much QAT recovers depends on the model, the task, the quantizer, the calibration data, the kernels and the training recipe.

Check your understanding

Why does QAT keep a higher-precision master copy of each weight?

The forward pass uses fake-quantized values, but the optimizer updates master weights kept in fp32 or bf16. Updates too small to change a weight's level accumulate in the master copy until they carry it across a rounding boundary. Applied to the quantized value directly, each small update would be rounded away.

What gradient does this page's STE give a weight outside the clipping range?

Zero. Past α, the quantized value stays at the edge level however far the input moves, and the STE here keeps the derivative of clipping, which is 0 outside the range. Methods that learn the scale add a separate gradient for the scale itself, and other QAT methods use other estimators.

Why does QAT help more at lower precision?

Fewer bits means wider spacing between levels, so rounding each weight independently after training changes the network more. QAT computes the loss with those rounded values and adjusts the remaining parameters, and in some methods the scales, to reduce it. At 8 bits PTQ often leaves little error for QAT to recover.