The problem
A model's weights are stored as numbers with 16 bits each. Quantization stores each weight as a small integer instead, plus one floating-point scale per group of weights that says how big one integer step is. The model shrinks (4 bits instead of 16 is four times smaller), and generation gets faster, because it is limited by reading memory. The price is a rounding error.
For bits, symmetric absmax quantization of a group of weights works like this:
and each weight is stored as , an integer between and . To use the weight again, dequantize: .
where is the scale: the largest weight of the group (in absolute value) is mapped to , the others fall in between.
Example. One group of 4 weights, bits: .
- and .
- , rounded to : small integers.
- Dequantized: . The errors, , are never above .
With : , , and the dequantized weights are : more bits, smaller error.
Groups
A single large weight makes large and ruins the precision of all the others in its group (they all round to 0). So the weights are cut into groups of group_size consecutive values, each with its own scale: an outlier only damages its own group. Storage is bits per weight plus one scale per group.
Example. 7 billion weights, 4 bits, groups of 128, scales of 2 bytes: GB (gigabytes) for the integers, plus scales of 2 bytes, GB: GB in total, against GB in 16 bits.
What to implement
For a weight array w of any shape whose size is divisible by group_size:
quantize(w, bits, group_size): flattenw, cut it into consecutive groups ofgroup_sizevalues, and return(q, scales):qannp.int8array of shape(n_groups, group_size),scalesa float array of shape(n_groups,).dequantize(q, scales, shape): the reconstructed float array, reshaped toshape.quantized_bytes(n_weights, bits, group_size, scale_bytes=2): the storage size:bitsper weight (rounded up to whole bytes in total) plus one scale per group.
Requirements
- A group of all zeros has scale 0: its weights must come back as 0, without dividing by zero.
bitsis 8 or 4 (4-bit values are still stored innp.int8here; real kernels pack two per byte).- No Python loops over weights or groups.
Hint
w.reshape(-1, group_size) makes one row per group, and np.abs(groups).max(axis=1) gives the largest weight of each. To divide each row by its own scale, use scales[:, None].