量化数据运算

量化数据运算

量化数据乘积

使用记号(s, z, q)表示为量化中心 z, 步长为 s 以及量化符号为 q 的仿射映射量化数据

因此2个量化数   ( s a , z a , q a ) \ (s_a, z_a, q_a)  (sa,za,qa)   ( s b , z b , q b ) \ (s_b, z_b, q_b)  (sb,zb,qb) 相乘运算,乘积用   ( s c , z c , q c ) \ (s_c, z_c, q_c)  (sc,zc,qc) 表示
{ a = s a ( q a − z a ) b = s b ( q b − z b ) c = s c ( q c − z c ) \begin{cases} a = s_a (q_a - z_a) \\ b = s_b (q_b - z_b) \\ c = s_c (q_c - z_c) \end{cases} a=sa(qaza)b=sb(qbzb)c=sc(qczc)
根据 c = ab 可以得出

q c = S a S b s c ( q a − z a ) ( q b − z b ) + z c q_c = \frac{S_a S_b}{s_c} (q_a - z_a)(q_b - z_b) + z_c qc=scSaSb(qaza)(qbzb)+zc

其中 ( q a − z a ) ( q b − z b ) (q_a - z_a)(q_b - z_b) (qaza)(qbzb) 使用整数乘法即可完成,对于 S a S b s c \frac{S_a S_b}{s_c} scSaSb需要用到浮点数计算。

通过a和b的量化表示以及它们的格式参数计算c的量化表示一次整数乘法,一次浮点数乘法和一次整数加法

看似运算量没有改进,但应用到量化的矩阵运算就有不同了

仿射映射量化的矩阵运算

矩阵表示 矩阵C=AB

有向量表示A B即为

{ A = [ a ( 1 ) , a ( 2 ) , . . . , a ( N ) ] B = [ b ( 1 ) , b ( 2 ) , . . . , b ( N ) ] \begin{cases} A=[a^{(1)}, a^{(2)}, ... , a^{(N)}] \\ B=[b^{(1)}, b^{(2)}, ... , b^{(N)}] \end{cases} {A=[a(1),a(2),...,a(N)]B=[b(1),b(2),...,b(N)]

C=AB,可以表示为:
∑ n = 1 N a ( n ) b ( n ) \sum_{n=1}^N a^{(n)} b^{(n)} n=1Na(n)b(n)

我们也用仿射映射量化表示
C = s c ( C q − 1 c z c ) C=s_c(C_q−1_cz_c) C=sc(Cq1czc)
可以得出
q c = s a s b s c [ ∑ n = 1 N ( q a ( n ) − z a ) ( q b ( n ) − z b ) ] + z c q_c =\frac{s_as_b}{s_c}[\sum_{n=1}^N (q_a^{(n)}- z_a)(q_b^{(n)}- z_b)] + z_c qc=scsasb[n=1N(qa(n)za)(qb(n)zb)]+zc
算式中间的 ∑ n = 1 N ( q a ( n ) − z a ) ( q b ( n ) − z b ) \sum_{n=1}^N (q_a^{(n)}- z_a)(q_b^{(n)}- z_b) n=1N(qa(n)za)(qb(n)zb)整数减法乘法运算整个计算过程只需要1次浮点乘法,即乘以 s a s b s c \frac{s_as_b}{s_c} scsasb,相比之下,传统向量内积算法需要N次浮点数乘法

量化方式表示 矩阵C=AB

如果对于A和B中的元素,分别用量化步长 S a S_a Sa S b S_b Sb、量化中心 z a z_a za z b z_b zb的仿射映射表示:
{ A = s a ( A q − 1 a z a ) B = s b ( B q − 1 b z b ) ) \begin{cases} A=s_a(A_q− 1_a z_a)\\ B=s_b(B_q− 1_b z_b)) \end{cases} {A=sa(Aq1aza)B=sb(Bq1bzb))
整合上面2个式子
C q = s a s b s c ( A q − 1 a z a ) ( B q − 1 b z b ) + 1 c C_q=\frac{s_as_b}{s_c}(A_q−1_az_a)(B_q−1_bz_b)+1_c Cq=scsasb(Aq1aza)(Bq1bzb)+1c
注意,这里隐含了取整运算:

浮点数 s a s b s c \frac{s_as_b}{s_c} scsasb相乘需要改成“整数运算”,使用整数乘法加上移位实现,降低运算量,并使得运算结果和右侧矩阵的整数数据类型匹配

比如:0.3*x((int)(0.3*256)*x)>>8 ≈ (77*x)>>8,因为256 >> 8 = 1。另外77*x也可以用之前的常整数乘法简化成加减运算

所以,这里计算矩阵 C=AB 的步骤为:

  1. 使用整数加法计算 ( A q − 1 a z a ) (A_q−1_az_a) (Aq1aza)

  2. 使用整数加法计算 ( B q − 1 b z b ) (B_q−1_bz_b) (Bq1bzb)

  3. 前两步结果的相乘 ( A q − 1 a z a ) ( B q − 1 b z b ) (A_q−1_az_a)(B_q−1_bz_b) (Aq1aza)(Bq1bzb)

  4. 上一步运算结果乘以 s a s b s c \frac{s_as_b}{s_c} scsasb,得到 s a s b s c ( A q − 1 a z a ) ( B q − 1 b z b ) \frac{s_as_b}{s_c}(A_q−1_az_a)(B_q−1_bz_b) scsasb(Aq1aza)(Bq1bzb)

  5. 使用整数加法计算 1 c z c 1_cz_c 1czc得到 s a s b s c ( A q − 1 a z a ) ( B q − 1 b z b ) + 1 c \frac{s_as_b}{s_c}(A_q−1_az_a)(B_q−1_bz_b)+1_c scsasb(Aq1aza)(Bq1bzb)+1c

上面步骤3需要** M ∗ N ∗ P M * N * P MNP次整数乘法**,步骤4需要** M ∗ P M * P MP次浮点乘法**(矩阵乘以常数 s a s b s c \frac{s_as_b}{s_c} scsasb),相比之下,非量化算法需要 M ∗ N ∗ P M * N * P MNP次浮点乘法。

代码展示基于仿射映射量化的矩阵乘法

矩阵乘法计算API

import numpy as np

#######################
# 演示使用反射变换实现矩阵
# 乘法的过程
#######################

QMIN,QMAX=0,255 # 量化表示的最大最小值

## 根据numpy矩阵data分析并提取量化参数
def calc_quant_param(data):
    vmin,vmax=np.min(data.ravel()),np.max(data.ravel())
    s=float(vmax-vmin)/float(QMAX-QMIN)
    z=float(QMIN)-float(vmin)/s
    z=int(round(np.clip(z,QMIN,QMAX)))
    return s,z

## 数据量化,计算:dq=round(d/s+z)
def calc_quant_data(d,s,z):
    dq=d/s+z
    dq=np.round(np.clip(dq,QMIN,QMAX)).astype(int)
    return dq

## 数据反量化,计算:d=s*(dq-z)
def calc_dequant_data(dq,s,z):
    return s*(dq-z).astype(float)

## 量化矩阵乘法
# 从Aq、Bq计算C=A*B的量化表示Cq
def quant_matmul(Aq, sa, za, 
                 Bq, sb, zb,
                     sc, zc):
    # 整数乘法
    Cq=np.dot(Aq-za,
              Bq-zb)    
    # 乘以常数系数(浮点数),可以用整数乘法近似,但这里为演示简单,简单使用了浮点乘法
    Cq=(sa*sb/sc)*Cq.astype(float)  
    Cq=np.round(Cq).astype(int)+zc
    return Cq

通过仿射映射量化形式计算两个矩阵的乘法

if __name__ == '__main__':
    np.random.seed(1234)

    # 生成2个随机矩阵
    A=np.random.randn(2,3)      
    B=np.random.randn(3,3)

    # 用浮点运算计算参考答案
    C_ref=np.dot(A,B)

    # 计算A的量化参数sa,za和量化矩阵Aq
    # A=sa*(Aq-za)
    sa,za=calc_quant_param(A)   
    Aq=calc_quant_data(A,sa,za)

    #显示量化结果
    print('sa:%f, za:%f'%(sa,za))
    print('Aq:\n',Aq)
    print('A:\n',A)
    print('recovered A:\n',calc_dequant_data(Aq,sa,za))

    # 计算A的量化参数sa,za和量化矩阵Aq
    # A=sa*(Aq-za)
    sb,zb=calc_quant_param(B)   
    Bq=calc_quant_data(B,sb,zb)

    print('sb:%f, zb:%f'%(sb,zb))
    print('Bq:\n',Bq)
    print('B:\n',B)
    print('recovered B:\n',calc_dequant_data(Bq,sb,zb))

    # 计算C的量化参数sc,zc
    # 注意,实际运算时sc,zc是通过数据统计
    # 事先指定的,不会像这里从答案计算得到
    sc,zc=calc_quant_param(C_ref)   

    ## 使用量化形式计算乘法
    Cq=quant_matmul(Aq,sa,za,
                    Bq,sb,zb,
                       sc,zc)
                       
    ## 比较计算误差,我们将Cq反量化后和参考答案比较
    C=calc_dequant_data(Cq,sc,zc)

    print('sc:%f, zc:%f'%(sc,zc))
    print('Cq:\n',Cq)
    print('C:\n',C)
    print('reference C:\n',C_ref)
    print('relative err:',np.linalg.norm(C-C_ref)/np.linalg.norm(C))

结果:

sa:0.010289, za:116.000000

Aq:
 [[162   0 255]
 [ 86  46 202]]
 
A:
 [[ 0.47143516 -1.19097569  1.43270697]
 [-0.3126519  -0.72058873  0.88716294]]
 
recovered A:
 [[ 0.47329177 -1.19351839  1.43016428]
 [-0.30866855 -0.72022661  0.88484984]]
 
sb:0.013305, zb:169.000000

Bq:
 [[234 121 170]
 [  0 255 244]
 [241  17 144]]
 
B:
 [[ 0.85958841 -0.6365235   0.01569637]
 [-2.24268495  1.15003572  0.99194602]
 [ 0.95332413 -2.02125482 -0.33407737]]
 
recovered B:
 [[ 0.86481115 -0.63862977  0.01330479]
 [-2.248509    1.14421168  0.99785902]
 [ 0.95794466 -2.02232762 -0.33261967]]
 
sc:0.035324, zc:129.000000

Cq:
 [[255   0  82]
 [191  61 100]]
 
C:
 [[ 4.45084753 -4.55682009 -1.66023678]
 [ 2.19009958 -2.4020447  -1.02440142]]
 
reference C:
 [[ 4.4420576  -4.56561002 -1.65261875]
 [ 2.1930554  -2.42287488 -1.01607369]]
 
relative err: 0.0036312932138631597

欢迎关注公众号【三戒纪元】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值