专栏算法工具链【J6】J6工具链量化简介与代码实操

【J6】J6工具链量化简介与代码实操

Jade-self2025-05-27
216
0

1. 量化简述

1.1 定义

将网络参数从 32 位浮点数据映射到更低位数(int16/int8/int4等)的数据,这个过程称之为量化。反之,称之为反量化。

量化本质上是对数值范围的重新调整,可以「粗略」理解为是一种线性映射。(之所以加「粗略」二字,是因为有些论文会用非线性量化(对数量化等),但目前在工业界落地的还都是线性量化(对称量化、非对称量化、二值化等),地平线采用的主要是线性量化中的对称量化。

Description

反量化一般没有信息损失,而量化一般会有精度损失。这是由于float32 能保存的数值范围比 uint8 多,因此必定有大量数值无法用 uint8 表示,只能四舍五入成 uint8 类型的数值,继而引起量化误差。

Description

量化的可行性依据:神经网络具有良好的鲁棒性,将高精度模型量化到低精度模型,这个过程可以认为是引入了噪声,而模型对噪声相对不敏感,因此量化后的模型也能保持较好的精度。

量化的目的:降低计算复杂度,提高模型推理速度,降低存储体积,减少计算能耗。在一些对能耗和时间要求更高的场景下,量化是一个必然的选择。

1.2 浮点/定点转换公式

用 r 表示浮点实数,q 表示量化后的定点整数。浮点和整型之间的换算公式为:

Description

其中,S 是 scale,表示实数和整数之间的比例关系,Z 是 zero point,表示实数中的 0 经过量化后对应的整数,它们的计算方法为:

Description

其中,$r_{min}$ 、$r_{max}$ 分别是 浮点实数r 的最小值和最大值, $q_{min}$ 、$q_{max}$ 分别是 定点整数q 的最小值和最大值。

重点解释:定点整数的 zero point 代表浮点实数的 0,二者之间的换算不存在精度损失,这一点可以从公式 (2) 中看出来,把 r=0 代入后就可以得到 q=Z。这么做的目的是为了在 padding 时保证浮点数值的 0 和定点整数的 zero point 完全等价,保证定点和浮点之间的表征能够一致。

对称/非对称量化:当实数中的0量化后对应整数Z也是0时,称之为对称量化,否则,为非对称量化。对称量化相比于非对称量化的精度可能要差一些,但速度会快一些,原因可见公式(7),将公式中的Z置零。

1.3 矩阵运算的量化

卷积网络中的卷积层和全连接层本质上都是一堆矩阵乘法,下面我们来看一看如何将矩阵中的浮点运算转换为定点运算。

假设$r_1$、$r_2$ 是浮点实数上的两个N×N的矩阵,$r_3$ 是$r_1$、$r_2$ 相乘后的矩阵,矩阵相乘可表示为:

Description

假设$S_1$、$S_2$ 是$r_1$矩阵对应的 scale 和 zero point,$S_2$ 、$Z_2$ 、$S_3$ 、$Z_3$ 同理,那么由 (5) 式可以推出:

Description

整理一下可以得到:

Description
观察 (7) 式可以发现,除了 $S_1S_2/S_3$ ,其它都是定点整数运算。
那如何把 $S_1S_2/S_3$也变成定点运算呢?

假设$M=S_1S_2/S_3$,由于M通常都是 (0, 1) 之间的实数 (这是通过大量实验统计出来的),因此可以表示成$M=2^{−n}M_0$,其中 $M_0$ 是一个定点实数。注意,定点数并不一定是整数,所谓定点,指的是小数位数是固定的。

因此,如果存在$M=2^{−n}M_0$,我们就可以通过 $M_0$ 的 bit 位移操作实现$2^{−n}M_0$,这样整个矩阵计算过程就都在定点上计算了。

本节主要参考链接https://zhuanlan.zhihu.com/p/149659607

2. J6工具链中的量化

从J6 OE3.0.31版本开始,量化方式有所调整,主要影响QAT算法侧与UCP软件侧。修改内容:QAT从floor(x_data + 0.5)变成nearbyint(x_data),可以理解为“向最近偶数舍入”,称之为“round-half-to-even”。

下面主要介绍J6工具链OE3.0.31及以后量化公式写法,并简单介绍反量化节点的实现。

量化Quantize节点用于将模型 float 类型的数据量化至int类型,下面先对量化的公式和代码进行介绍。

2.1 numpy实现

输出:

注意:np.round在 .5 的时候会“向最近偶数舍入”(2.5 → 2,1.5 → 2),其他写法时注意也要这样。

解释:NumPy 默认采用的是 "round half to even"(银行家舍入法)

2.2 Pytorch实现

输出:

注意:torch.round 默认是 round-half-to-even

2.3 C++实现

编译运行:

输出:

注意:不能使用std::round,需要使用std::nearbyint,std::nearbyint可控制舍入模式(默认是“银行家舍入”)。

补充:如果使用如下写法也是可以的:

3. J6工具链中的反量化

反量化Dequantize节点用于将模型中 int8 或 int32 类型的数据反量化回 float 类型,其计算公式如下:

如下为Dequantize节点的C++的实现代码:

算法工具链
社区征文征程6杂谈技术深度解析
评论0
0/1000