神经网络量化压缩学习笔记



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 ; 2^{8}; 28;float32的值域为 [ ( 2 − 23 − 2 ) × 2 127 , ( 2 − 2 − 23 ) × 2 127 ] [(2^{-23}-2)\times 2^{127},(2-2^{-23})\times 2^{127}] [(2232)×2127,(2223)×2127],取值个数约为 2 32 2^{32} 232。int8在整个值域上的精度分布是均匀的,而float32不是均匀的,0附近的精度越高,越往值域区间两边精度越低。这是因为,在给定指数时,float32在此指数对应的区间内数值个数是一定的,如下图所示(图片来源https://jackwish.net/2019/neural-network-quantization-introduction-chn.html
在这里插入图片描述

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

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

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

x q u a n t i z e d = r o u n d ( x f l o a t / x s c a l e + x z e r o _ p o i n t ) (2-1) x_{quantized}=round(x_{float}/x_{scale}+x_{zero\_point})\tag{2-1} xquantized=round(xfloat/xscale+xzero_point)(2-1)
round表示取整
首先确定放缩因子:
x s c a l e = x f l o a t m a x − x f l o a t m i n x q u a n t i z e d m a x − x q u a n t i z e d m i n (2-2) x_{scale}=\frac{x_{float}^{max}-x_{float}^{min}}{x_{quantized}^{max}-x_{quantized}^{min}}\tag{2-2} xscale=xquantizedmaxxquantizedminxfloatmaxxfloatmin(2-2)

( x q u a n t i z e d m a x − x q u a n t i z e d ) × x s c a l e = ( x f l o a t m a x − x f l o a t ) (2-3) (x_{quantized}^{max}-x_{quantized})\times x_{scale}=(x_{float}^{max}-x_{float})\tag{2-3} (xquantizedmaxxquantized)×xscale=(xfloatmaxxfloat)(2-3)
所以:
x q u a n t i z e d = x f l o a t / x s c a l e + x q u a n t i z e d m a x − x f l o a t m a x / x s c a l e (2-4) x_{quantized}=x_{float}/x_{scale}+x_{quantized}^{max}-x_{float}^{max}/x_{scale}\tag{2-4} xquantized=xfloat/xscale+xquantizedmaxxfloatmax/xscale(2-4)
也即:
x z e r o _ p o i n t = x q u a n t i z e d m a x − x f l o a t m a x / x s c a l e (2-5) x_{zero\_point}=x_{quantized}^{max}-x_{float}^{max}/x_{scale}\tag{2-5} xzero_point=xquantizedmaxxfloatmax/xscale(2-5)
在工程中,int8可能会取[0,255] (无符号整数)

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

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

3. tf-lite中的量化方法

大体上分为两类:

  1. 训练后整数量化:输入、模型的weight和bias都是低bit(例如int8),模型在线上推理时,要求输入必须是int8,否则会报错;网络结构中的计算也是int8。
  2. 训练后动态范围量化:模型的weight是int8,bias是float32,每一层的输入是float32,在每层计算前先要进行float32->int8的转换。凡是支持量化计算的层(算子)都要进行这种转换,因此会增加一定的时延。输出仍是float32.

1又有两种实现方式,在tf1.15中,训练时搞定输入和权重的量化参数计算;在tf2.3中,训练时搞定权重的量化参数计算,训练完成后再用一个额外的数据集来获取输入的量化参数。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值