论文笔记:Quantizing deep convolutional networks for efficient inference: A whitepaper

地址:https://arxiv.org/abs/1806.08342

这篇白皮书统一介绍了当前将网络进行定点化的方法。下面将看到的重点记录下来。
为了降低深度神经网络的计算量以及带宽(内存占用量),一种简单可行的方式就是将浮点网络(网络权重都是float类型数值)转换成定点网络(网络权重都是或者部分是int8类型)具体来说,这么做的优势在于:

  1. 不用改变网络结构,不用重新训练
  2. 单个int8定点数的内存占用就已经是float32这种浮点数的1/4。显然这样的定点化转换能够降低模型的占用
  3. 降低运行时所需的带宽(内存占用量),降低功耗(因为很多时候功耗很大部分取决于内存的访问)
  4. 便于部署:目前许多平台都能为定点数的计算提供加速支持

量化器(Quantizer)的设计
这里说的量化器实际上做的就是指浮点数(取值范围[xmin,xmax][x_{min}, x_{max}])与定点数(取值范围[0,Nlevels1][0, N_{levels}-1],只能取整数)之间的映射。

Affine(Asymmetric quantizer)

这里我们只考虑线性映射:也就是包含2个参数,尺度(scale)Δ\Delta和零点zz。其中

  • zz是一个整数,并且需要保证零点的量化是没有损失的(我理解是一个数 + 零点还是得等于它本身,有点抽象代数的意思)
  • 因此[xmin,xmax][x_{min}, x_{max}]需要做一个松弛,使得它能够包含数值0,如[2.1,3.5][2.1, 3.5] 需要松弛至[0,3.5][0, 3.5]
  • 量化的过程如下表示:
    xint=round(x/Δ)+zxQ=clamp(0,Nlevels1,xint) x_{int} = round(x/\Delta) + z \\ x_{Q} = clamp(0, N_{levels}-1, x_{int})
  • clamp(0,Nlevels1,xint)clamp(0, N_{levels}-1, x_{int})指对数值进行截断至0,Nlevels10, N_{levels}-1之间。
  • 将量化数值转换回浮点的方式:xfloat=(xQz)Δx_{float} = (x_{Q} - z)\Delta
  • 做卷积的时候则需要先将量化后的数减去相应的z(将数值中心化),然后再进行乘加操作

Symmetric quantizer

一种简化的方式是将零点和浮点数的0绑定,浮点的0对应定点数,意味着z=0z=0,将[xmin,xmax][x_{min}, x_{max}]映射到[-128, 127],这样做的话公式会变得更加简单:
xint=round(x/Δ) x_{int} = round(x/\Delta)
xQ=clamp(Nlevels/2,Nlevels/21,xint) x_{Q} = clamp(-N_{levels}/2,N_{levels}/2-1, x_{int})

随机量化器

略,实际上就是在Asymmetric的基础上,对浮点数加上随机的扰动(个人认为加上随机数也起不了太大的作用,后面作者的实验也证实了这点)

量化训练的特殊化处理

如果按照上述的方式对卷积输出进行量化,有一个问题是:量化函数不连续,并且很多地方的导数为0。因此,如果要在训练时使用量化,则输出值只做截断,不会进行zzΔ\Delta相关的两种变换,而反向传播用的是输出值。

量化参数的计算

  • TensorRT用的是最小化原始值和量化后的数值的KL散度从而得到zzΔ\Delta
  • 本文提出了一种更简单的方法:
    • 对于权重Weight,直接用实际的max & min去求(相当于将原始数值做一个缩放)
    • 对于Actvation函数,则直接用几个batch之间的max & min的滑动平均数(也就是会用一个小的batch做标定)

量化粒度

  • 对于一个NCHW的4维tensor,我们可以用一个z,Δz,\Delta来进行量化(Per-layer)
  • 也可以用N个z,Δz,\Delta来进行量化(Per-layer)。显然这种方式增加了参数量,但提升了*度,从而提升精度

量化预测实验

量化预测,指的是用浮点训,训完之后直接转换成定点。这里作者给出了几个实验结论:

  • Per-channel 量化可以提供一个baseline,并且asymmetric的量化性能已经接近浮点
  • 大模型(Resnet, Inception-v3)对于量化模型会更加鲁棒
  • 如果用per-layer的量化会导致性能的大幅度下降(尤其是mobilenet)
  • 大多数的性能下降都来自于weight而不是activation

定点模型的训练

这里指的是在训练的时候就已经是定点模型。列出值得注意的几点:

  1. 在做BP的时候,其实是维护2套参数:进行反向传播的时候,用的是浮点的权重,更新的也是浮点的权重。原因是因为当训练的后期,梯度会很小,如果这个时候还用定点数表示梯度或权重的话,会导致数值underflow,也就是由于精度问题被四舍五入至0
  2. 然后更新权重完之后再通过z,Δz, \Delta进行截断&缩放

这里总结了与定点模型训练的相关步骤:

  1. 一种推荐的方式是先得到一个浮点模型,然后在浮点模型的基础上finetune
  2. 修改量化相关的OP,将原有的浮点OP换成fake quantization的:输出是定点(截断),但反向传播时候用浮点
  3. 定点模型的训练,同时保存相关的scale & zero-point信息
  4. 训练结束之后做定点的转换(去掉之前的fake quantization)
  5. 模型的部署

Fake Quantitization Op

下图为卷积层的浮点OP以及定点OP的差异。对于浮点OP来说,就是简单地conv + bias,无需多解释。但是对于定点的OP,这里做需要先将weight做量化,然后做定点的相乘,然后再对结果进行量化。
论文笔记:Quantizing deep convolutional networks for efficient inference: A whitepaper

BN的转换

**实际上,在定点模型预测的时候,是不包含BN的。**BN层是通过某种融合,与上一层卷积层的输出结果绑定在一起。具体我们从BN的定义入手。训练时:
xbn=γ((xμB)/σB)+β x_{bn} = \gamma((x-\mu_B)/\sigma_B) + \beta
预测时:
xbn=γ((xμ)/σ)+β x_{bn} = \gamma((x-\mu)/\sigma) + \beta
其中μ,σ\mu, \sigma分别是μB,σB\mu_B, \sigma_B的滑动平均值,μB,σB\mu_B, \sigma_B分别是每个batch在该层输出的均值&方差。BN的作用在于对一层的输出做归一化,从而降低层与层之间的数值依赖,提升精度。
在定点模型中,这样的BN操作,被融合到了的weight中:
Winfer=γW/σBiasinfer=Bias+βγμ/σ W_{infer} = \gamma W/\sigma \\ Bias_{infer} = Bias + \beta - \gamma\mu/\sigma

一些实验结论:

  1. 对于定点化训练,symmetric & asymmetric的量化是没有差别的
  2. 前面提到了如果用per-layer的量化会导致性能的大幅度下降。但是定点化训练能够使得更简单的量化方式(比如per-layer + symmetric)也能够得到近似于浮点模型的结果。

训练的最佳实践方式:

  1. 不要用随机的量化(stochastic quantization),会使得模型指标下降
  2. 先训一个浮点的模型,在此基础上进行定点化训练会得到更好的效果
  3. 将bn结构按预测进行匹配会降低数据抖动,提高模型精度
  4. 谨慎使用指数滑动平均