【量化学习2】矩阵运算的量化

根据https://zhuanlan.zhihu.com/p/149659607,今天来学习矩阵的量化。
顺便用class把量化代码重新写一下。

矩阵量化

参考至https://zhuanlan.zhihu.com/p/149659607
由于卷积网络中的卷积层和全连接层本质上都是一堆矩阵乘法,因此我们先看如何将浮点运算上的矩阵转换为定点运算。
假设 r 1 r1 r1、$r2 是浮点实数上的两个 是浮点实数上的两个 是浮点实数上的两个N×N 的矩阵, 的矩阵, 的矩阵,r3$ 是 r 1 r1 r1、$r2 $相乘后的矩阵:

r 3 i , k = ∑ j = 1 N r 1 i , j r 2 j , k r_3^{i,k}=\sum_{j=1}^Nr_1^{i,j}r_2^{j,k} r3i,k=j=1Nr1i,jr2j,k
假设$ S_1 、 、 Z_1$ 是 r 1 r_1 r1矩阵对应的 scale 和 zero point, S 2 S_2 S2 、 $Z_2 $、 S 3 S_3 S3 Z 3 Z_3 Z3 同理,那么可以推出:

S 3 ( q 3 i , k − Z 3 ) = ∑ j = 1 N S 1 ( q 1 i , j − Z 1 ) S 2 ( q 2 j , k − Z 2 ) S_3(q_3^{i,k}−Z_3)=\sum_{j=1}^{N}S_1(q_1^{i,j}−Z_1)S_2(q_2^{j,k}−Z_2) S3(q3i,kZ3)=j=1NS1(q1i,jZ1)S2(q2j,kZ2)

整理一下可以得到:

q 3 i , k = S 1 S 2 S 3 ∑ j = 1 N ( q 1 i , j − Z 1 ) ( q 2 j , k − Z 2 ) + Z 3 q_3^{i,k}=\frac{S1S2}{S3}\sum_{j=1}^N(q_1^{i,j}−Z_1)(q_2^{j,k}−Z_2)+Z_3 q3i,k=S3S1S2j=1N(q1i,jZ1)(q2j,kZ2)+Z3

仔细观察可以发现,除了 S 1 S 2 S 3 \frac{S1S2}{S3} S3S1S2 ,其他都是定点整数运算。那如何把 S 1 S 2 S 3 \frac{S1S2}{S3} S3S1S2 也变成定点运算呢?这里要用到一个 trick。假设 M = S 1 S 2 S 3 M=\frac{S1S2}{S3} M=S3S1S2 ,由于 M M M通常都是 ( 0 , 1 ) (0, 1) (0,1) 之间的实数 (这是通过大量实验统计出来的),因此可以表示成 M = 2 − n M 0 M=2^{−n}M_0 M=2nM0,其中 M 0 M_0 M0是一个定点实数。注意,定点数并不一定是整数,所谓定点,指的是小数点的位置是固定的,即小数位数是固定的。因此,如果存在 M = 2 − n M 0 M=2^{−n}M_0 M=2nM0,那我们就可以通过 M 0 M_0 M0 的 bit 位移操作实现 2 − n M 0 2^{−n}M_0 2nM0,这样整个过程就都在定点上计算了。

代码

Matrix类定义

class Matrix:
    q_min = None
    q_max = None
    d_min = None
    d_max = None
    d_matrix = None     #量化前的矩阵
    q_matrix = None     #量化后的整数矩阵
    de_matrix = None    #反量化的浮点数矩阵
    scale = None            #比例因子
    zero_point = None            #便宜量
    def __init__(self,
                 d_matrix,          #输入矩阵,需量化
                 bits,                 #数据宽度
                 unsigned = True   #默认为无符号整数
                 ):
        #求输入浮点数矩阵的最大值和最小值
        self.d_matrix = d_matrix
        self.d_min = np.min(self.d_matrix)
        self.d_max = np.max(self.d_matrix)

        self.bits = bits
        self.unsigned = unsigned
        # 量化表示的最大最小值
        # 有符号整数,有符号型的对称量化适合用于关于零大致对称的数据
        # 无符号型的对称量化很适合单尾分布的数据,比如RELU的激活值
        if self.unsigned:
            self.q_min = 0
            self.q_max = math.pow(2, self.bits) - 1
        else:
            self.q_min = -math.pow(2, self.bits - 1)
            self.q_max = math.pow(2, self.bits - 1) - 1

        # 求比例因子/规模因子
        self.scale = (self.d_max - self.d_min) / (self.q_max - self.q_min)
        # 求偏移量,保证零点的量化没有误差,
        # 可确保像zero padding或ReLU这样的常规操作不会引起量化错误
        self.zero_point = np.round(self.q_min - (self.d_min / self.scale))

代码解读见注释,在初始化阶段,计算好scale和zero_point

量化和反量化操作

    #单一矩阵做量化
    def quant(self):
         B = self.d_matrix / self.scale  + self.zero_point
         self.q_matrix = np.round(np.clip(B, self.q_min, self.q_max))
         return self.q_matrix

    # 单一矩阵进行反量化
    def dequant(self):
        self.de_matrix = self.scale * (self.q_matrix - self.zero_point)
        return self.de_matrix

矩阵乘法和主函数

def multiply(r1,r2,r3,bits):
    m1 = Matrix(r1,bits)
    m2 = Matrix(r2,bits)
    m3 = Matrix(r3,bits)
    M = (m1.scale * m2.scale)/m3.scale
    t = time.time()
    P = np.dot((m1.quant()-m1.zero_point),(m2.quant()-m2.zero_point))
    # print("timeQ:", (time.time() - t))
    calcM0(M,P)
    return np.round(M*P) + m3.zero_point

if __name__ == '__main__':
    # test_layer_op()
    n = 8       #数据宽度
    # r1 = np.array([[0.1, 0.2], [0.3, 0.4]])
    # r2 = np.array([[0.2, 0.1], [0.4, 0.3]])
    N = 4    #矩阵维度
    r1 = np.random.rand(N, N)
    r2 = np.random.rand(N, N)
    t = time.time()
    r3 = np.dot(r1, r2)
    # print("time:",(time.time()-t))
    # print(Matrix(r3,n).quant())
    print(multiply(r1,r2,r3,n))

实验结果

原始r3和量化后的r3如下

[[1.07010784 1.03473495 0.88933572 0.93330366]
 [1.45808753 1.75294973 1.10697695 1.29235102]
 [0.92677285 0.64595958 0.47124794 0.72815896]
 [1.79805959 1.56749148 0.94262982 1.52354675]]
[[115. 108.  80.  88.]
 [189. 246. 122. 157.]
 [ 87.  33.   0.  49.]
 [255. 210.  90. 202.]]

疑问

  1. 在原公式里,计算q3需计算S3,而计算S3需要对r3求min和max值。本来量化就是为了计算q3代替计算r3,感觉绕不清楚了。
    暂时解答:通过对Micronet代码的解析,大部分tensor里都是0-1的值,所以直接将0和1作为min和max,zero_point似乎也不必要。
  2. 具体矩阵运算到卷积、ReLU的深度学习网络中如何运算?
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐