INT8量化的深入理解
参考
博主在上面文章的基础上进行合并整理,包括从这些文章的评论里提取有用的信息,帮助大家理解INT8量化的原理,加深巩固认识,也方便自己复习。
强烈推荐一下第一篇知乎回答,我自己来回看了包括评论有三遍左右进行理解加深,答主写得清晰有趣。
量化几连问
- 为什么量化有用?
因为CNN对噪声不敏感。(博主认为,这个回答应该是基于CNN的卷积不变性。抹掉一两个像素点的话,对CNN最终的学习没有太大影响,毕竟CNN学习的也都是概率,这个噪声只会将降低一点点最终的得分,但是CNN的大体预测方向还是对的) - 为什么用量化?
模型太大,比如alexnet就200MB,存储压力大的哟,必须要降一降温;
每个层的weights范围基本都是确定的,且波动不大,适合量化压缩;
此外,既减少访存又减少计算量,优势很大的啊! - 为什么不直接训练低精度的模型?
因为你训练是需要反向传播和梯度下降的,int8就非常不好做了,举个例子就是我们的学习率一般都是零点几零点几的,你一个int8怎么玩?
其次大家的生态就是浮点模型,因此直接转换有效的多啊! - 为什么量化是可以保证原信息的?
这个原因就好比高清图跟低分辨率图的区别,只要你的目标是大体识别出图中是啥这一信息,那么低分辨率的图也是允许的。你看网上的视频加入马赛克后还会不会影响你的判断呢?并不会,你会脑补出额外的细节,只有当满屏的马赛克的时候才会影响你的观影体验,因此这个打码,噢不,量化其实就是一个程度的问题,一个你能否接受的程度问题。
INT8量化原理
INT8量化目的
就是把原来的float 32bit 的卷积操作(乘加指令)转换为int8的卷积操作,这样计算就变为原来的1/4,但是访存并没有变少哈,因为我们是在kernel里面才把float32变为int8进行计算的。
不饱和(最大值)映射
简单的将一个tensor 中的 -|max| 和 |max| FP32 value 映射为 -127 和 127 ,中间值按照线性关系进行映射。但是试验结果显示这样做会导致比较大的精度损失。
把一个layer的激活值范围的给圈出来,然后按照绝对值最大值作为阀值(因此当正负分布不均匀的时候,是有一部分是空缺的,也就是一部分值域被浪费了;这里有个小坑就是,假如我的激活值全是正的,没有负值,那么你怎么映射呢?),然后把这个范围直接按比例给映射到正负127的范围内来,公式如下:
FP32 Tensor (T) = scale_factor(sf) * 8-bit Tensor(t) + FP32_bias (b)
缺点:这是针对均匀分布的,很明显的可以知道,只要数据分布的不是很均匀,那么精度损失是很大很明显的(因为不均匀的地方就会出现空缺,值域也被浪费掉)。
为什么说最大值映射会精度损失严重???
你看值的分布,由于正负分布很不均匀,如果按照对称最大值映射(原意是为了尽可能多地保留原信息)的话,那么+max那边有一块区域就浪费了,也就是说scale到int8后,int8的动态范围就更小了,举个极端的例子就是量化后原本int8的动态范围只剩1bit了(就是正的样本没有,负的全部扎堆在一个很小的值附近),就是上面说到的满屏马赛克~这种情况下。。。那还表示个毛的原信息啊!
(这里其实没映射前这个值很详细吗,32bit呢,虽然都聚集到一个部分(使得最后映射到负的一个很小值附近),但是没有映射前仍然表示了很多信息啊,现在你映射了,就这么一小块信息,肯定表示不了了呀)
官方给的图:
下面这张图展示的是不同网络结构的不同layer的激活值分布,有卷积层,有池化层,他们之间的分布很不一样,因此合理的量化方式应该适用于不同的激活值分布,并且减小 信息损失。因为从FP32到INT8其实就是一种信息再编码的过程。
该文博主的理解的直接使用线性量化的方式导致精度损失比较大的原因是:
- 上图是一些网络模型中间层的 激活值统计,横坐标是激活值,纵坐标是统计数量的归一化表示,这里是归一化表示,不是绝对数值统计;
- 这个激活值统计 针对的是一批图片,不同的图片输出的激活值不完全相同。所以图上并不是一条曲线而是多条曲线(一张图片(输入到网络进行学习的图片)会对应一条曲线(或者说一条散点线)),