Fork me on GitHub

神经网络量化压缩笔记


@[toc]
https://jackwish.net/2019/neural-network-quantization-introduction-chn.html
https://zhuanlan.zhihu.com/p/149659607

0. 前言

近年来,基于神经网络的深度学习在图像处理、自然语言处理、语音识别等领域取得了显著效果。一般情况下,一个神经网络模型越大(一般指参数量越大),模型的拟合能力更强,准确度越高。这将进一步导致运行模型时需要的内存(包内存容量和内存带宽)、磁盘存储消耗增大,推理时延和功耗也会变大,从而限制模型的工业化落地。
为了解决这个问题,可以从两方面来下手:

  • 从原理上设计更高效地网络结构,即参数量更少的模型,并保持精度下降可以接受。
  • 不改变原有模型结构,而是对已有的模型进行参数压缩。

1. 量化压缩方法简介

量化压缩就是上一章节中的第二种解决方案,即对已有的模型进行参数压缩。所谓量化,就是用精度更低的类型来存储权重参数。例如,通常情况下模型默认以float32类型变量来存储权重,低精度就是以float16,int8甚至更低精度的类型来存储。最极端的情况下,可以采用一个bit来存储,也即二值神经网络。工业界中常采用的是int8类型。
在训练过程中,一般还是全精度(float32)类型,到了推理阶段,则有两种方案:一种是所有算子都支持量化后的类型的数据运算(以int8为例),因此模型的全过程中数据流都是int8;另一种是数据流仍是float32,每个算子前后分别有quantize层和dequantize层用以将float32转换为int8或者反过来。

2. 量化压缩原理

2.1. 定点数与浮点数

在计算机的存储中,int属于定点数,float和double属于浮点数。定点数与浮点数的介绍见https://www.jianshu.com/p/d39fb5792ac8
浮点数的存储与转换方式详解见https://www.cnblogs.com/lan0725/p/11515584.html
int8的值域为[-128,127],取值个数为$2^{8};$float32的值域为$[(2^{-23}-2)\times 2^{127},(2-2^{-23})\times 2^{127}]$,取值个数约为$2^{32}$。int8在整个值域上的精度分布是均匀的,而float32不是均匀的,0附近的精度越高,越往值域区间两边精度越低。这是因为,在给定指数时,float32在此指数对应的区间内数值个数是一定的,如下图所示(图片来源https://jackwish.net/2019/neural-network-quantization-introduction-chn.html
在这里插入图片描述

图2.1 float类型的数值个数分布

2.2. 将浮点数量化为定点数

量化压缩的过程本质上是一个一个区间放缩到另一个区间的过程,这里仅讨论线性放缩:

round表示取整
首先确定放缩因子:

所以:

也即:

在工程中,int8可能会取[0,255] (无符号整数)

有时float类型的极大值和极小值会太极端,从而使得放缩因子太大,导致最后量化后的结果太集中于某一个小的子区间,浪费了其他部分的值域空间。这时,可以读float类型的极大值极小值进行clip。例如,认为设为-1.0和1.0.

这里留一个小彩蛋,为什么(2-3)里面不用$x-x^{min}$来与缩放因子相乘,而是采用$x-x^{max}$呢?虽然从数学上是等价的,但是工程实现上的效果会有点不一样,读者可以自己思考一下