NPU的量化处理原理分析

量化带来的好处有很多,首先,由于神经网络对数据精度的不敏感,通过量化将 参数从4byte float转换位1byte,减少了数据量,可以使用容量更小的存储设备,节省了成本;其次,量化带来计算效率的提升,单位时间,单位能效内的计算成果多了,或者说,同样的算力需求的模型,所消费的时间和能量少了,结果就是又快又省电,现在的移动终端都是用电池供电的,也就意味着更久的续航和更好的体验,量化的好处很多很多,但是在那时只能想到这么多,后面有时间再补充。 

量化虽好,也有一个问题,就是比较难以理解,尤其是结合复杂的网络拓扑结构之后,更是千头万绪难以把握,经过多日的冥思苦想,自感小有收获,暂且记录在此,至于对错,我一直认为只要是自己下功夫思考得出的,错误的结论反而更能加深你对问题多角度的思考,很多时候,我们都是从错误中学习不是么?

一个典型的神经网络拓扑结构如下:

我们将他抽象一下,摘取和量化相关的主要部分,在NPU中运行时的模型为:

Q^{l-1}_o \ \ S^{l-1}_o \ \ Z^{l-1}_o:第l-1层输出量化值,Scale和Zero Point.

Q^{l}_i \ \ S^{l}_i \ \ Z^{l}_i:第l层输入量化值,Scale和Zero Point.并且:

                                Q^{l-1}_o==Q^{l}_i \ \ S^{l-1}_o == S^{l}_i \ \ Z^{l-1}_o==Z^{l}_i

Q^{l}_w \ \ S^{l}_w \ \ Z^{l}_w: 第l层权重量化值,Scale和Zero Point.

 Q^{l}_b \ \ S^{l}_b \ \ Z^{l}_b: 第l层偏置量化值,Scale和Zero Point.

Q^{l}_o \ \ S^{l}_o \ \ Z^{l}_o:第l层输出量化值,Scale和Zero Point.

Q^{l+1}_i \ \ S^{l+1}_i \ \ Z^{l+1}_i:第l+1层输入量化值,Scale和Zero Point.并且:

                            Q^{l}_o==Q^{l+1}_i \ \ S^{l}_o == S^{l+1}_i \ \ Z^{l}_o==Z^{l+1}_i

注意图中的S^{l+1}_i \ \ Z^{l+1}_i没有画出,通过这种输入和输出直接对接,不同层之间无缝连接再一起。

下面推导量化公式,看再NPU中,执行的运算是什么样子:

                   (Q^{l}_o - Z^{l}_o)S^l_o=(Q^l_i-Z^l_i)S^l_i \cdot (Q^l_w-Z^l_w)S^l_w +(Q^l_b-Z^l_b)S^l_b

所以:

                  (Q^{l}_o - Z^{l}_o)=(Q^l_i-Z^l_i)\cdot (Q^l_w-Z^l_w)\frac{S^l_i \cdot S^l_w}{S^l_o} +(Q^l_b-Z^l_b)\frac{S^l_b}{S^l_o}

所以:

                  \mathbf{Q^{l}_o=(Q^l_i-Z^l_i)\cdot (Q^l_w-Z^l_w)\frac{S^l_i \cdot S^l_w}{S^l_o} +(Q^l_b-Z^l_b)\frac{S^l_b}{S^l_o}+Z^{l}_o}

继续化简:

                Q^{l}_o=\frac{(Q^l_i-Z^l_i)\cdot (Q^l_w-Z^l_w)}{S^l_b}\frac{S^l_i \cdot S^l_w\cdot S^l_b}{S^l_o} +\frac{(Q^l_b-Z^l_b)}{S^l_i \cdot S^l_w}\frac{S^l_b\cdot S^l_i \cdot S^l_w}{S^l_o}+\frac{S^l_o }{S^l_i \cdot S^l_w\cdot S^l_b}Z^{l}_o

得到:

               \\ \boldsymbol{Q^{l}_o=\frac{(Q^l_i-Z^l_i)\cdot (Q^l_w-Z^l_w)}{S^l_b}\frac{S^l_i \cdot S^l_w\cdot S^l_b}{S^l_o} +\frac{(Q^l_b-Z^l_b)}{S^l_i \cdot S^l_w}\frac{S^l_b\cdot S^l_i \cdot S^l_w}{S^l_o}+\frac{ S^l_i \cdot S^l_w\cdot S^l_b }{S^l_o}\cdot \frac{S^l_o }{S^l_i \cdot S^l_w\cdot S^l_b}Z^{l}_o}

最终,得到公式:

              \boldsymbol{Q^{l}_o=\bigg[\frac{(Q^l_i-Z^l_i)\cdot (Q^l_w-Z^l_w)}{S^l_b} +\frac{(Q^l_b-Z^l_b)}{S^l_i \cdot S^l_w}+\frac{S^l_o }{S^l_i \cdot S^l_w\cdot S^l_b}Z^{l}_o\bigg]\cdot \frac{S^l_b\cdot S^l_i \cdot S^l_w}{S^l_o}}

右边整理后,得到一个所有变量都有确定值的有理分式,并且大结构是两个子分式相乘,适合交给硬件运算。针对每一层,有理分式的每个元是都可以计算出来的,所以左边的Q output的值是明确的,这样的话,我们就知道了本层输出的量化值,零点和Scale,可以i继续传递给下一层做推理。如果想得到本层的浮点输出,可以直接用本层的量化值,zero point以及 scale通过以下公式得到:

      float = (Q-Z)\cdot S

所以,一切完美解决。

反思:

下图是我之前的理解,这篇博客有记录

关于量化原理的思考_tugouxp的专栏-CSDN博客模型部署过程中遇到问题最多的是量化环节,最复杂的,也是量化。我接触神经网络的时间不算长,接触量化就更短了,但是从第一次了解量化这个模型起,就有一个问题深深的困扰着我,简单描述如下。首先描述一下我对量化的认知:量化是个访射变换,将x变换为 px+q, y量化为mx+n。在n,q不为0的情况下,量化后的数据如何反量化回去呢?看公式,根据量化得到的二次式反推xy是很困难的,总会有一次项存在已经同时请教了四位算法专家,等候答复中。。。。。结束!...https://blog.csdn.net/tugouxp/article/details/121982281

我再总结之间一直未理解的点如下,之前我的认知是:
1.量化是个访射变换,将x变换为 px+q, y量化为mx+n。
2.在n,q不为0的情况下,量化后的数据如何反量化回去呢?看公式,根据量化得到的二次式反推xy是很困难的,总会有一次项存在。

之前理解的主要问题是,我把输出的Q^{l}_o \ \ S^{l}_o \ \ Z^{l}_o当成了未知量,这样,就必须先求出当前层的浮点输出,再得到Q^{l}_o \ \ S^{l}_o \ \ Z^{l}_o,而如果要得到当前层的浮点输出,必须通过方程关系将量化输出反量化,而反量化又会涉及到上图中各个参数的耦合。按照上图中的方程,如果要求解量化后的浮点输出,必然设计到忽略某些项,导致精度误差。

但是,如果事先就规定了输出层的scale和 zero point,就只剩下Q output为未知量了,根据上面的推导,根据浮点量守恒列出方程,可以解出Q output.

这样,问题迎刃而解,而且中间根本不用真正求出浮点量,没有精度损失,没有 tradeoff.

联想&总结:

acuity tools 导入模型的时候,在量化阶段,生成quanlize文件的时候,不是已经生成了各层的量化参数了嘛?zero point,scale都是已知量了,这么明显的线索我竟然一直忽略了. 据此,我也更加理解为何量化阶段需要输入几张图像进行量化处理了,很可能是为了解决这个鸡和蛋的问题,在事先不确定输出层scale和zero point的情况下,只能先用图片执行浮点推理,根据各层的实际浮点输出,大概定义一个zero 和 scale,之后在进行部署推理的时候,用的就是在这个阶段确定好的scale和zero point.所以为了避免精度损失,量化选用的图片一定要覆盖实际的模型应用场景,否则,如果你的算法覆盖场景是夜晚抓拍,而你却用白天的风景照片做量化,这样得到的各层scale和zeropoint一定不是最理想的结果。

给这段时间的思考结个尾吧,先不管有没有问题,至少,逻辑上想通了,后面会继续结合应用,思考这个问题,所以即便现在理解有偏差,也总会被我发现的一天吧。


结束!

猜你喜欢

转载自blog.csdn.net/tugouxp/article/details/122329521