Input(activation) quantization

username: ingu_jeong
cooked: <p>Hi, all.<br>
I have some questions about the quantization.<br>
When I quantize a model, the following results is obtained.<br>
I know that scale and zero_point in torch,per_channel_affine can make int8 weight.<br>
Then, what do fc1.scale and fc1.zero_point mean?<br>
Also, I wonder what zero_point and scale are used to quantize the input(activation).</p>
<pre><code class="lang-auto">model.qconfig = torch.quantization.get_default_qat_qconfig("fbgemm")
model_q = torch.quantization.prepare_qat(model, inplace=False)
model_q.load_state_dict(torch.load('220307_quant_mnist.pt'))
model.eval().to('cpu')
model_int8 = torch.quantization.convert(model_q, inplace=False)
torch.save(model_int8.state_dict(), './220307_quantized_mnist.pt')
test()
params = torch.load('220307_quantized_mnist.pt', map_location = "cpu")
print(params)
print(params['fc1._packed_params._packed_params'][0].int_repr())
</code></pre>
<pre><code class="lang-auto">OrderedDict ([('fc1.scale', tensor(8.2687topic deleted by author)), ('fc1.zero_point', tensor(44)), ('fc1._packed_params.dtype', torch.qint8), ('fc1._packed_params._packed_params', (tensor([[-0.5753, -0.6056, -0.6056,  ..., -0.5753, -0.6056, -0.6056],
        [ 0.0289,  0.0289,  0.0000,  ...,  0.0867,  0.0867,  0.0289],
        [ 0.2502,  0.0834,  0.0556,  ...,  0.0556,  0.1390,  0.0556],
        ...,
        [-0.0295, -0.0295,  0.0000,  ...,  0.0000,  0.0000, -0.0295],
        [-0.0677,  0.0000, -0.0677,  ..., -0.0338,  0.0000, -0.0677],
        [ 0.0000,  0.0000,  0.0625,  ...,  0.0937,  0.0937,  0.0000]],
       size=(512, 784), dtype=torch.qint8,
       quantization_scheme=torch.per_channel_affine,
       scale=tensor([0.0303, 0.0289, 0.0278, 0.0299, 0.0323, 0.0261, 0.0319, 0.0226, 0.0196,
        0.0299, 0.0369, 0.0324, 0.0264, 0.0330, 0.0228, 0.0257, 0.0376, 0.0149,
        0.0333, 0.0402, 0.0261, 0.0377, 0.0397, 0.0280, 0.0422, 0.0287, 0.0244,
        0.0251, 0.0341, 0.0225, 0.0246, 0.0277, 0.0177, 0.0381, 0.0314, 0.0341,
        0.0274, 0.0269, 0.0248, 0.0344, 0.0365, 0.0262, 0.0267, 0.0302, 0.0271,
        0.0299, 0.0246, 0.0228, 0.0263, 0.0267, 0.0329, 0.0165, 0.0367, 0.0304,
</code></pre>

username: HDCharles
cooked: <p>most quantized ops for static quantizaztion take as an input:</p>
<ol>
<li>qint8 activation</li>
<li>a packedparams object (which is essentially the weight and bias)</li>
<li>a scale</li>
<li>a zero_point.</li>
</ol>
<p>It then uses the activation and packedparams to calculate the output which is quantizes using the scale and zero point to give a qint8 output. An example of this can be seen with quantized linear: <a href="https://github.com/pytorch/pytorch/blob/39470387cf17896cf38f3e04b5b81d4647734275/torch/nn/quantized/modules/linear.py#L161" class="inline-onebox" rel="noopener nofollow ugc">pytorch/linear.py at 39470387cf17896cf38f3e04b5b81d4647734275 · pytorch/pytorch · GitHub</a></p>
<p>so the fc1 scale and fc1 zero_point are just the output quantization parameters.</p>

username: ingu_jeong
cooked: <p>Oh, Thanks for reply my question.<br>
I understand fc1 scale and fc1 zero_point.<br>
Then, how are qint8 activations quantized??</p>

username: HDCharles
cooked: <p>Well lets say we have a bunch of quantized ops, the activation to op 2 is quantized because its just the output for op 1. The same is true for all op n and n-1. This leaves op 1 having a non quantized input but a quantized output. If you look at the various tutorials, you have to add a QuantStub() at the start and a DeQuantStub() at the end of your model which has the purpose of the initial quantize and final dequantize steps for the activations. During the convert step those are converted to quantize_per_tensor/_per_channel depending on your quantization scheme.</p>

username: ingu_jeong
cooked: <p>Oh, Thanks to your clear explanation, I understood perfectly.</p>