AI学习者 · 2022年05月11日

深度学习Int8的部署推理原理和经验验证

论文出处:《Integer Quantization for Deep Learning Inference Principles and Empirical Evaluation》
时间:2020.April
单位:NVIDIA

0.前言

这篇paper主要的共享是工程上的经验分享,做了比较充足的实验,基本误差在1%以内,也给出了比较workable的int8量化部署的workflow。而且对以前比较多paper”避而不谈“的mobilenet系列和BERT模型的int8量化做了详尽的实验。

1、Introduction

这部分我不一一细说,就总结几点很显而易见的量化的好处:

1、32-bit的乘加变成了8-bit的乘加,同样的硬件单元下可以speed up;
2、32-bit的参数变成了8-bit,显然是model size变小了,需要的空间也变少了,ROM和RAM都减少了;
3、因为内存的要求降低了,对cache的利用率等其他内存系统操作等效率也会提高。

Table 1给出了理论位宽对吞吐、带宽等的要求,有用的信息是累加的数据类型,这一点在做量化部署实现的时候非常重要,其他数值只是个理论值,实际用处不大。
782af6fa53d98c236f64d63186ee8e89.png

2、相关工作

这部分总结得非常好,有必要我也总结一下。

1、《Quantization and training of neural networks for effificient integer-arithmetic-only inference》,也就是做量化必须看的paper,也是tflite的实现,也是目前QAT的做法的主流参考。这篇说的是如何用全整形进行推理,这部分我用c代码全部实现了(下次一定写稿)。

2、《Quantizing deep convolutional networks for effificient inference: A whitepaper》,白皮书,无需多言,必看paper,也是google出品。

3、《 Discovering low-precision networks close to full-precision networks for effificient embedded inference.》这篇说的是,在imagenet上,量化成int8后只需要1个epoch就会恢复到float-32的精度;一个trick即采用退火学习率调度和最后的学习率需要很小;截断阈值采用的百分比的策略。

4、《 Pact: Parameterized clipping activation for quantized neural networks.》,PACT讲的是激活函数的值域范围是可学习的。
然后很多低于8-bit的量化工作,比如1-bit、2-bit等,一些非均匀分布的量化算法(这种实际应用中对速度有比较大的影响,大部分情况都不使用)。

上述基本都是图像领域的,也例举了一些nlp领域的,比如Q8bert。最近关于BERT的量化的工作有以下:Q8BERT、Q-BERT、TernaryBERT、BinaryBERT、BiBERT等。

3、量化基础

均分量化即Uniform quantization分两步:

1、选择要量化的数值(浮点)的范围并截断,截断即大于数值范围的就让其为数值范围的最大值,反正就变成数值范围的最小值,min(range_min, max(x,  range_max));

2、将截断后的数值映射到整数上,这一步有round的操作。如图所示:

cf7ae3ed39e640803c1833a5b2e5bbee.png

描述在代码和论文中常见的称呼:

Quantize:将一个实数转换为一个量化的整数, 即可float32 变成int8

Dequantize:将一个数从一个量化的整数表示形式转换为一个实数,即int8变成float32

Range Mapping:即一组数值的范围 [β, α]

Affifine Quantization:_f_(_x_) = s _·_ x + z ,即非对称量化,s是缩放因子,z是零点,对于int8,那么int8的值域范围就是[0,255]

a90927b3d6064800950e611bc28bd281.png

Scale Quantization :_f_(_x_) = s _·_ _x_,  即对称量化,对于int8,那么int8的值域范围就是[-127, 127],不适用128这个数值,原因在IAQ论文说了是为了能用16-bit的累加器来存int8*int8,因为永远不存在-128 × -128,也就是改乘法的结果的绝对值不会超过2^14,可以保证用16-bit的累加器来存这个乘法结果。
1652241254(1).png
Tensor Quantization Granularity:per-tensor & per-channel: 这里更加细致地分析了颗粒度如何从per-tensor变到per-channel,还有中间心态的颗粒度,但实际并不会做太多骚操作,感兴趣的朋友们可以翻原文来了解。per-tensor就是整个神经网络层用一组量化参数(scale, zero-point),per-channel就是一层神经网络每个通道用一组量化参数(scale, zero-point)。那么就是per-channel需要存更多的量化参数,对gemm的计算也有一点影响。

下图表示了这两种颗粒度的实验。值得注意的是,table3的实验是只量化weight的对比实验,可以看到per-channel和per-tensor精度上基本没什么区别,但per-tensor对fold BN在轻量化网络会有比较的性能影响,而per-channel不会有这个情况。

个人观点,这个图并不能下结论:per-channel和per-tensor在int8量化推理下没有太大的差异,因为这只量化weight,而没有考虑其他,比如量化activation。

ab40d32f58eaa284e3f55f74c83e7ce3.png

Affifine Quantization即非对称量化在做矩阵乘法的时候比对称量化多了好几项:

下面是非对称量化的乘法,即y=w × x,这里不考虑bias。
1652241298(1).png

多的两项为:

1652241321(1).png

那么,实际应用为了提高推理速度,更加愿意用对称量化;这样又有新的问题了:非对称量化多了这么些操作,有什么增益吗?

答:在激活函数是relu的时候,激活值全大于0,这个情况下还是用对称量化就会浪费一个bit的表示能力,只能[0, 127];

Calibration:Calibration是用来选模型参数和激活值的最大值和最小值,用来做截断。

又三种校准的策略:

max-min:tensor的最大值和最小值,这个策略没有截断。

KL散度(KL divergence):最小化量化后int8与float32数据的KL散度,tensorrt采用这个策略。

百分比(Percentile):选取tensor的99%或者其他百分比的数值,其余的截断。

6c431351cf103c116696130feaa7f040.png

4、PTQ:训练后量化

即对训练好的模型做量化,但不finetune。

前面发现per-tensor的量化,在有折叠BN的情况下会有比较大的精度损失,故作了table4的实验,可以发现在只量化weight的情况下,per-channel量化和max-min的校准策略,折叠BN并不会带来影响,证明了在这个策略下可以使用折叠BN。折叠BN的好处就是算子融合,把之前需要2层即conv+bn变成了1层,减少了计算和取值。

f8041097750abd0ff97c2fb541a943b7.png

tabel 4仅仅只是weight量化那肯定是不够的,需要对activation也量化做的量化才是实际部署使用的,效果如下图table 5,可以看到不用的模型/任务(分类/检测/NLP),需要采用的校准策略是不一样的,也就是校准策略没有哪一中可以称霸。基本int8的量化,在分类、检测、NLP上精度损失都不大,是可以真实落地使用的量化比特位。

5dd0d41087af91430f9aad0c9c917265.png

5、恢复精度的技术:Partial Quantization(部分量化)和QAT(量化中训练)

Partial Quantization:量化后对模型效果影响比较大(也就是更加敏感)的就用高比特来表示,用float32/float16/int16。这个敏感度判别采用最直接简单的方法:每次只量化一层跑一遍,看模型效果的影响,影响大的就更敏感,反之就不敏感。

因为前面的实验发现efficientNet精度影响比较大,用他来实验partial quantization

8ce004072249521c0a016423529cfea6.png

可以看出,最开始的层和最后的层对精度影响很大,另外depthwise conv对精度的影响也比较大。depthwise conv的问题可以使用weight equalization来缓解精度损失。

Quantization-Aware Training(QAT): QAT就是在训练中插入fake quantize节点 做dequantize(quantize(x, b, s), b, s),对quantize这个节点因为导数为0,采用STE(即梯度为0)来解决。QAT的实验如下table 7,会发现有些情况下QAT-int8比float32还好一丢丢,这时候论文作者的描述格局非常大:”Likewise, we do not interpret cases where accuracy is higher than fp32 as quantization acting as a regularizer, it is more likely to be noise or the result of the additional fifine-tuning.“,意思就是这只是实验中的“噪声”,也就是并不是方法多么sota,就只是实验的随机性罢了。大格局!

485ccc622b8f5de1b0b57d7d9f942bd0.png

可学习的量化参数:作者实验了PACT的方法,学习截断值域。结论是:当预设的range合理的时候,PACT几乎没有增益,也就是告诉大家不用迷信这种方法。

dc07024aea5faabf278563b995940b48.png

6、推荐的量化的流程图

6f70980ba3c01cc953964e1cf9e1a79d.png

weight:对称量化+per-channel+max-min校准策略+[-127, 127]activation:per-tensor+对称量化

先进行PTQ,即可量化所有层,校准策略遍历完,max-min、KL散度、百分比99.99%/99.999%,达不到要求做部分量化;

Partial Quantization:只量化某一层跑推理,记录模型效果并且排序,用来选择敏感度,敏感度大的用高精度;,还是达不到要求用QAT;

QAT:加载效果最好的量化模型参数,插入伪量化节点,用原来初始学习率的1%做完QAT的初始学习率,采用退化学习率衰减器。

论文附录有炼丹配方

b20eeffdd98718c7f7a0c5f27718019a.png

深度学习Int8的部署推理原理和经验验证

原文:GiantPandaCV
作者:LoBob

推荐阅读

更多嵌入式AI相关技术干货请关注嵌入式AI专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
16356
内容数
1226
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息