Re-quantization
详解“重量化”操作¶
1. 为什么需要“重量化”?(背景与动机)¶
在你的设计中,卷积层和全连接层的输入、权重都是 INT8(8位整型)。
- 当你执行一次乘法时:INT8 × INT8 = INT16。
- 卷积操作包含了大量的乘加运算(MAC)。比如在你的 Conv1 中,一个输出点需要进行 \(1 \times 11 \times 7 = 77\) 次乘加。多个 INT16 累加在一起,如果还用 INT16 存,必然会溢出(Overflow)。
- 因此,硬件上必须使用更宽的寄存器(规范中要求是 INT32)来保存累加的中间结果(Partial Sum)。
问题来了:如果当前层的输出是 INT32,那喂给下一层的输入就变成 INT32 了。下一层如果做 INT32 × INT8 的乘法,硬件乘加器(MAC)的面积和功耗将呈指数级爆炸,完全违背了课程要求的 PPA(功耗、性能、面积)指标。
解决办法:在把结果传给下一层(或存入SRAM)之前,必须把 INT32 重新缩放(映射)回 INT8。这个过程就叫 重量化(Re-quantization)。
2. 数学原理与硬件设计的妥协¶
在算法端(比如 TensorFlow/PyTorch),重量化通常是一个简单的浮点数乘法:
这里的 \(M\) 是一个浮点数(通常是介于 0 到 1 之间的小数,比如 0.00345),由算法训练和量化过程决定。
从数字芯片设计的角度看:
如果在芯片里放一个浮点乘法器来算这个 \(M\),面积和功耗代价极大。数字逻辑设计最讨厌浮点数,最喜欢的是整数乘法和移位(Shift)。
因此,规范中给出了定点化实现公式:
也就是说,我们用一个整数 \(M_0\)(INT16) 配合向右移位(除以 \(2^n\)),来逼近那个浮点数 \(M\)。
所以最终在硬件中实际执行的公式是:
3. 详细举例说明¶
假设算法工程师告诉你,当前卷积层算完后,要把 INT32 压回 INT8,给出的浮点比例系数是 \(M = 0.0054321\)。
Step 1: 助教/算法端的参数转换(软件提前做好的事)¶
助教会帮你把 \(M = 0.0054321\) 转换成硬件友好的 \(M_0\) 和 \(n\)。
假设选择右移 \(n = 16\) 位(即除以 \(2^{16} = 65536\)):
所以,助教提供给你的参数就是:\(M_0 = 356\) (INT16), \(n = 16\)。
(注意:\(356 \div 65536 = 0.005432128\),这与原始浮点数极为接近,产生的误差在神经网络中完全可接受。)
Step 2: 硬件执行重量化(你在Verilog里要做的事)¶
假设你的 MAC 算完之后,某个通道输出的累加结果是:\(I_{INT32} = 15000\)。
- 整数乘法(Multiplier):
$\(15000 (\text{INT32}) \times 356 (\text{INT16}) = 5340000 (\text{内部通常用 48 位 wire 保存})\)$
二进制:5340000 = 48'b000...0101_0001_0111_1000_0000_0000_0000
-
移位操作(Shift):
规范要求右移 \(n=16\) 位。在 Verilog 中,右移 16 位就是直接截断低 16 位(如果是固定值 \(n\),在硬件上连逻辑门都不需要,就是单纯的连线变换 / Wire Routing,完全不消耗面积!):
5340000 >> 16
相当于截掉低 16 位,剩下:1010001(二进制),也就是十进制的 \(81\)。 -
结果验证:
我们用纯浮点算一遍:\(15000 \times 0.0054321 = 81.4815\)。
硬件定点算出来是 \(81\)。两者完美契合!这个 \(81\) 就可以用一个 INT8 寄存器存下来,作为输入传给下一层(或者去过 ReLU 激活函数)了。
4. 总结:作为数字 IC 工程师,你在写 Verilog 时需要注意什么?¶
如果你在写 Verilog RTL 代码,对于重量化模块,你需要实现以下逻辑:
- 乘法器:例化一个
32-bit × 16-bit的有符号乘法器($signed(I_INT32) * $signed(M0))。 - 移位逻辑:根据助教给的 \(n\),进行算术右移
>>> n。 - 饱和截断(Saturation / Clipping)(重点!高分项!):
虽然理论上算出来应该在 INT8 范围内,但实际硬件计算中,经过乘法和移位后的结果仍可能超出[-128, 127]的有符号 INT8 范围。作为严谨的设计,你需要加一个钳位电路: - 如果结果
> 127,强制输出127。 - 如果结果
< -128,强制输出-128。
一句话总结:
重量化操作,就是为了防止中间数据位宽越积越大拖垮硬件硬件,利用“乘以一个整数再截掉低几位(右移)”这种极具硬件性价比的方式,把 32 位大数据重新压缩回 8 位数据的过程。
希望这个解释对你接下来的 RTL 编码有帮助!祝你在课程设计中成功流片(跑通综合和物理设计),拿到高分!如果对具体的 Verilog 实现(如符号位扩展、饱和截断怎么写最高效)有疑问,随时可以继续探讨。
Enjoy Reading This Article?
Here are some more articles you might like to read next: