- Key idea
- Salient weight channels chosen by activation size, per-channel scaling before quantization, a search over one exponent α
- Real world
- An invoice where every unit price is rounded to the nearest £10. For an item bought once, that’s fine. For screws bought 1,000 at a time at £0.40 each, rounding to £0 loses £400. Price them per box of 100 instead (£40 a box, 10 boxes) and the same rounding costs at most £50.
- In an LLM
- In a 4-bit Llama layer, about 1% of input channels see activations far larger than the rest. Keeping just those weights in 16-bit recovers most of the lost quality. AWQ gets the same effect while keeping every weight 4-bit: it multiplies those channels’ weights by a factor s and divides their inputs by s.
Not all weights are equal
The error a rounded weight causes in the output is its rounding error times the input it multiplies:
So a weight that always meets a large input matters far more than one that meets a small input. AWQ (activation-aware weight quantization) measures this directly: run calibration text through the model, and call an input channel salient if its average activation magnitude is large. In LLMs a small fraction of channels, around 1%, stand out.
The paper’s first experiment is striking: keep just those 1% of weight channels in FP16 and quantize the rest to 4 bits, and most of the quality lost by round-to-nearest comes back. Choosing the 1% by weight size instead barely helps. It’s the activations that tell you what matters.
Scaling instead of mixed precision
Keeping some weights in 16-bit and the rest in 4-bit is awkward for hardware: kernels want every weight in a group stored the same way. AWQ uses the same identity as SmoothQuant instead. For a salient input channel :
Multiply the weight by before quantizing and divide its input by . The rounding error of is about the same absolute size as before (the grid step barely changes as long as isn’t the largest weight in its group), but after dividing by , its effect on the output shrinks by a factor of . The division of the input is folded into the previous operation, so it’s free at run time.
Push too far and becomes the largest weight in its group. Then it sets the group’s scale, the grid gets coarser for every other weight, and the error goes back up. There is a sweet spot.
Try it
The invoice rounds unit prices to the nearest £10. Screws cost £0.40 and you buy 1,000: rounded to £0, the invoice is £400 short. Re-list them as boxes of 100 at £40 a box, 10 boxes. Rounding £40 to £40 costs nothing here, and even a bad case (£45 → £50) is £5 a box, £50 in total. Same purchase, same rounding rule; you just moved a factor of 100 from the quantity to the price. That’s AWQ’s : move a factor from the activation into the weight, where the rounding happens.
In the widget, channel c3 sees activations 8× larger than the others. Without scaling it causes most of the layer’s output error, although its weights are ordinary. Scaling it by about 2.5 cuts the total error to under 40% of where it started, and every weight is still a 4-bit integer with one scale per group. Push past about ×5 and the layer becomes worse than with no scaling at all. Keeping c3 entirely in 16-bit (the dashed line) does better still, but that needs a second storage format inside the same layer.
Searching for s
AWQ doesn’t tune each channel’s separately. It uses one formula with one knob:
where is channel ’s average activation magnitude on the calibration data. means no scaling; larger scales salient channels more. AWQ tries a grid of values of between 0 and 1 for each layer, quantizes, measures the layer’s output error on calibration data, and keeps the best. No gradients, no weight updates beyond the scaling.
That simplicity is a feature. Because AWQ never fits the weights to the calibration set (unlike GPTQ, which adjusts weights to cancel errors on it), it is less sensitive to which calibration data you use, and generalises well to other domains and to instruction-tuned and multimodal models.
In practice
- AWQ is weight-only: weights in 4-bit (usually group size 128), activations stay in 16-bit (W4A16). It speeds up generation because decoding is limited by reading weights from memory: see what to quantize.
- AWQ checkpoints are served by vLLM, SGLang, TGI and TensorRT-LLM. The original AutoAWQ library is no longer maintained; its functionality moved into llm-compressor.
- AWQ and GPTQ can be combined: apply AWQ’s scaling first, then GPTQ’s error feedback. Many toolkits offer both as steps in one recipe.
Where this shows up
- The same scaling identity used for activations: LLM.int8() and SmoothQuant.
- Error feedback instead of scaling: GPTQ.
- llama.cpp’s importance matrix also weights errors by activation size: GGUF.
Why scaling helps
For a group of weights quantized with step , the error on weight is , where is the rounding error in units of the step (roughly uniform on ). Its contribution to the output is . After scaling,
If scaling one weight doesn’t change the group maximum, and has the same distribution as , so the expected error on that channel falls by a factor of . Once exceeds the group maximum, grows with and every other weight in the group gets worse.
The search
AWQ minimises the output error of each linear layer on calibration inputs :
with the per-channel average activation magnitude. The paper searches 20 values of . Weight clipping (shrinking each group’s maximum slightly, as in outliers and clipping) is searched the same way.
In papers
- AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration (Lin et al., 2023). Also introduces TinyChat, a 4-bit inference engine for edge devices.
- SqueezeLLM (Kim et al., 2023) and SpQR (Dettmers et al., 2023) take the other route: keep the few sensitive weights in high precision as a sparse matrix next to a low-bit dense one.