【论文阅读笔记】Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference

时间:2024-04-03 17:09:57

该方法的简称为:IAO

  该论文提出了一种允许网络在推测时只使用整数计算的方法,即 float32 --> int8.
  该文在MobileNets等已经压缩过的网络进行量化,测试数据集为ImageNet分类数据集。不同于其他的方法,在高度冗余的模型,如Alexnet等网络上进行的,且提出的硬件加速方法需要专门的特殊硬件(即他们并没有在真正的硬件上加速,只是提出一种理论),该方法在普通的CPU上即可运行。


Introduction

  以前的方法在两方面减少modle size和推测时间:

  1. 网络结构上,如MobileNet、SqueezeNet、ShuffleNet和DenseNet
  2. 量化权重和**函数,从32 bit的floating point到更低的 bit-depth表示

  但是之前的方法有两个方面的缺陷:

  1. 之前的方法没有在一个合理的基准结构上评估,他们大部分的基准结构普遍在AlexNet,VGGnet,GoogleNet上,这些网络在设计时,为了达到高准确率,有很大的冗余,所以在压缩这些网络时有不小的效果
  2. **很多量化放大没有真正在硬件上进行有效性证明。**① 有的方法只在weight上量化,仅仅在乎在设备的存储,而不在乎计算效率;② 有的方法,比如二元/三元权重网络和bit-shift网络,他们的方法把权重限制为0或者2n,他们想把乘法计算用bit-shift实现。但是,在现有的硬件上,bit-shift实现并不比<乘法-加法>好。而且,只有当bit较大时,乘法才显得“昂贵”,所以,我们需要将乘法的输入—weight和activation都给量化成较小的bit-width。

Quantized Inference

Quantized scheme

  作者的量化方案为:在测试时只使用int计算,在训练时使用浮点计算。
  量化方案的基本要求就是:允许只用量化值的整数运算来完成所有的算术运算。
  量化方案是一个仿射映射,在整数qq和实值rr(1)r=S(qZ)r = S(q-Z) \tag{1}  其中SZS和Z是常数SZS和Z是量化参数)【对每一层array用一对S/Z参数,即每一个activation、每一个weight的Tensor都有一个对应S/Z】

  对于8-bit的量化,q就是8-bit的整数。对于B-bit的量化,q就是B-bit的实数。
  而对于bias,就固定量化为32-bit的实数。

  常数SSstep_size,代表一个“scale”,是一个实数。常数ZZ是偏移shift,代表“zero-point”,就是为了让实数rr00和量化数qq00对应,和q是一个类型的数。(9)S=arraymaxarraymin2bit_width1S=\frac{array_{max}-array_{min}}{2^{bit\_width}-1}\tag{9}设定:round(x)={0,x&lt;0x0&lt;x&lt;2n12n1,x&gt;2n1round(x) = \begin{cases}0,&amp;x&lt;0 \\ \lfloor x \rceil, &amp;0&lt;\lfloor x \rceil&lt;2^{n}-1 \\ 2^{n}-1, &amp;x&gt;2^{n}-1\end{cases}
(10)Z=round(arrayminS)Z=round(-\frac{array_{min}}{S})\tag{10}

从公式(1)(1)可以推出:(11)q=round(Z+rS)q = round(Z + \frac{r}{S}) \tag{11}

template<typename QType>                    // e.q.  QType=uint8
struch QuantizedBuffer{
	vector<QType> q;                        // the quantized values
	float S;                                // the scale
	QType Z;                                // the zero-point
}

量化卷积计算过程

1. 输入 量化的特征图 lhs_quantized_val, uint8类型, 偏移量 lhs_zero_point, int32类型;
2. 输入 量化的卷积核 rhs_quantized_val, uint8类型, 偏移量 rhs_zero_point, int32类型;
3. 转换 unit8 到 int32类型;
4. 每一块卷积求和(int32乘法求和有溢出风险,可换成固定点小数树乘法);
   int32_accumulator += (lhs_quantized_val(i, j) - lhs_zero_point) * (rhs_quantized_val(j, k) - rhs_zero_point);
5. 输入 量化的乘子 quantized_multiplier, int32类型 和 右移次数记录 right_shift, int类型;
6. 计算乘法,得到int32类型的结果 (int32乘法有溢出风险,可换成固定点小数树乘法);
   quantized_multiplier * int32_accumulator
   
7. 再左移动 right_shift 位还原,得到 int32的结果;
8. 最后再加上 结果的偏移量 result_zero_point;
   (7和8的顺序和 官方说的先后顺序颠倒);
9. 将int32类型结果 限幅到[0, 255], 再强制转换到 uint8类型;
10. 之后再 反量化到浮点数,更新统计输出值分布信息 max, min;
11. 再量化回 uint8;
11. 之后 经过 量化**层;
12. 最后 反量化到 浮点数,本层网络输出;

13. 进入下一层
    循环执行 1~12 步骤

  若有连续的层需要量化操作时,就没必要进行反量化了。。比如上面步骤的 101110\rightarrow11,实际上是可以省略的【但相应的。。可能会带来乘法加法累积造成的溢出】
【论文阅读笔记】Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference
Integer-arithmetic-only matrix multiplication

  由公式(1)(1)可以知道,每个array中的实数 rir_i 都能表示带有一对参数SZS和Z的实数qiq_i。则对实数矩阵R1R2R_1和R2的乘法,其结果矩阵的每个实数可以表示为:(2)S3(q3(i,k)Z3)=j=1NS1(q1(i,j)Z1)S2(q2(j,k)Z2)S_3(q_3^{(i,k)}-Z_3)=\sum_{j=1}^{N}S_1(q_1^{(i,j)}-Z_1)S_2(q_2^{(j,k)}-Z_2)\tag{2} (3)q3(i,k)=Z3+Mj=1N(q1(i,j)Z1)(q2(j,k)Z2)q_3^{(i,k)}=Z_3+M\sum_{j=1}^{N}(q_1^{(i,j)}-Z_1)(q_2^{(j,k)}-Z_2)\tag{3}其中,(4)M=S1S2S3M=\frac{S_1S_2}{S_3}\tag{4}MM是公式(3)(3)中唯一不是实数的值。经验发现,MM总是在(0,1)(0,1)中,所以将MM表达为:(5)M=2nM0M=2^{-n}M_0\tag{5}  其中,nn为非负整数,M0M_0是一个整数。这样,实数运算就变成了整数运算。同时,2n2^{-n}可以用移位运算。因为图片输入的每一个数字都在[0,255][0,255],而映射的区域也是[0,281]=[0,255][0, 2^8-1]=[0,255]

注意,在预测时,权重矩阵的量化step_size,可以通过已有参数统计出来,而activation的量化step_size是大量训练数据的指数移动均值统计出来的。所以,才会有q3q_3没出来,但应用了S3S_3的公式出现。【下面会有S的计算公式,是通过映射前的range的最小值aa和最大值bb出来的】

Implementation of a typical fused layer

  定义bias-vector的量化:Sbias=S1S2Zbias=0S_{bias}=S_1S_2,Z_{bias}=0其中,SbiasS_{bias}用int32表示。
  矩阵乘法,weights和activations的矩阵乘法后的加法:int32+=uint8uint8int32 += uint8 *uint8
  使用32位加法器得结构后,还有3件事情需要做:① 将这个output activations的int32 scale downint8scale指标;② 将 int32 的output activations 仿射到 uint8;③ 对这个uint8的结果应用**函数。
  这个乘法的down-scaling就是公式(3)(3)的MM.


Training with simulated quantization

Quantized scheme

  作者分析原有的量化方法:用floating point训练,然后量化训练的weight.(有时候会进行量化后的微调),发现这种方法对于大模型来说结果不错,对小模型来说精确度下降不小。作者对这种量化后的微调方法分析了存在的两点问题:

  1. 同一layers不同channels权重分布尺度差很多(100x)
  2. 量化会带来一些“参数丢失”,即量化了就丢失了精度(废话。。)

  作者提出了一种在前向传播阶段模拟量化的方法,反向传播和平常一样,所有的权重和biases都用floating point保存以微调小变化。前向传播的浮点数计算方法,模拟量化的方式,就如公式(3)(3)所示

  对每一层,数值都用参数(量化层次,和clamping range)量化:(6)clamp(r;a,b):=min(max(x,a),b)clamp(r;a,b) := min(max(x,a),b)\tag{6}(7)s(a,b,n):=ban1s(a,b,n):=\frac{b-a}{n-1} \tag{7}(8)q(r;a,b,n):=clamp(r;a,b)as(a,b,n)s(a,b,n)+aq(r;a,b,n):=\lfloor\frac{clamp(r;a,b)-a}{s(a,b,n)}\rceil s(a,b,n)+a\tag{8}其中,rr是待量化的实数;[a,b][a,b]是量化的范围;nn是量化的等级数,对所有layer都是固定的,比如8-bit量化,n=28=256n=2^8=256\lfloor * \rceil就是取整到最近的整数。
  公式(6)(6)表示x所处的范围,即映射前的range,之所以有这个看起来没用的公式,是因为activation的range是移动均值平均的出来的;公式(7)(7)表示range映射到n-1段,每段的大小;公式(8)(8)表示一个实数r就近到能完整量化的实数,且clamp(r;a,b)as(a,b,n)\lfloor\frac{clamp(r;a,b)-a}{s(a,b,n)}\rceil是能我们的B-bit表示的量化结果;

  bias没有被量化,因为在推测阶段,它被32-bit表示。【这句没懂,因为前面不是有量化bias的参数SbiasS_{bias}吗?】

Learning quantization ranges
  weight 和 activation的量化range不同:

  • 对于weight:a:=min(w)a:=min(w)b:=max(w)b:=max(w)
  • 对于activation:在训练时,用指数移动平均来收集activation的range [a,b],平滑参数接近1.【若范围快速变化时,指数移动平均会延迟反映出来,所以在训练初期(5万步到200万步)时,不量化activation是有用的】