一、摘要
- 过往的模型量化的研究核心点都是如何寻一个更优scale值使得量化后的模型精度更高。
- 高通提出了一种名为AdaRound的后量化算法,该算法的关注点不再是如何为每个kernel/channel/tensor寻找更好的scale,而是对量化过程中的round做改进。
- 该论文首先从数学的角度证明了在模型量化中,直接将浮点数进行四舍五入round到最近定点数的方法并不是精度最优的,同时,分析Round对于最终task loss的影响。并且抽象成一个优化问题: Quadratic Unconstrained Binary Optimization
- 通过一个简单的实验证实了这个猜想
- 然后作者通过一系列的数学推导和先验假设,得到了AdaRound算法。通过per-layer的fine-tuning来获取对于Round误差的补偿。这个补偿和每个weight相关的,有的weights会被Round Up,有的weights会被 Round down。 这个补偿是一个可以train的东西,通过梯度回传的方法而学习获得。
- 通过一系列消融实验和对比实验验证了该算法的优越性。
AdaRound算法一言以蔽之:对conv中每个weight值进行量化时,不再是四舍五入的round-to-nearest,而是自适应的决定weight量化时将浮点值转到最近右定点值还是左定点值。(例如根据round-to-nearest规则,4.3,4.5,4.7会分别转到定点值4,5,5。而AdaRound则可能将其转换为5,4,4的定点值)。
二、背景
1、 Round的类型
Rounding 有挺多种类和不同的行为。Deeplearning量化常用的Rounding,通常有以下几个
- Round down : np.floor
- Round up : np.cell
- RNE (Round Nearest to Even): 当出现0.5 tie的情况,向偶数那边round
- Stochastic Rounding:根据概率随机偏向一方,比如说 0.7,有70%的概率round到0, 30%的概率round到1. 这种rounding通常在低精度下的模型训练上有的较多。
2、量化流程
量化指的是将一个范围的浮点值转换成定点值,常规的模型量化流程是:
先根据量化算法计算scale值。
浮点值除以scale后进行round操作。
最后再按照定点取值范围进行clip,得到量化定点值。
3、反量化流程
而如果对量化产生的定点值再乘个scale(该过程也被称为反量化),得到的就是伪量化值记做W_hat(虽然又回到了浮点值,但由于量化过程中有round和clip操作,伪量化值已经不同于原始浮点值)。整个反量化过程如公式(1) 所示:
image.png
4、量化误差
W - W_hat的绝对值就是weight量化产生的误差。
三、动机
1、四舍五入的量化误差从理论上来说并不是最优的
image.pngimage.png
-
公式(2) 中定义了模型量化前和量化后的误差对比。其中L是损失函数,x,y,w分别表示输入数据,输出label和权重weight,∆w则表示对weight量化过程中引入的量化误差。
-
对公式(2) 进行二阶泰勒展开得到公式(3, 4)。其中g(w)表示weight的梯度,而H(w)表示weight的多元二阶导也就是w的海森矩阵
-
由于我们假设待量化模型已经训练至收敛,所以weight的梯度g(w)是接近于0的极小项可以忽略。所以模型量化前后的损失也就是公式(4),其实就取决于损失的二阶展开项,也就是∆W和H构成的乘项。
image.png
2、最小化误差损失应该如何做?
-
从优化的角度来看,我们要寻找最优的W来使得上面的目标最小。 这里边有两个挑战:
- Hessian矩阵的求解。Hessian矩阵理论上是可以求解的,但是所需要的计算的内存是一个特别夸张的数据。假设网络权重的个数是N,那Hessian是一个 NN的矩阵。当然,在实际应用中,也有一些类似的近似解的求法,但在这里就不特别展开了。
-
其次公式(13)的求解也是一个NP-hard问题,无法直接求解。
= 经过很多一段时间推理,得到说优化全局的损失可以简化到优化局部层的mse,因此优化的公式可以修改为:
image.png
对于W_{hat}的是一个soft quantized weight,通过引入一个连续变量V,H为任意的可微分的激活函数用来将V映射到0到1。
四、AdaRound
简化优化问题(21)
1、 from task loss to local loss
为了便于理解,作者首先从一个简单的例子出发:假设一个模型最后输出层l是一个只有两个weight的全连接,对weight计算二阶偏导即为:
image.png
这里z(l)表示当前层的输入也就上一层w和x的乘,所以我们可以计算得出第l层权重的海森矩阵即为:
image.png
从这个公式中不难看出,计算第L层weight的海森矩阵,需要借助第L-1层的二阶导信息,也就是这个原因导致直接计算每一层的海森矩阵是极其困难的。所以为了继续求解我们最小量化误差,需要对海森矩阵的计算进行进一步的简化:
将公式(16)推导到公式(17),作者引入了一个比较强的假设:假设公式(16)中关于L-1层(也就是z(L))的海森矩阵是一个对角矩阵也就是L-1层的weights之间的相互关联对第L层weights的海森矩阵求导是不相关的。
然后再将公式(17)带入到公式(13)中,得到公式(18):
image.png而从公式(18)到公式(19)也是用了一个很强的先验假设,即对于第L层来说:
image.png
也就是说,作者假设在对第L层计算量化损失时,认为第L-1层对L层的量化损失影响是一个不依赖于校准数据集输入的常量,所以在公式(18)中可以直接省略。(在论文的实验中证明了该假设对最终结果的影响)。
于是通过多个假设,我们要求解的公式就从公式(13)简化成了公式(20)。观察公式(20),可以发现此时待求解公式变成了一个每个layer独立的、不依赖于其它layer和task loss的求解公式。问题变成了找到可以使∆WX的MSE loss值最小的∆W。
2、NP-hard问题简化
为了求解∆W,我们还需要对公式(20)进行简化,得到下面的一组公式:
这四个公式是AdaRound的核心计算公式,下面对这四个公式进行逐一解释:
- 公式(21) 相当于将公式(20)中的∆W展开并忽略x的影响(因为对于公式20 来说x是定值,x的改变并不会影响公式20的结果),同时引入了可微正则项freg(V),用以约束h(Vij)向0和1两个值收敛。V是什么后面解释。
- 公式(22) 即为反量化weight的计算方式,可以发现其值是原始浮点值除scale后进行round-to-floor然后加上一个h(V),再进行截取和反量化。也就是说,对于任意一个权重Wij,需要找到一个Vij值帮助我们完成量化。
- 公式(23) 是rectified sigmoid函数,也就是作者选择的h(V)函数,h(V)函数理论上可以是任意一个可导的并且输出值域在[0, 1]之间的函数(比如说sigmod)。rectified sigmoid函数中ζ 和 γ是伸缩参数,分别取值为1.1 和 −0.1。
公式(24) 为作者定义的正则项freg(V)用以约束V的收敛方向。freg(V)中引入了正整数参数ß,作为退火参数。在AdaRound运行的初始阶段,ß用较大值可以帮助公式(21)更快收敛,而到了运行后期,ß会使用一个较小的正整数,使得h(Vij)逼近0或1。
下图展示了优化前和优化后h(Vi,j)之间的对应关系,可以发现所有h(Vij)都已收敛到0或1两个值上,而图中的虚线则是round-to-nearest的量化截取方法得到的效果(两条垂直虚线分成的四个格子中,右上的h(Vij)应该被量化到1,左下的h(Vij)应该被量化到0.)可以看到AdaRound对不同值的量化选择和round-to-nearest是不相同的。
image.png
3、正文
至此我们得到了公式(21~24)一组公式就可以一层一层的对weight进行优化。然而公式(21)存在一个缺陷:无法避免量化误差的不断累积也没有考虑到激活函数,所以作者对公式(21)进行进一步的优化:
image.png
其中fa()为激活函数。x_hat为当前层的反量化输入,x为当前层的浮点输入。也就是将对∆W的优化转换为对conv计算后结果的优化。
公式(25) 即为AdaRound最后表达式,我们可以直接使用梯度下降算法进行求解。
4、代码
首先安装AMIET,有tf和pytorch两个版本可以安装
https://quic.github.io/aimet-pages/releases/1.16.2/user_guide/adaround.html#ug-adaround
params = AdaroundParameters(data_loader=data_loader, num_batches=num_batches, default_num_iterations=10000,
default_reg_param=0.01, default_beta_range=(20, 2))
# W4A8
param_bw = 4
output_bw = 8
quant_scheme = QuantScheme.post_training_tf_enhanced
adarounded_model = Adaround.apply_adaround(model, torch.rand(1, 3, 224, 224).cuda(), params, path='./',
filename_prefix=filename_prefix, default_param_bw=param_bw,
default_quant_scheme=quant_scheme)
# Create QuantSim using adarounded_model, set and freeze parameter encodings and then invoke compute_encodings
sim = QuantizationSimModel(adarounded_model, quant_scheme=quant_scheme, default_param_bw=param_bw,
default_output_bw=output_bw, dummy_input= torch.rand(1, 3, 224, 224).cuda())
sim.set_and_freeze_param_encodings(encoding_path=f'./{filename_prefix}.encodings')
sim.compute_encodings(model_eval, forward_pass_callback_args=1)
quantized_model_accuracy = model_eval(model=sim.model, early_stopping_iterations=None)
五、实验
由于整个AdaRound的推导过程引入了不少强先验假设,作者首先在ResNet18上进行了消融实验,从实验的角度验证先验假设对模型精度的影响。同时作者对比了AdaRound和其它主流后量化算法对不同CV任务的精度量化效果。
1、5.1 消融实验
作者用ResNet18进行消融实验,实验配置如下:
-
有all-layer 4bit量化和first-layer-only量化两种量化方法进行对比。
-
所有的激活值都是FP32的。
-
校准数据集大小为1024。
-
优化器用了Adam optimzier并且迭代了10k次。
-
batch_size=32。
耗时情况来看,AdaRound量化ResNet18在一张1080Ti上用了10分钟。
image.png
Table2对比了AdaRound推导过程中的几个强假设并得出以下结论:
-
直接优化海森矩阵(公式13)相对于round-to-nearest有很大的精度提升。
image.png -
将优化二阶泰勒展开式中的海森矩阵转换成优化local MSE loss(公式20)是一个很强的假设但其并没有导致精度损失。
image.png -
将公式(20)简化成公式(21),不仅可以将AdaRound的耗时从几个小时减少到几分钟,甚至可以少量的提升模型精度。
image.png
六、阅读中的疑惑点
1、优化公式13如何得到使用何种round方法?
image.png其中weihgt floor即表示floor round 方法,weight ceil的计算方法类似。s表示量化用的scale值,而∆W就是浮点weight和伪量化weight之间的差值。
优化公式13得到了,即浮点weight和伪量化weight之间的差值。又因为已知了浮点量化weight,则可以得到伪量化weight,进而求出是上round哈市下round
提出AdaRound的方法,通过per-layer的fine-tuning来获取对于Round误差的补偿。这个补偿和每个weight相关的,有的weights会被Round Up,有的weights会被 Round down。 这个补偿是一个可以train的东西,通过梯度回传的方法而学习获得。
在一个多层的网络中,文章中采取逐层fine-tuning的方法来对每一层进行adaround的操作。 这样,非常有利于嵌入到post-training的工具里边,只需要获得 full-precision的输出,以及量化后的网络的输出,就可以来train该层的V.
AdaRound是train了一个变量V来控制每一个individual的weights是往上还是往下Round
2、优化公式20如何何种round方法?
V取0还是1决定是上round还是下round。
七、改进点:
AdaRound是train了一个变量V来控制每一个individual的weights是往上还是往下Round。那么是否可以有类似的想法,用在Pruning上呢?
AdaRound能否用在quantization-aware的training上? Training中虽然可以用到STE,但是也会有Round的误差,是否可以用类似的方法来取代STE?
Activaiton可以用这个方法吗?通常Activation的每次都会随着输入的变化而改变,但是否能发现某些区域的activation更偏向于round up,而某些区域更偏向于 rounddown。如果这样的话,是否可以train V 来代表round up/down 的概率。
参考:
https://zhuanlan.zhihu.com/p/391203508
https://zhuanlan.zhihu.com/p/363941822
https://quic.github.io/aimet-pages/releases/1.16.2/user_guide/adaround.html#ug-adaround
网友评论