Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference

Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference

量化感知论文

Jacob, Benoit, et al. “Quantization and training of neural networks for efficient integer-arithmetic-only inference.” Proceedings of the IEEE conference on computer vision and pattern recognition. 2018. citations:2302

解决什么问题

解决在精度和端侧延迟要求的前提下,如何进行模型压缩、模型量化的问题,使之能用在端侧。

提出什么方法

提出一种量化方案,能实现推理过程是全整数计算的。

同时提出一种训练流程,以保持端到端模型量化后的准确性。

效果如何

improving the latency-vs-accuracy tradeoffs of MobileNets on common mobile hardware ,端侧用的手机,即使在mobilenet这种实时率很高的小模型上也是有改进的。实验用CPU运行imagenet分类和coco目标检测证明了。

还存在什么问题

贡献

  • 提供了一种量化方案,将权重和激活量化为8位整数,仅将几个参数(偏差向量)量化为32位整数;
  • 提供了一个量化的推理框架,它可以有效地在只有整数运算的硬件上实现,比如高通的Hexagon,在ARM NEON上展示了高效、准确的实现。
  • 提供一个量化的训练框架,共同设计了一个量化推理过程,以最大限度地减少量化对实际模型的精度损失。
  • 将我们的框架应用于基于MobileNets的高效分类和检测系统,并在流行的ARM cpu上提供基准测试结果。在最先进的MobileNet架构中,延迟与精度之间的权衡有了显著的改善,在ImageNet分类、COCO目标检测等任务中得到了证明。
  • 提出的量化方案侧重于提高移动cpu上的推理速度和准确性的权衡。

背景

在最小精度损失的情况下减少cnn的模型大小和推理时间,大致分为两类:

  1. 第一类,修改模型结构:代表为MobileNetb、SqueezeNet、ShuffleNet和DenseNet设计了利用计算的新颖网络架构/内存高效操作。
  2. 第二类,将模型中的weight从fp32量化为更小的bit-depth 表示。 代表为Ternary weight networks (TWN)、Binary Neural Networks
    (BNN)、XNOR-net

这两类量化方法在权衡延迟和准确性方面缺乏两个方面:

  1. 之前的方法没有在合理的基线架构上进行评估。之前实验里对象是, AlexNet、VGG、GoogleNet这种本身就过度参数化、很多冗余的大模型,因此量化很容易就能压缩很多。

    一个更有意义的挑战将是量化模型架构,这些架构在权衡延迟和准确性方面已经很有效,例如MobileNets。

  2. 许多量化方法不能在实际硬件上提供可验证的效率改进。一些方法只关注如何减少设备上的存储空间大小,而较少关注计算效率。一些方法可以将乘法和加法都通过有效的位移位和位计数操作来实现,但是精度会掉很多。

Quantized Inference

Quantization scheme

提出的量化方案在推理过程中使用纯整数运算实现,在训练过程中使用浮点运算实现,两种实现彼此保持高度对应。

首先通过提供量化方案的数学上严格的定义来实现这一点,并分别将该方案用于整数算术推理和浮点训练。

其中:$r$ 为数学实数(浮点数),$q$ 为量化值。量化方法是整数q到实数r的仿射映射:
$$
r=S(q-Z)
$$
$S$ 是scale缩放因子,一般也是浮点型,和 $r$ 类型一样;

$Z$ 是zero point,和 $q$ 类型一致,含义是当实值 $r=0$ 时,对应的 $q$ 取多少。这一要求的动机是神经网络运算符的有效实现通常需要在边界周围对数组进行0 padding?是不是设置一个实值阈值,阈值外的实值映射的量化值是0?

对每个激活数组和每个权重数组中的所有值使用一组量化参数;

上述可以用一种数据结构来表示,称为quantized buffer。在TensorFlow Lite的 Converter的实际数据结构是这个头文件中的QuantizationParams和Array。(这个仍然包含浮点数的数据结构不会出现在实际的量化设备上推理代码中)

在神经网络中,每个激活数组和权重数组都有一个这样的缓冲区实例,C++表示为(使用c++语法是因为它允许明确的类型传递):

1
2
3
4
5
6
template<typename QType> // e.g. QType=uint8
struct QuantizedBuffer {
vector<QType> q; // the quantized values
float S; // the scale
QType Z; // the zero-point
};

Integer-arithmetic-only matrix multiplication

如何仅使用整数运算运行推理过程?因为公式1里有浮点数$S$,怎么变为纯整数运算呢?这里考虑两个$N\times N$ 的实值矩阵 $r_1$ 和 $r_2$,乘积表示为 $r_3=r_1r_2$,里面元素记为 $r_\alpha^{(i,j)}$ ($\alpha=1,2,3$ ,$1\le i,j\le N$),每个矩阵的量化参数记为 $(S_\alpha , Z_\alpha)$ ,量化值记为 $q_\alpha^{(i,j)}$ 。上式变为:
$$
r_\alpha^{(i, j)}=S_\alpha\left(q_\alpha^{(i, j)}-Z_\alpha\right)
$$
根据矩阵乘法的定义,我们有:
$$
S_3\left(q_3^{(i, k)}-Z_3\right)=\sum_{j=1}^N S_1\left(q_1^{(i, j)}-Z_1\right) S_2\left(q_2^{(j, k)}-Z_2\right)
$$
可以写成:
$$
q_3^{(i, k)}=Z_3+M \sum_{j=1}^N\left(q_1^{(i, j)}-Z_1\right)\left(q_2^{(j, k)}-Z_2\right)
$$
其中乘数 $M$ 定义为:
$$
M:=\frac{S_1 S_2}{S_3}
$$
也就是说,计算过程是:计算两个实数矩阵的乘积 $r_1r_2$,它对应的乘积后的量化结果 $q_3$,然后根据上述公式(4),可以看到里面只有一个浮点数$M$,其他都是整数,而这个浮点数M是可以离线计算、事先计算好的(因为$S_1$、$S_2$、$S_3$ 是常数),因此这个乘积量化结果很容易计算。经验地发现$M$总是在区间 $(0,1)$ 内,因此可以用标准化形式表示它:
$$
M=2^{-n}M_0
$$
其中,$M_0$ 在区间 $ [0.5,1]$ 内,$n$ 为非负整数。规范化乘数$M_0$现在可以很好地表示为定点乘数(例如int16或int32,具体取决于硬件能力)。???

例如,如果使用int32,则表示$M_0$的整数是最接近 $2^{31}M_0$ 的int32值。由于$M_0 \ge 0.5$,该值始终至少为$2^{30}$,因此将始终具有至少30位的相对精度。

因此,乘以 $M_0$ 可以实现为定点乘法。???

同时,乘$2^{−n}$可以通过有效的位移实现(尽管需要有正确的四舍五入行为)。

本节中讨论的计算是在TensorFlow Lite参考代码中实现的,用于全连接层。

因此这种方法是“仅使用整数运算运行推理过程”。

  • TODO 没看懂

Efficient handling of zero-points

为了有效地实现对公式(4)的求值,而不必执行$2N^3$次减法($\sum_j$是2N次,外部遍历i、k,因此是$2NNN=2N^3$次),也无需将乘法的操作数展开为16位整数。公式(4)展开为:
$$
q_3^{(i, k)}= Z_3+M\left(N Z_1 Z_2-Z_1 a_2^{(k)}\right. \left.-Z_2 \bar{a}1^{(i)}+\sum{j=1}^N q_1^{(i, j)} q_2^{(j, k)}\right)
$$
where
$$
a_2^{(k)}:=\sum_{j=1}^N q_2^{(j, k)}, \quad \bar{a}1^{(i)}:=\sum{j=1}^N q_1^{(i, j)} .
$$
每一个 $a_2^{(k)}$ 或 $\bar{a}_1^{(i)}$ 只需要$N$次加法来计算,所以总共只加了$2 N^2$次(分开看,每个N次加法,$a_2^{(k)}$遍历k,一共$N^2$次;$\bar{a}1^{(i)}$ 遍历i,一共$N^2$次;因此一共$2N^2$次)。 (7)求值的其余cost几乎全部集中在核心整数矩阵乘法累加上:
$$
\sum
{j=1}^N q_1^{(i, j)} q_2^{(j, k)}
$$

上式进行了$2N^3$次运算,实际上,公式(7)中的其他项都是$O(N^2)$在$O$中有一个小常数。

与zero point $Z$ 有关的项的计算量都很小,因此将问题减少到其实只要算与其他无零点量化方案中必须计算的相同的核心整数矩阵乘法累积(公式(9))。

Implementation of a typical fused layer

推理代码中融合运算符的粒度(接受8位量化输入并产生8位量化输出)必须与训练图中“伪量化 fake quantization”运算符的位置匹配。

对于在ARM和x86 CPU架构上的实现,使用gemmlowp库。GemmWithOutputPipeline 入口点提供了对现在描述的融合操作的支持。(这里用TensorFlow Lite中实现)例如卷积运算符(参考代码是自带的,优化代码调用gemmlowp)。

在神经网络里,上面的 $q_1$ 矩阵可以用来对应的weight、$q_2$对应activations,累积uint8值的乘积需要一个32位的累加器,我们选择一个有符号类型的累加器。(9)式可以写为:
$$
\mathbf{int}32 += \mathbf{uint}8 * \mathbf{uint}8
$$

(uint8范围0-255,相乘范围0-65535,为什么不用uint16??)

scale参数$S_{bias}$也是用乘法表示
$$
S_{bias}=S_1S_2, Z_{bias}=0
$$
…TODO