模型量化学习-线性量化
由于工作需要进行模型量化,此前从未接触过相关工作,只得从头学起,简单记录一下学习经过,首先,从线性量化和pipeline开始。一开始看的是吴恩达的量化教程,最近看了一下b站的一个PPQ的作者的量化教程,感觉更简单明白,可能因为说的是中文吧,补充一些内容。
非对称量化
线性量化的概念上比较简单,说白了就是把高bit的值映射到一个低bit的区间内,例如16位浮点数转8位浮点数。
中间涉及到一个变化方式,如下图所示

其中r_min,r_max就是被量化的张量的最大最小值,q_min,q_max是量化目标的最大最小值,z是偏移0点,很简单就能用下面公式推出来一个值要怎么变成量化的结果

略微变形就是q的公式

其中 就是真实值, 是一个比例尺, ,z是一个0点位置,可以用上面公式q和r取极值的时候求出来,也就是 。round可以理解成四舍五入,最后转成int型。
对称量化
对称量化相比于非对称,其实非常简单,说白了就是z的偏移量是0,之前所有公式里的z都被去除了。这种也符合大部分的情况,我们的量化过程中,大部分的值其实都是对称区间的,q,s结果就如下所示:

Power of two量化
又叫整数量化,彻底舍弃了scale,用shift来表示,scale是乘除一个缩放因子,shift是直接对结果进行移位操作,相当于乘除2^n,优点是整个量化过程全是整数,速度更快,缺点就是精度不够,毕竟量化的缩放有限制。
通道量化
在对称量化后,就已经可以对一个张量进行16bit往8bit的量化了,但是这样往往精度损失比较大,所以就有了各种各样的量化策略,比如通道量化。
通道量化顾名思义,就是沿着通道进行量化。

在之前的对称量化当中,对整长图进行了量化,通道量化则是对行或者列或者通道进行维度的划分,例如图中,对每一行进行量化,那自然而然就想到了,既然是对每一行量化,每一行都需要有一个专门的s,然后q的结果就是每一行的r计算 。其他也同理如此。
组量化
组量化说白了就是通道量化的衍生,通道量化是对每一行每一列分组,组量化是自定义一组有多少个数,然后对这这些数求s并做量化

激活量化和权重量化
在学习之前,其实我一直不知道什么A8W8,A4W8之类的都是什么意思,看了之后才get到,A代表的是激活量化,W代表的是权重量化。
权重量化代表了对模型权重进行量化,但是量化之后模型还是用浮点进行计算的。而在权重量化的基础上,进行激活量化,那么整个网络就是以整数进行运算的。激活量化说白了就是对输入进行量化,那么整个网络计算都是在整数上运行了
如果只进行权重量化:貌似只会降低模型的大小,但是计算量并不会低,甚至由于需要映射回去,多了一步,貌似更慢。
同时进行激活量化:权重量化降低模型大小,然后在低bit上运算,会加速。问题是两次量化会进一步降低精度。
构造量化网络层
以一个简单的线性层为例子,如果是W8A16,实现的逻辑如下:
说白了就是把w变成激活层的量化,然后过网络层。
整个类的撰写逻辑如下:
需要注意的点例如:init的时候,接收和正常的linear是一样的。
对于存储的值,需要用register_buffer定义,因为整数在pytorch中是没办法有梯度的。然后设计一个quantize函数进行后续的参数量化权重更新
替换网络完成模型量化
在构造好了量化版本的模型之后,就需要对整个模型进行量化了,说白了就是替换整个网络当中所有的层,然后把它改成对应的量化层,顺便把这些层中间的参数进行量化保存。据说一般量化是不量化最后一层的线性层的,可能因为和分类一样最后一层比较重要吧
这里的quantize就是上面写的量化方式了,感觉也是整个量化的核心,其他的东西现在应该都集成好了,有现成的能用吧。学习仅作理解