Before you start
Quantise a weight tensor to 8-bit integers and back, with symmetric per-tensor quantisation.
A float32 weight takes 4 bytes. Stored as int8 it takes 1, so the model is four
times smaller and integer arithmetic can run faster. Symmetric quantisation maps
the range [-max|x|, max|x|] onto the integers [-127, 127] with one scale
factor:
scale = max(|x|) / 127 (use 1.0 if every value is 0)
q = clip(round(x / scale), -127, 127) as int8
x_hat = q * scale
round here is np.round, which rounds halves to the nearest even integer.
Write two functions: quantize_int8(x) returns (q, scale), and
dequantize(q, scale) returns the float approximation x_hat.
def quantize_int8(x):
def dequantize(q, scale):
Input
q, scale = quantize_int8(np.array([0.5, -1.27, 0.0, 1.0]))
dequantize(q, scale)
Output
q = array([ 50, -127, 0, 100], dtype=int8)
scale = 0.01
x_hat = array([ 0.5 , -1.27, 0. , 1. ])
Reasoning
The largest magnitude is 1.27, so scale = 1.27 / 127 = 0.01. Dividing by
the scale gives 50, -127, 0 and 100, which are already whole numbers.
Here the round trip is exact. In general each value can be off by at most
half a scale step.
q must have dtype int8 and the same shape as x.scale is a plain float.scale / 2 for every element.Check runs the visible cases against a fresh kernel. Submit runs the hidden ones too.
⇧↵run cell