PPQ QLinear函数介绍

在本次教程中将展示使用ppq TensorQuantizationConfig量化一个torch Tensor。首先,想给你看一个例子,说明如何使用pyyotch自己的函数量化tensor:

import torch
v_fp32 = torch.Tensor([1,2,3,4,5,6,7,8,9,10])
scale,clip_min,clip_max = 2,-128,127
v_int8 = torch.clip(torch.round(v_fp32 / scale),min=clip_min,max=clip_max)
print(f'Torch Quantized v_int8: {v_int8}')
# tensor([0.,1.,2.,2.,2.,3.,4.,4.,4.,5.])
# let's dequant v_int8
dqv_fp32 = v_int8 * scale
print(f'Torch Dequantized dqv_fp32: {dqv_fp32}')
# tensor([0.,2.,4.,4.,4.,6.,8.,8.,8.,10.])

这里使用对称量化,其中scale=2,,截断值为-128-127。使用的量化公式为:

y = torch.clip(torch.round(x / scale),min=clip_min,max=clip_max)

同样地,使用的反量化公式为:

y = x * scale 

 卷积量化

为了在pytorch中量化卷积层,一个合理的方式是在卷积之前量化卷积输入和权重,得到卷积结果后量化输出。下面这个例子展示了一个卷积层是如何被量化的:

import torch
import torch.nn.functional as F
def quant(tensor: torch.Tensor, scale = 0.1, clip_min = -128, clip_max = 127):
    y = torch.clip(torch.rpund(tensor / scale),min_clip_min,max=clip_max)
    y = y.char() # convert y to int8 dtype
def dequant(tensor: torch.Tensor,scale = 0.1):
    y = tensor * scale
    y = y.float() # convert y to fp32 dtype
    return y
# fp32 version
v_fp32 = torch.rand(size=[1,3,96,96])
w_fp32 = torch.rand(size=[3,3,3,3])
b_fp32 = torch.rand(size=[3])
o_fp32 = F.conv2d(v_fp32,w_fp32,b_fp32)
print(o_fp32)
# int8 version
v_fp32 = torch.rand(size=[1,3,96,96])
w_fp32 = torch.rand(size=[3,3,3,3])
b_fp32 = torch.rand(size=[3])
dqv_fp32 = dequant(quant(v_fp32))
dqw_fp32 = dequant(quant(w_fp32))
o_fp32 = F.conv2d(dqv_fp32,dqw_fp32,b_fp32)
dqo_fp32 = dequant(quant(o_fp32))
print(dqo_fp32)

注意到F.conv2d使用的是dqv_fp32,dqw_fp32而不是v_int8,w_int8,即使pytorch在最新版本中确实有一个int8卷积的实现。事实上使用反量化值和使用int8量化值将会有相同的结果,然而应用int8版本将会在执行过程中阻止梯度方向传播。在PTQ中梯度是必须的,它确保我们可以微调你的网络,以获得更好的量化性能,所以我们我们更愿意在前向传播中使用反量化的值。

PPQ QLinear函数

现在是使用PPQ QLinear函数的时候了,让我们先从PPQ库中导入它们:

from ppq import TensorQuantizationConfig
from ppq.quantization.qfunction.linear import PPQLinearQuant_toInt,PPQLinearQuantFunction

PPQLinearQuantFunction和PPQLinearQuant_toInt是PPQ执行器中的量化(反量化)函数:PPQLinearQuant_toInt是把fp32 tensor量化到int8,PPQLinearQuantFunction可以量化和反量化一个fp32 tensor。TensorQuantizationConfig是描述量化参数(scale,offset,...)的数据结构。换言之,TensorQuantizationConfig告诉你如何量化你的tensor。

import torch
from ppq import TensorQuantizationConfig
from ppq.core import *
from ppq.quantization.qfunction.linear import PPQLinearQuant_toInt,PPQLinearQuantFunction
v_fp32 = torch.tensor([1,2,3,4,5,6,7,8,9,10])
tqc = TensorQuantizationConfig(
    policy = (QuantizationPolicy(
        QuantizationProperty.SYMMETRICAL +
        QuantizationProperty.LINEAR + 
        QuantizationProperty.PER_TENSOR
    )),
    rounding = RoundingPolicy.ROUND_HALF_EVEN,
    num_of_bits = 8,
    quant_min = -128, quant_max = 128,
    scale = torch.tensor([2.0]),
    offset = torch.tensor([0.0]),
    observer_algorithm = None,
    state = QuantizationStates.ACTIVATED)
v_int8 = PPQLinearQuant_toInt(tensor=v_fp32,config=tqc)
dqv_fp32 = PPQLinearQuantFunction(tensor=v_fp32,config=tqc)
print(f'PPQ Quantized v_int8: {v_int8}') # tensor([0,1,2,2,2,3,4,4,4,5],dtype=torch.int32)
print(f'PPQ Dequant dqv_fp32: {dqv_fp32}') # tensor([0.,2.,4.,4.,4.,6.,8.,8.,8.,10.]) 
        

在这里一个TensorQuantizationConfig对象被初始化,TensorQuantizationConfig的属性是QuantizationPolicy,RoundingPolicy,num_of_bits,quant_min,quant_max,scale,offset和QuantizationStates。

QuantizationPolicy

一个QuantizationPolicy是一些QuantizationPropertys的组合,在PPQ(与其他配置单独使用)中QuantizationPolicy被使用去描述一个tensor是如何被量化的。PPQ现在支持7中不同的量化属性。

PER_TENSOR:所有Tensor中共享Scale和Offset(对于有偏置的Convulution层和Gemm层,偏置层将被动量化)

PER_CHANNEL:一个Tensor中允许Scale不共享,有很多Scale(不能每一个元素都有一个scale,硬件无法计算),可以每一个channel一个Scale

LINEAR:线性量化,遵循公式quant(x)=clip(round(x/scale))

EXPONENTIAL:表示指数量化,还没有使用。

SYMMETRICAL:对称量化,在这种模式下offset被停用

ASYMMETRICAL:非对称量化,在这种模式下offset被使用

POWER_OF_2:表示整数量化,在这种模式下scale必须是pow(2,k)

RoundingPolicy 

 RoundingPolicy 是PPQ量化加速的核心设置。它定义了量化计算中的取整行为。

公式:quant(x) = clip(round(x/scale,RoundingPolicy),-128,127)

PPQ现在支持7中不同的取整策略。

ROUND_HALF_EVEN = 0

ROUND_HALF_UP = 1

ROUND_HALF_DOWN = 2

ROUND_HALF_TOEARDS_ZERO = 3

ROUND_HALF_FAR_FROM_ZERO = 4

ROUND_TO_NEAR_INT = 5

ROUND_UP = 6

QParams

Scale,offset,quant_min,quant_max都是参数,它们是量化函数中使用的参数。

quant(x) = clip(round(x/scale,RoundingPolicy),-128,127)

在对称量化中offset将被忽略,在PER_TENSOR量化中 scale和offset是单元素tensor。对于PER_CHANNEL策略,每一个channel都有一个scale和offset。在PPQ中,Offset总是一个浮点tensor,PPQ执行器将在执行前将它取整为int。

QuantizationStates

 QuantizationStates是PPQ量化中的核心数据结构。QuantizationStates表示量化配置是否被激活。对于一个TensorQuantizationConfig实例,现在有11个可用的量化状态。

现在我们对每一种量化状态给一个简短的描述:

INITIAL:在创建TensorQuantizationConfig时给出,是所有量化配置的初始状态。

PASSIVE_INITIAL:对于特定的参数,如GEMM(卷积)的偏置和Pad的填充值。通常情况下,它没有独立的量化scale和偏移量,而与其他tensor的配置一起被量化。对于GEMM和卷积,它的偏置将与输入scale*权重scale一起被量化,对于填充值和clip值,它与输入sale相同。如果有任何量化配置是INITIAL或PASSIVE_INIT状态,PPQ将拒绝部署你的模型,并出现错误。部署你的模型,并且会抛出一个错误。 当PPQ.core.config.DEBUG设置为True时,该检查将被忽略。

DEACTIVATED:DEACTIVATED状态与 "反量化 "功能有关,一旦一个操作被反量化、 所有相关的tensor配置将被替换为DEACTIVATED,这样就跳过了执行过程中的所有量化。

SOI:只要tensor量化配置持有SOI状态、 它将永远不会被量化,也不会被包括在任何优化算法中。 这意味着底层tensor是与SOI相关的tensor,它不能被量化。

ACTIVATE:意味着相应的tensor已经准备好用其配置进行量化。

PASSIVE:意味着相应的tensor已经准备好用其配置进行量化。(然而,它的配置并不是独立的,它仍然取决于其他的人)。

BAKED:意味着相应的张量已经被预先量化,它的值可以直接前向传播而不需要量化。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值