模型的weights数据通常是float32的,量化即将他们转换为int8的。固然其实量化有不少种,主流是int8/fp16量化,其余的还有好比git
量化的优势很明显了,int8占用内存更少,运算更快,量化后的模型能够更好地跑在低功耗嵌入式设备上。以应用到手机端,自动驾驶等等。
缺点天然也很明显,量化后的模型损失了精度。形成模型准确率降低.github
先来看一下计算机如何存储浮点数与定点数:
其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。
float的范围为-2^128 ~ +2^128. 能够看到float的值域分布是极其广的。api
说回量化的本质是:找到一个映射关系,使得float32与int8可以一一对应. 。那问题来了,float32可以表达值域是很是广的,而int8只能表达[0,255].
怎么可以用255个数表明无限多(其实也不是无限多,不少,可是也仍是有限个)的浮点数?网络
幸运地是,实践证实,神经网络的weights每每是集中在一个很是狭窄的范围,以下:
因此这个问题解决了,即咱们并不须要对值域-2^128 ~ +2^128的全部值都作映射。但即使是一个很小的范围,好比[-1,1]可以表达的浮点数也是很是多的,因此势必
会有多个浮点数被映射成同一个int8整数.从而形成精度的丢失.框架
这时候,第二个问题来了,为何量化是有效的,为何weights变为int8后,并不会让模型的精度降低太多?
在搜索了大量的资料之后,我发现目前并无一个很严谨的理论解释这个事情.工具
您可能会问为何量化是有效的(具备足够好的预测准确度),尤为是将 FP32 转换为 INT8 时已经丢失了信息?严格来讲,目前还没有出现相关的严谨的理论。一个直觉解释是,神经网络被过分参数化,进而包含足够的冗余信息,裁剪这些冗余信息不会致使明显的准确度降低。相关证据是,对于给定的量化方法,FP32 网络和 INT8 网络之间的准确度差距对于大型网络来讲较小,由于大型网络过分参数化的程度更高学习
和深度学习模型同样,不少时候,咱们没法解释为何有的参数就是能work,量化也是同样,实践证实,量化损失的精度不会太多,do not know why it works,it just works.spa
由如下公式完成float和int8之间的相互映射.
\(x_{float} = x_{scale} \times (x_{quantized} - x_{zero\_point})\)
其中参数由如下公式肯定:
举个例子,假设原始fp32模型的weights分布在[-1.0,1.0],要映射到[0,255],则\(x_{scale}=2/255\),\(x_{zero\_point}=255-1/(2/255)=127\)code
量化后的乘法和加法:
依旧以上述例子为例:
咱们能够获得0.0:127,1.0:255的映射关系.
那么原先的0.0 X 1.0 = 0.0 注意:并不是用127x255再用公式转回为float,这样算获得的float=(2/255)x(127x255-127)=253
咱们假设全部layer的数据分布都是一致的.则根据上述公式可得\(z_{quantized}=127\),再将其转换回float32,即0.0.blog
同理加法:
平常吐槽:tensorflow sucks. tensorflow要不是大公司开发的,绝对不可能这么流行. 文档混乱,又多又杂,api难理解难使用.
tensorflow中使用tflite_convert作模型量化.用法:
tflite_convert \ --output_file=/tmp/foo.cc \ --graph_def_file=/tmp/mobilenet_v1_0.50_128/frozen_graph.pb \ --inference_type=QUANTIZED_UINT8 \ --input_arrays=input \ --output_arrays=MobilenetV1/Predictions/Reshape_1 \ --default_ranges_min=0 \ --default_ranges_max=6 \ --mean_values=128 \ --std_dev_values=127
官方指导:https://www.tensorflow.org/lite/convert/cmdline_examples
关于各参数的说明参见:
https://www.tensorflow.org/lite/convert/cmdline_reference
关于参数mean_values,std_dev_values比较让人困惑.tf的文档里,对这个参数的描述有3种形式.
std_dev = 1.0 / scale mean = zero_point mean = 255.0*min / (min - max) std_dev = 255.0 / (max - min)
结论:
训练时模型的输入tensor的值在不一样范围时,对应的mean_values,std_dev_values分别以下:
参考:
https://heartbeat.fritz.ai/8-bit-quantization-and-tensorflow-lite-speeding-up-mobile-inference-with-low-precision-a882dfcafbbd
https://stackoverflow.com/questions/54830869/understanding-tf-contrib-lite-tfliteconverter-quantization-parameters/58096430#58096430
https://arleyzhang.github.io/articles/923e2c40/
https://zhuanlan.zhihu.com/p/79744430
https://zhuanlan.zhihu.com/p/58182172