浅谈深度学习模型量化

2021-06-07 10:28:36 浏览数 (1)

首发于GiantPandaCV,未经允许,不可转载

【导读】本次简要的总结了模型量化研究的一些问题,介绍了量化存在的量化误差与其总体上解决量化误差的一些方法。主要讨论了5种非线性量化的方法。

量化任务的简要总结: 1、量化映射方法,也就是将float-32映射到Int数据类型,每个间隔是相等的还是不相等的,这里就是均匀量化(uniform quantization)和非均匀量化(non-uniform quantization),也可以叫作线性量化和非线性量化

2、关于映射到整数是数值范围是有正负数,还是都是正数,这里就是对称量化(有正负数)和非对称量化(全是正数),非对称量化就有zero-point,zero-point的主要作用是用于做padding。

3、原精度即浮float-32,量化到什么样的数据类型,这里就有float和int;到底要选择量化后的是多少个bit,这里就有1-bit(二值网络)、2-bit(三值网络)、3-bit、4-bit、5-bit、6-bit、7-bit、8-bit,这几种量化后的数值类型是整型。

4、是固定所有网络都是相同的bit-width,还是不同的,这里就有混合精度量化(Mixed precision)

5、是从一个已经训练好的模型再进行量化,还是有fine tune的过程或者直接是从头开始训练一个量化的模型,这里就有Post-training quantization(后量化,即将已经训练完的模型参数进行量化)、quantization-aware training(量化感知训练,即在从头开始训练中加入量化)和quantization-aware fine tune(在fine tune训练中加入量化)。

我们与剪枝来做一个不恰当的对比

上面的(3)选择什么样的bit-width来做量化,对应的是剪枝的压缩率,即要剪枝多少参数;

上面的(4)是所有参数统一一个bit表示还是采用混合精度量化,对应的剪枝就是结构化剪枝和非结构化;

上面的(5)有没有fine tune操作,对应的剪枝就是《training from starch》和三步剪枝方法(train-prune-fine tune);

量化误差到底来自于哪里?

1、从float-32到Int数据类型,其中有一个round的操作,这里肯定会有误差; 2、激活函数的截断; 3、溢出时候的处理也有可能带来误差。

那么继续来讨论一下量化遇到的问题是什么:

1、weight和activation的数据分布呈现出一个类拉普拉斯分布或者类高斯分布,数据分布是一个钟型分布,大部分数据集中在中间,两头的数据比较少。如果采用均与量化(uniform quantization),由于是等间隔的,那么中间密集分布的数据的分辨率低。举个例子,假如总共10个值,fp32_x={10,-10,0.11,0.21,0.15,0.05,-0.14,-0.22,-0.08,-0.35},采用对称均匀分布量化,(10-(-10))/(255)= 20/255,Int8_x={127,-127,1,3,2,1,-2,-3, -1,-5},可以看到,0.05和0.11被量化同一个int-8的数值1,而且量化后的数值大部分集中在了[-5,3],而其余的数值没有用到。如何解决这个问题?很简单,给密集的区域用比较多的Int数值表示(增大分辨率),稀疏的区域用比较少的Int数值表示;这就是一个不等间距间隔,称为非均与量化(non-uniform quantization)或非线性量化。

2、另外一个问题就是每一层的数值范围不一定都相同,activation在不同层的数值范围会不一样,这就会产生另外一个问题,动态值域问题,dynamic range。这个dynamic range如何影响量化效果呢?比如8-bit的均与分布量化,值域[-2, 2]的单位间隔是(2-(-2))/255 = 4/ 255,值域[-6, 6]的单位间隔是(6-(-6))/255 = 12/ 255,明显前面的分辨率更高。还是由于数据分布钟型分布,即可abs(数值)大的占据的比例很小,因此,这里可以采用截断的方式提高来量化的分辨率。

3、round会肯定会带来误差,怎么处理呢?Stochastic rounding,因为其期望是x,可以减少round的误差.

Round $(x)=left{begin{array}{ll}lfloor xrfloor & text { with probability } 1-(x-lfloor xrfloor) lfloor xrfloor 1 & text { with probability } x-lfloor xrfloorend{array}right.$ 证明: $E(x)= (1-(x-lfloor xrfloor)) * lfloor xrfloor (x-lfloor xrfloor)*(lfloor xrfloor 1)$ $= lfloor xrfloor - (x-lfloor xrfloor) * lfloor xrfloor (x-lfloor xrfloor)*lfloor xrfloor (x-lfloor xrfloor)$ $= lfloor xrfloor x-lfloor xrfloor = x$

其中$lfloor x rfloor$是向下取整,即取不大于x的最大整数 4、如果是量化感知训练,会遇到另外一个新的问题,就是量化这个操作的导数为0,在backwards的时候,梯度在后向传播中传不到后面。怎么办?(1)STE(Straight-Through Estimator ),直通估算器,即将该操作的梯度设置为1,那么梯度就可以传递下去,$frac{partial mathbf{w}_{q}}{partial mathbf{w}}=frac{partial q(mathbf{w})}{partial mathbf{w}} underset{S widetilde{T} E}{approx} frac{partial I(mathbf{w})}{partial mathbf{w}}=1$;(2)设计一个光滑可导且导数不为零的量化,比如Lq-Net和DSQ(Differentiable Soft Quantization)

以上就是总结的量化知识点和存在的一些问题,可能列举的还不够完善,希望大家提出宝贵的建议。 下面先来讨论非均匀量化/非线性量化,参考文章如下: 1、《Convolutional Neural Networks using Logarithmic Data Representation》 2016 2、 Powers-of-two quantization,这个方法我还没找到原论文 3、《Quantization Networks》CVPR 2019 4、《Additive powers-of-two quantization: an efficient non-uniform discretization for neural networks》 ICLR 2020 5、《Differentiable Soft Quantization: Bridging Full-Precision and Low-Bit Neural Networks》 CVPR 2019 6、《Post-Training Piecewise Linear Quantization for Deep Neural Networks》ECCV 2020

一、均匀量化/线性量化 uniform quantization

$s(a, b, n)$ 就是scale,$leftlfloorfrac{operatorname{clamp}(r ; a, b)-a}{s(a, b, n)}right]$是量化,$leftlfloorfrac{operatorname{clamp}(r ; a, b)-a}{s(a, b, n)}right] s(a, b, n) a$是反量化,

$begin{aligned} operatorname{clamp}(r ; a, b) &:=min (max (x, a), b) s(a, b, n) &:=frac{b-a}{n-1} q(r ; a, b, n) &:=leftlfloorfrac{operatorname{clamp}(r ; a, b)-a}{s(a, b, n)}right] s(a, b, n) a end{aligned}$

另外一种写法,$alpha$是全精度数据的值域[0,$alpha$],大括号里面就有255个值,让全精度数据映射到这255值。

$mathcal{Q}^{u}(alpha, b)=alpha timesleft{0, frac{pm 1}{2^{b-1}-1}, frac{pm 2}{2^{b-1}-1}, frac{pm 3}{2^{b-1}-1}, ldots,pm 1right}$

二、Logarithmic 文章:《Convolutional Neural Networks using Logarithmic Data Representation》

先贴公式,下面公式是上图(b)的方案:

$begin{aligned} w^{T} x & simeq sum{i=1}^{n} w{i} times 2^{tilde{x}{i}} &=sum{i=1}^{n} operatorname{Bitshift}left(w{i}, tilde{x}{i}right) end{aligned}$

为啥要将x变成log呢?就是为了在做矩阵乘法的时候用2的指数形式,先$log_{2}x$,后面再用$2^{x}$复原。这是为了可以用位移的方式实现乘法。方案(b)只将input做了log操作。

继续看上图(c)的方案,即weight和input两个矩阵乘法的乘子都做了log操作,那么:

$begin{aligned} w^{T} x & simeq sum{i=1}^{n} 2^{text {Quantize }left(log _{2}left(w{i}right)right) text { Quantize }left(log {2}left(x{i}right)right)} &=sum{i=1}^{n} operatorname{Bitshift}left(1, tilde{w}{i} tilde{x}_{i}right) end{aligned}$

做矩阵乘法的时候,weight和input都需要用$2^{x}$来复原之前的log操作。 另外还要担心溢出,所以要有做一个截断clamp。一定的bit的数值范围$2^{FSR}$,记为$FSR$, $LogQuant (x, bitwidth, mathrm{FSR})=left{begin{array}{ll}0 & x=0 2^{tilde{x}} & text { otherwise }end{array}right.$ 其中,

$tilde{x}=operatorname{Clip}left(operatorname{Round}left(log _{2}(|x|)right), mathrm{FSR}-2^{text {bitwidth }}, mathrm{FSR}right)$

$operatorname{Clip}(x, min , max )=left{begin{array}{ll}0 & x leq min max -1 & x geq max x & text { otherwise }end{array}right.$

对应的均匀量化/线性量化:

$begin{array}{r} text { LinearQuant }(x, text { bitwidth, FSR }) =operatorname{Clip}left(text { Round }left(frac{x}{text { step }}right) times text { step }, 0,2^{mathrm{FSR}}right) end{array}$

其中$text { step }=2^{mathrm{FSR}-text { bitwidth }}$

这个方法,还是比较复杂的,而且是2016的论文,大家就看看理解一下就好了。

三、Powers-of-two quantization(PoT)

量化的值跟名字一样,就是要把浮点数量化为2的n次幂,下面$Q_{P oT }(x)$公式是PoT的计算公式

$Q_{P oT }(x)=left{begin{array}{ll}operatorname{sign}(x) & |x| geq 1 operatorname{sign}(x) *2^{leftlfloorlog _{2}|x|rightrfloor} & 2^{-b 1} leq|x|<1 0 & |x|<2^{-b 1}end{array}right.$

下面$mathcal{Q}^{p}(alpha, b)$公式也是PoT的计算公式,不过是以集合的形式,列举可以选的PoT值

$mathcal{Q}^{p}(alpha, b)=alpha timesleft{0, pm 2^{-2^{b-1} 1}, pm 2^{-2^{j-1} 2}, ldots, pm 2^{-1},pm 1right}$

其中$b$表示bit位数,这个也是为了做乘法的时候,可以使用位移。 $2^{x} r=left{begin{array}{ll}r & text { if } x=0 r<0 r>>x & text { if } x<0end{array}right.$ 那PoT的缺限在哪里呢?举个例子最好理解: $mathcal{Q}^{p}(1, 5)$ 为例子,那么两个最小的值为$2^{-15}$ 和 $2^{-14}$,这两数值太接近了;而两个最大值为$2^{-1}$ 和 $2^{0}$,这两个最大值的间距又过大。 当bit位数增加的时候,最大值仍然是$2^{-1}$ 和 $2^{0}$,最小值的颗粒度更加细了。

四、Additive powers-of-two(APoT)

先来看看APoT的名字比powers-of-two多了个additive,也就说明比PoT的非线性量化多了个加法项, $mathcal{Q}^{a}(alpha, k n)=gamma timesleft{sum{i=0}^{n-1} p{i}right}$ where $p_{i} inleft{0, frac{1}{2^{i}}, frac{1}{2^{i n}}, ldots, frac{1}{2^{i left(2^{k}-2right) n}}right}$ 其中, $gamma$表示是缩放系数,以保证$mathcal{Q}^{a}$中的最大水平为$alpha$; $k$是基位宽度(base bit-width),是每个加法项的位宽; $n$是加法项的数量。 当设置了 $b$ 和 $k$ 时,可以通过$n=frac{b}{k}$计算$n$。 总共有$2^{k n}=2^{b}$个级别。 APoT量化中加法项的数量可以随位宽b的增加而增加,这为非均匀水平提供了更高的分辨率。下面的图片展示了均匀分布量化、PoT量化和APoT量化的图,可以看出一下几点: 1、均与分布量化是一个等间隔的阶梯,对于钟性分布的数据不利; 2、PoT解决了一个问题,就是在值比较小的区域阶梯变多了,也就是分辨率变高了,但是也存在Powers-of-two quantization中提到的问题; 3、APoT就是为了解决PoT存在的问题,也就是当bit数增加的时候,两个最大值的之间的分辨率没有提高的问题。

因为没有mobilenet、shufflnet的实验,实验结果就不贴图了,另外因为多了一些累加,具体speedup不好说,代码开源了。

五、可微分的非线性量化

可微分的非线性量化留到下一次讨论,给自己挖个坑,即两篇《Quantization Networks》和《Differentiable Soft Quantization: Bridging Full-Precision and Low-Bit Neural Networks》(当然还有其他的文章,我还没找到),主要的思路是:量化映射这个操作原来是阶跃函数或者阶段函数,是不可导或者导数为0,那么找一个可导的函数比如sigmoid去模拟阶跃函数,利用学习的方式去找上图的阶梯。这样就可以不用STE的方法,也就没有了梯度不匹配的问题。 当然这种量化方法也有局限性,就是,得用训练的方式,肯定增加了耗时,而且对于硬件是否友好,这个还得具体落地实验。 因为DSQ有mobilenet的实验,就贴实验效果表格(Quantization Networks没有mobilenet实验就忘记他吧): DSQ:

六、Piecewise Linear Quantization(推荐)

最后来讨论一下PWLQ(作者自己要这么缩写的,不是我强行造词)。代码开源了。 就同名字一样,分段线性量化,这个思路简单而巧妙:在全精度数据的分布中,找一个分位点,那么中间的部分给予比较多的bit位数做一个线性量化,两边分比较少的bit位数也做一个线性量化,非常的巧妙的做法。

$mathrm{pw}(r ; b, m, p)=left{begin{array}{l}operatorname{sign}(r) times operatorname{uni}(|r| ; b-1,0, p, 0), r in R{1} operatorname{sign}(r) times operatorname{uni}(|r| ; b-1, p, m, p), r in R{2}end{array}right.$

m是量化的值域[-m; m] (m > 0) ,$R{1}=[-p, p]$ , $R{2}=[-m,-p) cup(p, m]$,uni表示的是均匀分布量化/线性量化。看图就能一眼看懂这个公式。

图中,m=0.8,$R{1}=[-0.2, 0.2]$,$R{2}=[-0.8,-0.2) cup(0.2, 0.8]$。 来看看效果,(有mobilenet的实验):

后续有时间回过头来,我想更加详细的写一下这些内容。

0 人点赞