TensorFlow 8 bit模型量化

本文基本参考自这篇文章:8-Bit Quantization and TensorFlow Lite: Speeding up mobile inference with low precisionnode

首先来一段keras dalao Francois Chollet的鸡汤:git

  • make it possible
  • make it work
  • make it efficient
  • make it dependable and invisible
  • move on to next layer and think about it again

这段鸡汤已经把8-bit量化的需求和阶段已经说得很清楚了:提高算法效率github

What is 8 bit

目前DL中大部分都是用32bit float类型进行计算的,bit位数的多少直接限制了数据类型可以表达的数据范围,好比float 32的数据是由1bit表示符号,8bit表示整数部,23位表示分数部组成。
Float example.svg算法

num bits Min value Max value
8 bit -128 (-2^7) 128 (x^7)
16 bit -32768 (-2^15) 32768(2^15)
32 bit –2147483648 (-2^31) –2147483648 (2^31)

用更低位的数值类型意味着更小的数据表示范围和更稀疏的数值,量化的时候就会形成数值精度损失。好比要把float数值量化到int类型,那么首先小数部分会损失,而那些超过int类型可以表达的范围的值也会被压缩到int可以表达的最大或最小值。
网络

Why 8 bit?

那么既然会有精度损失那为啥如今int8运算愈来愈流行,主要缘由就是了。架构

  • 快:低bit位的数值计算通常会比高bit的要快。虽然现代计算芯片上浮点型的计算已经并不比int类型慢,可是这主要是设计了专用的float计算核,而如今很流行的ai芯片和一些嵌入式芯片上通常并不会设计不少的float计算核,所以对float数值的计算算力很低算力。svg

    下图就是Nvidia RTX2080ti的芯片架构(ref)post

  • 省:从32bit压缩到8bit,最直接的就是内存可以减小1/4。同时,从RAM中读取数据的时间也会缩短;也能下降运算能好。

这也就说明了为何如今愈来愈多的ai芯片专门针对int8计算进行优化并提供很大的int8算力,好比RK3399 pro搭载的NPU提供3T int8算力。优化

Why 8 bit works?

那么为何int8在DL模型中可以应用呢?不是有数值精度损失么?主要缘由有两个:spa

  1. 训练好的DNN网络时出了名的对噪声和扰动鲁棒性强。
  2. 大部分训练好的权重都落在一个很小的区间内。

通常正常操做的话,8 bit量化只会形成很低的精度损失,并且这个损失是能够经过微调重训练进行弥补的。好比在Han等人在这篇文章里对AlexNet其中一层的权重进行分析:
<imge="https://cdn-images-1.medium.com/max/1600/0*UkgbJuMdr6eOBjux.png" style="zoom:50%"/>

左图是实际权重,大部分分布在-0.1到0.1的范围内,而右图是进行了4bit量化后的权重数值分布,4bit可以最大表示16个数值,所以大部分权重都有塌缩,可以保持原来的值的只有16个值。那么若是进行8bit的量化,最大可以保持256个值,对原始权重的保留会更加完整,量化形成的数值损失会很小。

虽然目前已经有文章开始研究直接用低精度的数值进行训练,好比这个,可是须要替换乘法操做,训练很是复杂,并且效果也暂时不够好,仍是处于实验阶段。这主要是目前的SGD等算法须要不断积累小很是小的梯度值进行权重更新。

How 8 bit works?

那么如何用int类型来表示float类型呢?最简单的方式就是乘一个系数把float类型的小数部分转换成整数部分,而后用这个转换出来的整数进行计算,计算结果在还原成float。相似的,量化具体须要如下几点:

  1. 量化的变换必须是线性的,这样才能确保计算结果可以映射会原始值
  2. 量化必须可以保持0.f的精度,这是由于0在DNN中做用很大,若是原始的0映射到量化后的值变成了其余值并出现了精度损失,那个在计算过程当中就会引入误差。

所以对于实际值和量化值的映射关系,通常能够用如下公式表示:

\(r= (r_{max}-r_{min})/(2^B-1)-0*(q-z)\)

其中,r表示实际值;q表示量化的比特数,好比int8量化就是8;z表示量化后的0点值。

具体的映射关系以下:
img2
从公式中能够看到,量化的重要一点就是要肯定合适的\(r_{max}\)\(r_{min}\).对于训练好的模型就行post-training 量化来讲,这点比较容易,只须要统计冻存的全部权重参数便可。

Post training quantization

通常来讲,冻好的模型中典型的conv层包含如下参数:

  • weights tensor
  • input tensor
  • forward pass operator
  • output tensor

输出来讲,大部分层输出的值都只会落在一个很窄的区间内,所以对output进行量化就须要利用在在训练的时候统计大部分输入获得的输出来进行统计肯定合适的最大和最小值。

可是对于operation来讲,直接利用以前的量化方式须要注意一点:因为计算包括乘法,所以有可能会有计算结果的值溢出量化值(好比int8)所能表达的范围(overflow)。所以这里经常使用的方法是首先将结果用较大数值空间的量化值(好比int32)进行存储,而后再量化到8 bit。采用int32至少能够彻底覆盖原始float类型运算的结果而不担忧溢出。

此外,对于一些操做,其逻辑须要改变。好比ReLU,量化后须要比较的值是quantize(0)而不是原来的0.f。
img3
如上图所示,权重,输入首先进行量化,其中输入的量化范围是须要训练数据做为参考;而输出进行两步量化,来兼容计算过程可能出现的溢出。

Fake Quantization in TFLite

在TensorFlow中,量化是经过fake quantization node来进行的。对于大模型来讲,冗余参数比较多,直接量化的影响比较小;可是对于小模型来讲,冗余参数就比较少了,直接量化致使的 精度损失可能会比较大。在fake quantizaiton中,训练太重就就会模拟评估量化带来的round effect在inference的时候的影响,所以在训练过程当中量化值仍是会议float类型保存,并能够经过反向传播进行调整。具体quantization aware training能够查看这里.

此外,就如以前所说的fake quantizaiton node会记录计算和输出值的范围用于量化。

Result

下表记录了量化形成的精度损失,整体来看仍是很是小的。

What's next

到这位置,只是介绍了8bit量化在TF上的实现原理和方式。而如今学术界对于量化有不少新的研究包括:quantized training, non-linear quantization, binary quantization, networks without multipliers等等,在不久的未来但愿可以出现无损且高效的量化手段,这将极大收益训练和推测。

相关文章
相关标签/搜索