模型量化入门介绍——从谷歌白皮书学模型量化(2)

        紧接着上次的《模型量化入门介绍——从谷歌白皮书学模型量化(1)》,这次介绍一下剩余的部分。大致可分为一下几个部分:量化方式,训练实验,量化建议。这些是我个人的总结,和原文的章节不同,感兴趣的同学可以直接阅读原文。

1. 量化方式

1.1 后量化(post training quantization)

        后量化,顾名思义,就是完成训练后的量化。后量化不需要重新训练模型,直接拿训练好的float模型,对齐权重和激活值进行量化,从而达到减少模型大小,压缩参数,加快推理的目的。后量化一般用起来比较简单,只需要少量的校验数据就可以实现,甚至有些后量化都不需要校验数据都可以完成。

1.1.1 后量化——权重量化

由于只对权重进行量化,因此不需要额外的校验数据,只需要一定的转换工具就可以完成。对于一些不太在意相对浮点模型的效果损失,而主要是对模型大小进行压缩以方便存储和传输的话,一般可只量化权重。

 1.1.2 后量化——权重量化和激活量化

单纯通过统计,其实就可以实现所有的参数权重的量化。但是实际量化推理的时候,会需要对每一层的输入进行量化,大多数时候是对上一层的输出进行量化,这个就是激活值的量化。因此,需要产生前向推理,才能知道激活值,对一些有代表性数据进行前向推理,来获取这些激活值的统计,从而进行激活量化。这些用来做推理的数据,我们叫做校验数据(calibration data)。 通常来说100个mni-batch对于估计激活值的范围就够用了。

之后,文章对后量化的两种量化方式,以8bit量化做了一些实验,比如对权重和激活值都量化有以下实验结果:

通过后量化实验,获得了以下观察:

1) 对于8bit量化,per-channel量化效果很好,几乎可以达到float的准确度。

2) 8bit的激活量化几乎没有准确度的损失,导致激活值的统计范围小有以下原因:

        a) Batch Normalizaiton没有统一尺度

        b) Relu6会限制激活值的范围,把范围限制在(0, 0.6),因此会降低统计范围。

3)  模型参数越多,比如resnet, inception 等,相比于mobilenet系列参数少的模型,量化鲁棒性越好。

4) 对于mobilenet系列,per-layer的量化会有较大的量化损失(可能主要由于batch normalization的参数)。

5) 几乎所有的量化损失,都来源于weight量化的损失。

1.2 量化感知训练(quantization aware training)

       量化感知训练,顾名思义,通过训练来进行量化的感知。也就是通过模型训练来学习量化。一般来说,量化感知训练会比后量化提供更好的精度。白皮书中也进一步描述了如何进行量化感知训练。并且用不同的量化感知训练方案进行精度对比,同时也展现了,即便是per-layer的量化感知感知训练也能相比后量化提供更高的精度。而且对于4bit的量化,相比于后量化的方式,量化感知训练能明显的提高精度。

       那么如果进行量化感知训练呢?可以通过对权重和激活值利用模拟量化的方式来进行量化训练。在量化训练的过程中,利用直通估计(straight through estimator)进行反向传播,而在前向推理和反向传播计算中,用模拟的量化权重和激活值进行计算。但是,会将权重保持为浮点型,并且利用梯度更新来更新权重。更新好的权重又进行量化,进入下一次的前向和反向计算,这样不断的迭代更新。对于SGD, 白皮书中给出了如下权重更新的公式:

       上面的两个公式,相信在刚才的解释之后应该是比较明确的。w_{out}是通过量化操作对浮点型权重进行量化操作得到的,但是这个量化后的权重w_{out}也会保持浮点类型。而训练更新的参数也是量化前的浮点型的权重w_{float},但是用来计算梯度的是经过量化操作后的权重w_{out},这也就是直通估计。

       通过自动的在计算图中插入模拟量化的量化(操作)实现量化训练。TensorFlow中提供了量化库来实现,当然pytorch框架也有,pytorch大多数时候是可以自己根据量化方法自定义的,核心的算法原理是一致的。

1.2.1 量化算子转换

 在量化训练中,由于要插入伪量化节点(前面说的模拟量化操作的节点),需要对原浮点模型的算子转化为可执行量化操作的算子。拿普通的卷积算子为例,如下所示的图是浮点型的计算过程:

 如下图所示为定点量化推理的过程:

 可以看到,是需要对权重进行量化,对输出(激活值)进行量化算子转化的。

       比如对于add和concate算子(操作),在浮点模型中,是无关紧要的,以torch模型为例,在forward随便一个加号和torch.cat搞定。但是对于量化却不那么简单,需要对add,或者concate操作加上伪量化节点。因为量化操作不能像浮点那样直接相加,以add操作为例,如下图所示:

如上所示,xQ, yQ相加,统一到32bit, 再由32bit量化到8bit。为什么得这样呢?自己可以思考一下。最简单的一个道理,相加有可能溢出,必须要转化到一个更大位宽的数据范围里面,然后再转回去。然后还有一些会涉及到,合并的,这个看硬件平台,比如conv + relu, 像某平台就不会有conv输出的激活值了,因此量化模型里面对于conv+relu这种也不需要对conv的输出添加激活量化节点了。

1.2.2 Batch Normalization

batch Normalization在神经网络中是一种常见的操作,batch norm的原理和作用,希望大家都能熟练掌握(个人觉得这是个必会的基础知识点,这里就不展开说了)。我们知道batch norm在训练时,根据每一层的激活值(输出值)的统计值(均值mean和标准差standard deviations), 进行归一化。如下公式所示:

其中的\mu_{B} 和\sigma_{B}是当前batch的均值和标准差。但是对于推理时的batch norm却是通过对训练时每个batch的统计做移动均值(moving average, 不明白同学moving average的可以自行百度一下, 这里不解释)计算得到的。用以下公式表示:

这里的\mu\sigma就分别是每个训练batch的\mu_{B}\sigma_{B}的移动均值。可以看到推理和训练时,这个统计是不一样的。而量化训练的目的是要通过训练的方式来模拟推理的量化过程,这里batch norm推理和训练时是不一样的。那么怎么处理这个batch norm对于量化训练来说很关键,不同的处理方式会影响最终的量化推理效果。在白皮书中,给出了一种方法:对batch norm进行校正和冻结参数的方法( batch normalization with correction and freezing provides the best accuracy)。

        在推理时,batch norm可以折叠到卷积的weight和bias中,如下公式所示:

 这个应该是可以理解的吧。对于量化来说,首先也考虑将batch norm的参数根据上面的公式,将其折叠到卷积的权重和偏置中。如下图所示:

但是这样做,有一个很明显的问题,训练时拿到的统计值,每个batch都不一样,但是在推理阶段却用的是长期的统计值。这样在量化训练中,按照这种方式来训练的话,一定会带来我们不想要的量化参数的抖动,从而影响量化训练和最终的量化推理效果。一个最简单的解决方法是,利用推理阶段的统计值来替换训练中对每个batch的实时统计值,但是利用推理阶段的统计值和当前的batch统计值又不一样,这样也会影响batch norm的效果,从而导致训练不稳定。

        白皮书里面提供了一种很好的解决方案,如下图所示:

        这个图能看明白吗?我是看不太明白,感觉有点抽象。不过下面解释一下应该能知道什么回事了(推荐大家看一下原文,这里是我个人根据原文的理解,虽然我觉得自己理解没问题,但是也担心读者朋友们看我写的,理解又不一样了,最好对比着看)。

        上面提到的两个方法,都会又各自的问题。

        a. 采用直接拿当前batch norm统计值进行折叠的问题是和推理时的统计不同。

        b. 采用推理时的统计会与实时batch norm的统计不同。这两种方式都会产生问题,虽然上面说的a,b像是废话,但是仔细想想,这两个方法引起问题的点(key point)是不同的。a产生问题的关键点,在于模拟量化,如果采用实时的batch统计值,折叠到weights和bias上时,模拟量化和实际推理的差异会产生量化的问题。而b产生问题的关键点在于训练,采用推理统计值代替实时batch统计值,会导致训练时,计算的输出有问题。问题点是不同的!那么有没有什么方法兼顾两者呢?那就是白皮书提出的方法。

        两者都用,在模拟量化时,采用推理统计值,回到训练时,采用实时batch统计。训练稳定后,冻结参数,直接采用推理统计值。就这么简单!具体操作如下:

1. 在进入模拟量化前,对权重进行矫正,如下公式:

结合上面的图,这个很好理解,通过这样一个缩放因子,这样进入量化的权重变成了\frac{\gamma W}{\sigma}, 保证量化计算是的权重和推理保持一致。 

2. 在训练的开始阶段,为了保证正常的batch normalization的输出,我们把这个输出给缩放回去,除以这个c, 把结果变成和正常batch norm一样, 这样就满足了b。如下公式所示:

 3. 等到训练迭代次数足够了,冻结推理统计值(也不需要缩放了,直接用推理时的统计值),稳定训练。如下公式所示:

2. 量化建议与总结

        白皮书中在介绍完量化方式后,做了一些训练实验,然后得出了一些结论,以及一些测速相关的。篇幅有限,这里就不多说,只想给大家交代重点内容,其他的大家可以自行阅读。最后来到量化建议和总结。

1. 模型结构设计上,不要限制激活值的范围。(比如relu比relu6好)

2. 权衡好量化位宽。参数多的模型,量化鲁棒性一般会相对好一些,可以考虑更低的量化位宽,然而需要权衡,比如用30层的卷积,使用4bit的量化位宽,和10层的卷积,采用8bit的量化位宽,这个需要权衡。对于不同任务,位宽的选择也需要权重,对于分类任务,或许可以到更低的位宽,比如4bit,但是对于一些超分,hdr图像处理相关的任务,可能需要选择更高的位宽。

        感觉能写一下的也就这两个吧,其实有好些个,但是感觉是废话,比如量化训练的量化效果会更好呀,per-channel的会更好呀,基于float模型进行finetune量化训练呀,这些感觉都是废话,就不多说啦,大家去看原文吧,至此,量化白皮书的内容讲完啦,喜欢的同学点个关注收藏之类的哈,持续更新更多知识分享!

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机械系的AI小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值