前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入了解NNIE量化技术

深入了解NNIE量化技术

作者头像
BBuf
发布2020-09-27 11:09:45
3.3K0
发布2020-09-27 11:09:45
举报
文章被收录于专栏:GiantPandaCVGiantPandaCV

【GiantPandaCV导语】这篇文章对量化技术做了概要的介绍,由原理推导,验证实现了海思NNIE的量化算法。最后,作者还尝试了使用Pytorch对训练感知量化算法的进行复现,使其不依赖固定Cuda版本,并且可以使用多卡进行训练,内容非常硬核,具体请看文章。本文同步发表于知乎,地址为:https://zhuanlan.zhihu.com/p/223018242 。文末送出4本《机器学习与深度学习算法基础》书籍,欢迎评论区留言抽奖。

神经网络的端侧部署与量化技术

神经网络在端侧的部署由于内存、带宽、计算能力的限制,相比服务器的模型,所占的空间和计算资源小,通常使用模型量化技术来优化。模型量化将模型的参数离散化,原本32bit的浮点数被映射到8bit的整数上,模型的大小压缩4倍左右;将计算层的输入进行离散化,原本32bit浮点数的乘加操作变为8bit的整数乘加操作,减少了模型推理的计算量,在cpu上能够有2到3倍的速度提升,在DSP上能够有10倍左右的速度提升。

FP32浮点数能够表示的数值范围为

-3.4 \times 10^{38} \sim3.4 \times 10^{38}

,INT8能表示的数值范围是

-128\sim+127

,量化技术将一定范围的FP32数据映射到INT8数值上。以均匀量化为例,量化过程要确认两个参数:缩放系数

\left( \Delta \right)

和零点

\left( z \right)

,通过这两个参数将浮点数转换为整数:

x_{int} = round(\frac{x}{\Delta}) + z

最后将超过INT8表示范围的值进行截断:

x_{Q} = clamp(-128, 127, x_{int})

反量化的操作如下:

x_{float} = \left( x_{Q} - z\right)\Delta

原始数值在量化和反量化的过程中有一定的数值偏差,带来的好处是中间形态用INT8类型表示,原本32位浮点数的乘加操作可以转换为8位定点数的计算,减少计算量,加快了计算速度。

FP32、INT8之间映射参数的确定,根据缩放系数

\left( \Delta \right)

能否覆盖网络数据和参数的范围,可以将量化分类为饱和量化(需要截断)和非饱和量化(不需要截断);根据零点

\left( z \right)

是否为零,可以分为对称量化和非对称量化。给定一个网络,网络参数范围已确定,能够采用指定的量化算法对网络参数进行量化;而网络层的输出激活数值分布与网络的输入有关,需要通过跑量化校准集来进行统计和计算。

训练感知量化根据是否有样本数据和是否进行重新训练可分为动态离线量化、静态离线量化和量化感知训练。

动态离线训练无样本数据,对模型的参数在推理前预先进行量化,而模型层的激活值在预测的时候再决定量化。相比其它两个量化方法,动态离线量化的依赖最少,量化的效果一般,量化的加速效果弱些。

静态离线训练在预测前使用量化校准集进行模型激活值分布的统计,确定激活层的量化参数、能够较方便地进行操作,量化的效果较好,加速效果能够得到保证。

量化感知训练在训练的过程中网络模拟量化的效果进行参数更新和优化,量化的效果最好,部署预测无速度损失,训练过程需要进行改变。常见的深度学习训练框架(tensorflow和pytorch) 有量化感知训练模块,但需要与自家的模型推断框架(tensorflow lite、caffe2)进行配合使用,与海思nnie的量化算法和部署框架不一致,无法进行使用保证量化效果。基于此,笔者写了基于pytorch框架进行量化感知训练的扩展包nnieqat。扩展包刚开始直接调用调用海思量化库,后来复现了量化算法,最终提升了海思nnie上模型部署的效果。

训练感知量化的流程

量化感知训练在训练过程中对网络进行量化,forward()和backward()使用的是量化过后的weights和activation,训练的loss能够反应部署的效果。需要注意的是,update()使用的是未量化的weights。若直接用量化值替代初始值,可能会出现更新的幅度小于量化的幅度,网络“原地踏步”的情况。

量化感知训练由于数据的量化,与正常的训练过程在层的使用和训练的策略上有些不同:

  • 由于网络参数的量化,bn层统计的均值和方差会有抖动,造成训练的不稳定。最好先正常进行训练,得到模型后冻结bn进行finetune;或者在一定的epoch后冻结bn进行训练。
  • 移动端网络使用RELU6代替RELU限制激活值能提升模型的准确率,这在量化感知训练中行不通。量化感知训练过程中让网络自行确定激活值的范围能得到更好的结果。
  • 在量化感知训练中要小心使用指数滑动平均(EMA)的参数更新策略。反向传播使用的是量化后的权重,量化使得权重的震荡变大,使用EMA会导致训练的不稳定。

NNIE量化包的实现和使用

对于量化效果的提升,华为海思部门给出了更改caffe框架、使用量化库进行网络finetune的方法。鉴于caffe训练框架更新维护不够,pytorch框架使用较多的情况,笔者写了基于pytorch的NNIE量化感知训练包nnieqat,方便进行量化感知训练和部署。

nnieqat 安装方便,直接 pip install 后加载模块进行使用, 训练过程中使用四个函数 register_quantization_hook 、merge_freeze_bn、 quant_dequant_weight、 unquant_weight ,增加不到10行代码就可以方便的进行nnie量化感知训练。

四个函数分别对应的功能如下:

  • register_quantization_hook: 为需要量化的层添加了register_forward_pre_hook, register_forward_hook,进行权重量化和激活量化。
  • merge_freeze_bn:合并convolution层和batchnorm层,并固定batchnorm参数防止出现训练不稳定现象。
  • quant_dequant_weight:使用量化反量化后的权重。
  • unquant_weight:使用原始的权重。

NNIE量化算法与实现

前几节对神经网络的端侧部署需求和量化技术作了介绍,给出了基于海思量化库,在pytorch训练框架下使用的量化感知训练包nnieqat,方便进行量化感知训练和部署。文章下半部分将对NNIE量化算法细节进行说明。

海思量化库在使用者角度看是一个算法黑盒:输入数据,得到量化反量化后的结果,没有相关量化方法的说明。量化库有一定的不足之处:

  • 量化库非线程安全,无法多GPU运行;
  • 需要先加载量化库,再导入torch模块(全局变量污染?);
  • CUDA版本需要适配给定的量化库。

这些问题限制了量化库的使用和效率。为了解决这些问题,笔者猜想、验证、实现了NNIE的量化算法,成功避开海思量化库依赖,优化了模型finetune的效率问题。

下边将从海思的量化方案流程、量化方法的猜想和验证、量化方法评价与量化实验总结三个方面对NNIE的量化算法作介绍和评价。

NNIE量化方案流程

对于量化后模型效果不好的问题,海思给出更改caffe训练框架加载“黑盒子”GFPQ量化库进行finetune的方案。从量化的数据来看,可以划分为权重量化和激活量化。对于激活量化,海思在caffe中添加激活量化层,用户可下载参考包,重新编译caffe来添加使用量化层。修改后的网络结构示意图如下:

示意图

对于权重量化,用户需要根据具体的训练环境在训练脚本里手动进行相应的修改(没有参考样例)。权重量化过程网络有一份原始的浮点权重,和一份量化后的定点权重。训练过程网络forward() 、backward()在定点权重上进行;update()在浮点权重上进行。如果网络卷积层和batchnorm()层在部署时进行合并加速,在量化感知训练保存模型时要先合并生成对应的量化参数。

量化方法猜想与验证

为了解决量化库不友好、finetune 没效率的问题,笔者通过给“黑盒子”喂数据、吐数据,接着拟合数据,猜想并验证确认了海思的量化方法。

海思“黑盒子”GFPQ量化库接受fp32数据,量化反量化后输出的数值也是fp32,但数值的个数会被压缩到最多256个(

2^8

)。笔者对[0,256]均匀采样n个数送入“黑盒子”,当n>=5000时,输出的量化反量化后的数值个数为129个且不在增加,得到的结果如下:

y_{positive} = [0,1.0, 1.044, 1.091, 1.139, 1.189, 1.242, 1.297, 1.354, 1.414, 1.477, 1.542, 1.61, 1.682, 1.756, 1.834, 1.915, 2.0, 2.089, 2.181, 2.278, 2.378, 2.484, 2.594 ...]

对[-256, 0] 均匀采样n个数送入“黑盒子”,当n>=5000时,输出的量化反量化后的数值个数为128个且不在增加,得到的结果如下:

y_{negative} = [0,-1.044, -1.091, -1.139, -1.189, -1.242, -1.297, -1.354, -1.414, -1.477, -1.542, -1.61, -1.682, -1.756, -1.834, -1.915, -2.0, -2.089, -2.181, -2.278, -2.378, -2.484, -2.594 ..]

正负两边的得到的数值有一个相同的数值0,合起来正好是256个。从量化数值分布我们可以大概猜想GFPQ采用指数函数量化、近似对称量化的方法。把

y_{positive}

非零的128个元素与x=[1,128]建立映射,能够拟合得到曲线:

y = 0.9576032751*e^{0.0433216988x}
y_{negative}

的非零元素数值与

y_{positive}

的数值一致,差个符号,易得曲线为:

y = -0.9576032751*e^{-0.0433216988x}

变化输入数据的范围时,指数e幂部分的常数因子不变,笔者把变量和常数因子与256联系起来,得到反量化过程

y = f_y(i)

的函数:

y =f_y(i)= \left( \left| data\right|_{max}\right / 256)e^{i * ln(256)/128}, i\in \left[ 0,127 \right]
y = 0
y = f_y(i)= -\left( \left| data\right|_{max}\right / 256)e^{-i * ln(256)/128}, i\in \left[ -127,-1 \right]

化简得:

y =f_y(i)= \left| data\right|_{max} \times2^{i/16-8} , i\in \left[ 0,127 \right]
y = 0
y = f_y(i)= - \left| data\right|_{max} \times2^{-i/16-8}, i\in \left[ -127,-1 \right]

对于公式中的

\left| data\right|_{max}

,先确认量化方法。在采样空间

[0, z ]

进行均匀采样5000次,当

z

等于256时,

y_{max}=256

,将i=127 代入得到

\left| data\right|_{max}=267.33

。海思给出了量化表,即

\left| data\right|_{max}

在离散空间的一些取值。其中245.14、256、267.33三个数紧邻。变化z 在(245.15, 256)范围取值,输出的

y_{max}

均为245.14,计算得到

\left| data\right|_{max} =256

。判断是进行了非饱和量化,

\left| data\right|_{max}

是离散取值空间大于z的最小上界。接着,确定

\left| data\right|_{max}

的分布函数。在量化表中笔者发现 1,2,4,8,16 这些2的n次方形式的数都存在,间隔16个出现。猜想是在2的指数幂空间进行采样,验证确认了

\left| data\right|_{max}

数值服从的离散分布为:

f_{m}(t) = 2^{t/16}, t\in N \\ \left| data\right|_{max} \in f_{m}(t)

至此,我们从输入数据得到

\left| data\right|_{max}

,确认了

y=f_y(i)

的映射。下一步需要建立 x 与 i的映射

i=f_i(x)

,从而得到 x与 y的映射关系

y=f_y(f_i(x))

首先,对于y=0的特殊情况,笔者通过小范围密集均匀采样实验确认x的取值范围为

(-\left| data\right|_{max} \times 2^{1/16-9}, \left| data\right|_{max} \times 2^{1/16-9} )

,直接将该范围内的x映射到0:

y=0, x \in (-\left| data\right|_{max} \times 2^{1/16-9}, \left| data\right|_{max} \times 2^{1/16-9} )

接着,梳理下x, i, y的三者的关系:x经过量化后得到 i,i反量化后得到 y,y是 x在量化反量化后稀疏空间的近似。因此用 x替代 y,根据正负代入上文 y与 i的分段函数,求得 i并取整,得到量化过程

i=f_i(x)

的函数:

i = clamp(-round(ln(-256*x/|data|_{max}) * 128 /ln(256)), 0, 127), x<=-\left| data\right|_{max} \times 2^{1/16-9}
i = clamp(round(ln(256*x/|data|_{max}) * 128 /ln(256)),-127,-1), x>=\left| data\right|_{max} \times 2^{1/16-9}

将上述的量化函数和反量化函数进行合并,再加上y=0情况的特判,便是海思黑盒子的量化方法。 得到量化方法后,笔者在nnieqat量化训练库中对上述方法进行了CUDA实现,然后随机生成数据作为输入,比对nnieqat量化库的输出与海思GFPQ的输出,确认量化方法猜想和算法实现正确性,最后在实际应用中也能达到基本一致的训练效果。

量化方法评价与量化实验总结

海思的量化算法采用指数分布量化,量化反量化过程中需要进行exp()与log()计算,相比均匀量化的移位计算复杂度高,代价大。

笔者采用了自实现的量化库,在imagenet公开数据集上以及在实际应用的场景中,能复现了海思量化库finetune的优化效果,量化后精度与fp32基本一致。目前在进行检测模型yolov5在公开数据集coco上的量化训练实验,具体代码和实验结果见 aovoc/nnieqat-pytorch,后续更新见 神经网络量化 , 欢迎使用、点赞、提pull request以及进行探讨交流。

需要注意,不同的部署框架的量化方法不同,进行量化感知训练需要先确认训练框架量化算法与部署框架的量化算法大体是一致的,最终才能够提升部署效果。例如,英伟达TensorRT 采用的是均匀量化的方案,海思采用的是指数量化方案,量化算法有较大差异,笔者使用nnieqat包进行量化训练在海思平台上能够恢复fp32的效果,在TensorRT上的部署效果没有提升。nnieqat量化训练包后续会根据需要添加其他平台的量化方法,不断提升量化包的场景适用性。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GiantPandaCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 神经网络的端侧部署与量化技术
  • 训练感知量化的流程
  • NNIE量化包的实现和使用
  • NNIE量化算法与实现
    • NNIE量化方案流程
      • 量化方法猜想与验证
        • 量化方法评价与量化实验总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档