<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Quantization in Depth on 笔记本子</title><link>https://tianshihao.github.io/series/quantization-in-depth/</link><description>Recent content in Quantization in Depth on 笔记本子</description><generator>Hugo -- gohugo.io</generator><language>cn</language><copyright>Copyright © 2026</copyright><lastBuildDate>Wed, 02 Sep 2026 13:55:10 +0800</lastBuildDate><atom:link href="https://tianshihao.github.io/series/quantization-in-depth/index.xml" rel="self" type="application/rss+xml"/><item><title>Quantization in Depth 3</title><link>https://tianshihao.github.io/posts/quantization-in-depth/03-symmetric-vs-asymmetric-mode/</link><pubDate>Wed, 02 Sep 2026 13:55:10 +0800</pubDate><guid>https://tianshihao.github.io/posts/quantization-in-depth/03-symmetric-vs-asymmetric-mode/</guid><description>&lt;h1 id="quantization-in-depth-3"&gt;Quantization in Depth 3&lt;/h1&gt;
&lt;h2 id="linear-quantization-mode"&gt;Linear Quantization Mode&lt;/h2&gt;
&lt;p&gt;There are two modes in linear quantization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asymmetric&lt;/strong&gt;: Mapping $[r_\text{min}, r_\text{max}]$ to $[q_\text{min}, q_\text{max}]$&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Symmetric&lt;/strong&gt;: Mapping $[-r_\text{max}, r_\text{max}]$ to $[-q_\text{max}, q_\text{max}]$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We don&amp;rsquo;t need to use zero point($z=0$) in symmetric mode.&lt;/p&gt;
&lt;p&gt;This happens because the floating-point range and the quantized range are symmetric with respect to zero.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/symmetric.png" alt="Symmetric"&gt;&lt;/p&gt;
&lt;p&gt;Hence, we can simplify the equations to:&lt;/p&gt;
&lt;p&gt;$$
\begin{cases}
q=int(round(r/s)) \\
s=r_\text{max}/q_\text{max}
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;Trade-off:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Utilization of quantized range:
&lt;ul&gt;
&lt;li&gt;When using asymmetric quantization, the quantized range is fully utilized.&lt;/li&gt;
&lt;li&gt;When symmetric mode, if the float range is biased towards one side, this will result in a quantized range where a part of the range is dedicated to values that we&amp;rsquo;ll never use.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Simplicity: Symmetric mode is simpler compared to asymmetric mode.&lt;/li&gt;
&lt;li&gt;Memory: We don&amp;rsquo;t store the zero-point for symmetric quantization.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;import torch
def get_q_scale_symmetric(tensor, dtype=torch.int8):
r_max = tensor.abs().max().item()
q_max = torch.iinfo(dtype).max
return r_max / q_max
test_tensor = torch.randn((4, 4))
test_tensor&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;tensor([[-1.1284, 2.3800, -2.2940, 0.6971],
[-0.4221, -0.4675, -0.6180, -1.4234],
[ 0.0705, 1.3060, -1.2461, 0.2461],
[-2.6319, 1.3008, 0.9376, 0.7069]])&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;get_q_scale_symmetric(test_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;0.020723763413316623&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;from helper import linear_q_with_scale_and_zero_point
def linear_q_symmetric(tensor, dtype=torch.int8):
scale = get_q_scale_symmetric(tensor, dtype=dtype)
quantized_tensor = linear_q_with_scale_and_zero_point(tensor, scale, zero_point=0, dtype=dtype)
return quantized_tensor, scale
quantized_tensor, scale = linear_q_symmetric(test_tensor)
from helper import linear_dequantization, plot_quantization_errors
from helper import quantization_error
dequantized_tensor = linear_dequantization(quantized_tensor, scale, 0)
plot_quantization_errors(test_tensor, quantized_tensor, dequantized_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;p&gt;这里补充图像&lt;/p&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;printf(f&amp;#34;&amp;#34;&amp;#34;Quantization Error : \
{quantization_error(test_tensor, dequantized_tensor)}&amp;#34;&amp;#34;&amp;#34;)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;p&gt;这里应该输出一个Quantization Error的值&lt;/p&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;Quantization Error :&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;</description></item><item><title>Quantization in Depth 2</title><link>https://tianshihao.github.io/posts/quantization-in-depth/02-scale-and-zero-point/</link><pubDate>Thu, 27 Aug 2026 14:12:40 +0800</pubDate><guid>https://tianshihao.github.io/posts/quantization-in-depth/02-scale-and-zero-point/</guid><description>&lt;h1 id="quantization-in-depth-2"&gt;Quantization in Depth 2&lt;/h1&gt;
&lt;h2 id="get-the-scale-and-zero-point"&gt;Get the scale and zero point&lt;/h2&gt;
&lt;p&gt;Linear quantization maps the floating point range $[r_{min}, r_{max}]$ to the quantized range $[q_{min}, q_{max}]$.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/scale-and-zero-point.png" alt="Scale and zero point"&gt;&lt;/p&gt;
&lt;p&gt;If we look the extreme values, we could get:&lt;/p&gt;
&lt;p&gt;$$
\begin{cases}
r_{min} = s(q_{min} - zero_ point) \\
r_{max} = s(q_{max} - zero_ point)
\end{cases}
$$&lt;/p&gt;
&lt;p&gt;Substracting the first equation from the second, we could get the scale $s$:&lt;/p&gt;
&lt;p&gt;$$
r_{max} - r_{min} = s (q_{max} - q_{min}) \\
$$
$$
s = (r_{max} - r_{min})/(q_{max} - q_{min})
$$&lt;/p&gt;
&lt;p&gt;For the zero point $z$, we need to round the value since it is an integer:&lt;/p&gt;
&lt;p&gt;$$
z = round(q_{min} - \frac{r_{min}}{s})
$$&lt;/p&gt;
&lt;h2 id="why-make-z-an-integer"&gt;Why make $z$ an integer?&lt;/h2&gt;
&lt;p&gt;$$
z = int(round(q_{min} - \frac{r_{min}}{s}))
$$&lt;/p&gt;
&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;p&gt;to be continued&amp;hellip;&lt;/p&gt;
&lt;h2 id="zero-point-out-of-range"&gt;Zero point out of range&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;$z$ &amp;lt; $q_{min}$: set $z$ = $q_{min}$&lt;/li&gt;
&lt;li&gt;$z$ &amp;gt; $q_{max}$: set $z$ = $q_{max}$&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="linear-quantization-scale-and-zero-point-pytorch"&gt;Linear quantization scale and zero point pytorch&lt;/h2&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;import torch
from helper import linear_q_with_scale_and_zero_point, linear_dequantization
test_tensor = torch.tensor(
[[191.6, -13.5, 728.6],
[92.14, 295.5, -184],
[0, 684.6, 245.5]]
)
q_min = torch.iinfo(torch.int8).min
q_max = torch.iinfo(torch.int8).max&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;q_min&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;-128&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;q_max&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;127&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;r_min = test_tensor.min().item()
r_min&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;-184.0&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;r_max = test_tensor.max().item()
r_max&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;728.5999755859375&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;scale = (r_max - r_min) / (q_max - q_min)
scale&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;3.578823433670343&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;zero_point = q_min - (r_min / scale)
zero_point&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;-76.58645490333825&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;zero_point = int(round(q_min - (r_min / scale)))&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;-77&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;def get_q_scale_and_zero_point(tensor, dtype=torch.int8):
q_min, q_max = torch.iinfo(dtype).min, torch.iinfo(dtype).max
r_min, r_max = tensor.min().item(), tensor.max().item()
scale = (r_max - r_min) / (q_max - q_min)
zero_point = q_min - (r_min / scale)
if zero_point &amp;lt; q_min:
zero_point = q_min
elif zero_point &amp;gt; q_max:
zero_point = q_max
else:
zero_point = int(round(zero_point))
return scale, zero_point
new_scale, new_zero_point = get_q_scale_and_zero_point(test_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;3.578823433670343&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;new_zero_point&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;-77&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;quantized_tensor = linear_q_with_scale_and_zero_point(test_tensor, new_scale, new_zero_point)
dequantized_tensor = linear_dequantization(quantized_tensor, new_scale, new_zero_point)
(dequantized_tensor - test_tensor).square().mean()&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;tensor(1.5730)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;from helper import plot_quantization_errors
plot_quantization_errors(test_tensor, quantized_tensor, dequantized_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;img src="images/quantization-errors.png" alt="Quantization errors"&gt;&lt;/p&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;(dequantized_tensor - test_tensor).square().mean()&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;tensor(1.5730)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;def linear_quantization(tensor, dtype=torch.int8):
scale, zero_point = get_q_scale_and_zero_point(tensor, dtype=dtype)
quantized_tensor = linear_q_with_scale_and_zero_point(tensor, scale, zero_point, dtype=dtype)
return quantized_tensor, scale, zero_point
r_tensor = torch.randn((4, 4))
r_tensor&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;tensor([[ 1.6714, -1.3308, 0.4337, 1.0956],
[-1.8622, 2.4572, 1.0099, -1.0139],
[ 0.2309, 1.3375, -0.8019, -0.6140],
[-0.4675, -1.4661, -1.0904, 0.4055]])&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;quantized_tensor, scale, zero_point = linear_quantization(r_tensor)
quantized_tensor&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;tensor([[ 81, -97, 8, 47],
[-128, 127, 42, -78],
[ -4, 61, -65, -54],
[ -46, -105, -82, 6]], dtype=torch.int8)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;scale&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;0.01693895798103482&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;zero_point&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;-18&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;dequantized_tensor = linear_dequantization(quantized_tensor, scale, zero_point)
plot_quantization_errors(r_tensor, quantized_tensor, dequantized_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;img src="images/quantization-errors-random.png" alt="Quantization errors of r_tensor"&gt;&lt;/p&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;(dequantized_tensor - r_tensor).square().mean()&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="text"&gt;&lt;code&gt;tensor(2.7701e-05)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Text&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;</description></item><item><title>Quantization in Depth 1</title><link>https://tianshihao.github.io/posts/quantization-in-depth/01-linear-quantization/</link><pubDate>Tue, 25 Aug 2026 13:36:10 +0800</pubDate><guid>https://tianshihao.github.io/posts/quantization-in-depth/01-linear-quantization/</guid><description>&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Quantization&lt;/strong&gt;: Store the parameters of the model in lower precision&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Knowledge Distillation&lt;/strong&gt;: Train a smaller model (student) using a original model (instructor)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pruning&lt;/strong&gt;: Remove connections (weights) from the model&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;FP32&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sign: 1 bit&lt;/li&gt;
&lt;li&gt;Exponent (range): 8 bits&lt;/li&gt;
&lt;li&gt;Fraction (precision): 23 bits&lt;/li&gt;
&lt;li&gt;Total: 32 bits&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="quantization"&gt;Quantization&lt;/h2&gt;
&lt;p&gt;Quantization refers to the process of mapping a large set to a smaller set of values. For example, mapping the continuous range of real numbers [-234.1, 251.51] to a discrete set of integers [-128, 127].&lt;/p&gt;
&lt;h2 id="neural-network-quantization"&gt;Neural Network Quantization&lt;/h2&gt;
&lt;p&gt;You can quantize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The weights: Neural network parameters&lt;/li&gt;
&lt;li&gt;The Activations: Values that propagate through the layers of the neural network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you quantize the NN after it has been quantized, you are doing &lt;strong&gt;post training quantization&lt;/strong&gt;(PTQ)&lt;/p&gt;
&lt;h2 id="advantages-of-quantization"&gt;Advantages of Quantization&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Smaller model&lt;/li&gt;
&lt;li&gt;Speed gains
&lt;ul&gt;
&lt;li&gt;Memory bandwidth: Less data to transfer&lt;/li&gt;
&lt;li&gt;Faster operations
&lt;ul&gt;
&lt;li&gt;GEMM: General Matrix Multiply&lt;/li&gt;
&lt;li&gt;GEMV: General Matrix Vector Multiplication&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="challenges-of-quantization"&gt;Challenges of Quantization&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Quantization error&lt;/li&gt;
&lt;li&gt;Retraining (Quantization Aware Training, QAT)&lt;/li&gt;
&lt;li&gt;Limited hardware support&lt;/li&gt;
&lt;li&gt;Calibration dataset needed&lt;/li&gt;
&lt;li&gt;Packing/unpacking&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="linear-quantization"&gt;Linear Quantization&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Idea: linear mapping&lt;/li&gt;
&lt;li&gt;Formula:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$$
r=s(q-z)
$$&lt;/p&gt;
&lt;p&gt;$s$ is the scale factor, $z$ is the zero point, $r$ is the real value (e.g. in FP32), $q$ is the quantized value (e.g. in INT8).&lt;/p&gt;
&lt;p&gt;&lt;img src="images/linear-quantization.png" alt="Linear Quantization"&gt;&lt;/p&gt;
&lt;p&gt;Example with $s=2$, and $z=0$:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We got $r=2(q-0)=2q$&lt;/li&gt;
&lt;li&gt;For $q=10$, we have $r=20$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Getting q&lt;/p&gt;
&lt;p&gt;$$
r=s(q-z) \implies q=\frac{r}{s}+z \quad (7.4)
$$&lt;/p&gt;
&lt;p&gt;$$
q=round(\frac{r}{s})+z 7.0
$$&lt;/p&gt;
&lt;p&gt;$$
q=int(round(\frac{r}{s})+z) 7
$$&lt;/p&gt;
&lt;p&gt;PyTorch&lt;/p&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;import torch
def linear_q_with_scale_and_zero_point(
tensor, scale, zero_point, dtype=torch.int8):
scaled_and_shifted_tensor = tensor / scale &amp;#43; zero_point
rounded_tensor = torch.round(scaled_and_shifted_tensor)
q_min = torch.iinfo(dtype).min
q_max = torch.iinfo(dtype).max
q_tensor = rounded_tensor.clamp(q_min, q_max).to(dtype)
return q_tensor
def linear_dequantization(quantized_tensor, scale, zero_point):
return scale * (quantized_tensor.float() - zero_point)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;p&gt;Test case with random scale and zero point:&lt;/p&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;test_tensor = torch.tensor(
[[191.6, -13.5, 728.6],
[92.14, 295.5, -184],
[0, 684.6, 245.5]]
)
scale = 3.5
zero_point = -70
quantized_tensor = linear_q_with_scale_and_zero_point(
test_tensor, scale, zero_point)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="bash"&gt;&lt;code&gt;tensor([[ -15, -74, 127],
[ -44, 14, -123],
[ -70, 126, 0]], dtype=torch.int8)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Bash&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;dequantized_tensor = linear_dequantization(quantized_tensor, scale, zero_point)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="bash"&gt;&lt;code&gt;tensor([[ 192.5000, -14.0000, 689.5000],
[ 91.0000, 294.0000, -185.5000],
[ 0.0000, 686.0000, 245.0000]])&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Bash&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;dequantized_tensor - test_tensor&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="bash"&gt;&lt;code&gt;tensor([[ 0.9000, -0.5000, -39.1000],
[ -1.1400, -1.5000, -1.5000],
[ 0.0000, 1.4000, -0.5000]])&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Bash&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;(dequantized_tensor - test_tensor).square()&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="bash"&gt;&lt;code&gt;tensor([[8.0999e-01, 2.5000e-01, 1.5288e&amp;#43;03],
[1.2996e&amp;#43;00, 2.2500e&amp;#43;00, 2.2500e&amp;#43;00],
[0.0000e&amp;#43;00, 1.9601e&amp;#43;00, 2.5000e-01]])&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Bash&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;(dequantized_tensor - test_tensor).square().mean()&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="bash"&gt;&lt;code&gt;tensor(170.8753)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Bash&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;figure class="tm-figure"&gt;
&lt;pre class="tm-code" data-lang="python"&gt;&lt;code&gt;from helper import plot_quantization_errors
plot_quantization_errors(test_tensor, quantized_tensor, dequantized_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;span class="tm-lang" aria-hidden="true"&gt;Python&lt;/span&gt;
&lt;button class="tm-copy" type="button"&gt;copy&lt;/button&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;img src="images/linear-quantizaiton-errors-with-random-scale-zero-point.png" alt="Linear quantizaiton errors with random scale, zero point"&gt;&lt;/p&gt;</description></item></channel></rss>