Practical considerations

最近通读了高通的量化白皮书,感觉受益匪浅,花了点时间做了下整理,也加入了一些自己的理解,有不足之处,希望能跟大家多交流。

草在结它的种子,风在摇它的叶子。我们站着,不说话,就十分美好。            ——顾城《门前》

当我们对多层神经网络进行量化是,我们将会面临大量的量化选择,包括:量化方案、量化粒度以及位宽。这一节主要介绍一些实用方法。注意到在白皮书中,我们仅仅考虑均匀位宽(硬件普遍支持均匀位宽),这也就意味着无论对于激活值还是权重值来说,位宽在所有层都是一个常数。

量化的分类

非对称量化:Uniform Affine Quantizer

非对称量化是将x_{float}\in(x_{min}, x_{max})区间映射到(0, N_{level}-1) 区间,单边分布中需要先将float区间延展到包含0,再进行量化。

Practical considerations_第1张图片

量化(x_{float}\Rightarrow x_{Q})x_{int}=round(\frac{x_{float}}{\Delta})+z, x_{Q}=clamp(0,N_{levels}-1,x_{int})

反量化(x_{Q}\Rightarrow x_{float})x_{float}=(x_{Q}-z)\Delta

对称量化vs非对称量化

对于每一个激活值和权重值我们需要选择一个量化方案,均匀量化因为有一个额外的偏移(zero-point)更具表现力,但是也带来了额外的计算开销。当权重和激活值都使用均匀量化,即:

\widehat{\mathbf{W}}=s_{\mathbf{W}}\left( \mathbf{W}_{int} - z_{\mathbf{W}}\right), \quad \widehat{\mathbf{x}}=s_{\mathbf{x}}\left( \mathbf{W}_{int} - z_{\mathbf{x}}\right)

则在卷积计算时,权重值和激活值的乘积可以表示为:

\begin{align*} \widehat{\mathbf{W}}\widehat{\mathbf{x}} & =s_{\mathbf{w}}\left( \mathbf{W}_{int} - z_{\mathbf{w}}\right)s_{\mathbf{x}}\left( \mathbf{x}_{int} - z_{\mathbf{x}}\right) =s_{\mathbf{w}}s_{\mathbf{x}}\mathbf{W}_{int}\mathbf{x}_{int}- \color{red}{s_{\mathbf{w}}z_{\mathbf{w}}s_{\mathbf{x}}\mathbf{x}_{int}}- \color{blue}{s_{\mathbf{w}}s_{\mathbf{x}}z_{\mathbf{x}}\mathbf{W}_{int}}+ \color{blue}{s_{\mathbf{w}}z_{\mathbf{w}}s_{\mathbf{x}}z_{\mathbf{x}}} \end{align*}

如果采用对称量化,第一项会存在,第三项和第四项的计算只与权重、尺度和偏移这些已知量有关,因此这两项可以在推理之前计算好并保存在卷积的bias中,不会产生额外的计算开销。第二项取决于输入 \mathbf{x}_{int},这意味着每次推理都需要重新进行计算,产生额外的计算开销和功耗。

基于此,通用的方法是:激活值采用对称量化,权重值采用非对称量化

per-tensor与per-channel

由于几乎所有的硬件加速器都支持per-tensor,所以对权值和激活值进行per-tensor量化在很长一段时间都是标准的量化处理方式。但更细粒度的per-channel能获得更高的精度,尤其当各通道之间的权重分布差异较大时。通过公式(3)我们可以看到,权重的per-channel量化可以通过对每个channel的使用不同的scale值来进行计算,而不需要重新进行缩放。激活值的per-channel量化则要困难的多,因为我们无法将scale值从总和中分离出来,因此需要对累加器输入中的每个通道重新缩放调整。然而,虽然对权重进行per-channel量化越来越成为通用,但并不是所有的硬件都支持这种操作,因此在量化前一定要验证硬件信息。

通用的处理方法:硬件支持per-channel时,权重使用per-channel,激活值使用per-tensor,硬件不支持时都是用per-tensor量化

你可能感兴趣的:(性能优化,深度学习)