pytorch中模型量化方案qconfig设置

1. 参考

pytorch官方quantization
quantization API

2. qconfig设置

2.1 选择量化后端qnnpack or fbgemm

'qnnpack’和’fbgemm’都是用于在量化部署中对模型进行加速。fbgemm目前被更新为‘x86’

  • 支持的硬件平台不同:'qnnpack’是一种专为 ARM CPU 设计的量化后端,而 ‘fbgemm’ 则是一种适用于 Intel CPU 和 ARM CPU 的通用量化后端。
  • 计算精度和速度不同: qnnpack 和 fbgemm 在计算精度和速度方面也存在差异。qnnpack 在精度上更接近于 FP32,但相应的速度相对较慢;而 fbgemm 则在速度上表现更出色,但在精度上可能会有更多的量化误差。
  • 支持的量化算法不同: qnnpack 和 fbgemm 支持的量化算法也不完全相同。例如,fbgemm 支持基于固定点算法的量化,而 qnnpack 支持更多种类的量化算法,例如针对对称量化和不对称量化等。
qconfig = torch.quantization.get_default_qconfig('qnnpack')
per_channel_quantized_model.qconfig = torch.quantization.get_default_qconfig('fbgemm')

2.2 get_default_qconfig 获取默认量化配置

用于获取默认量化配置的函数。该函数返回一个 QConfig 对象,该对象包含了默认的激活和权重量化配置。qconfig 就是一个QConfig实例。

def get_default_qconfig(backend='fbgemm', version=0):
    if version == 0:
        if backend == 'fbgemm':
            qconfig = QConfig(activation=HistogramObserver.with_args(reduce_range=True),
                              weight=default_per_channel_weight_observer)
        elif backend == 'qnnpack':
            qconfig = QConfig(activation=HistogramObserver.with_args(reduce_range=False),
                              weight=default_weight_observer)
        elif backend == 'onednn':
            qconfig = QConfig(activation=HistogramObserver.with_args(reduce_range=False),
                              weight=default_per_channel_weight_observer)
        else:
            qconfig = default_qconfig
    else:
        raise AssertionError("Version number: " + str(version) +
                             " in get_default_qconfig is not supported. Version number must be 0")

    return qconfig

上面代码对应的就是下面表格

量化的backendactivationweight
fbgemmHistogramObserver (reduce_range=True)PerChannelMinMaxObserver (default_per_channel_weight_observer)
qnnpackHistogramObserver (reduce_range=False)MinMaxObserver (default_weight_observer)
默认(非fbgemm和qnnpack)MinMaxObserver (default_observer)MinMaxObserver (default_weight_observer)

default_observer = MinMaxObserver.with_args(quant_min=0, quant_max=127)
default_weight_observer = MinMaxObserver.with_args(dtype=torch.qint8, qscheme=torch.per_tensor_symmetric)

QConfig 中包含 activation 和 weight的observer类(如MinMaxObserver)或在调用时返回实例的可调用类,而不是具体的observer实例本身。

observer类有默认参数,但可以使用with_args覆盖

class QConfig(namedtuple('QConfig', ['activation', 'weight'])):
    def __new__(cls, activation, weight):
        # catch common mistakes
        if isinstance(activation, nn.Module) or isinstance(weight, nn.Module):
            raise ValueError("QConfig received observer instance, please pass observer class instead. " +
                             "Use MyObserver.with_args(x=1) to override arguments to constructor if needed")
        return super(QConfig, cls).__new__(cls, activation, weight)
  • namedtuple 创建了一个名为 QConfig 的类,它包含了两个属性 activation 和 weight
  • activation 和 weight属性分别用于存储输入和权重的量化配置。

2.3 不使用默认量化配置

使用自定义量化配置如下,给QConfig中的activation和weight设置observer

my_qconfig = QConfig(
          activation=MinMaxObserver.with_args(dtype=torch.qint8),
          weight=default_observer.with_args(dtype=torch.qint8))

3. Observer

记录量化参数,根据四元组(min_val,max_val,qmin, qmax)来计算2个量化的参数:scalezero_point

其中,qmin、qmax是算法提前确定好的,min_val和max_val是从输入数据中观察到的。

  1. xmin和xmaxmin_val,max_val
    在这里插入图片描述
    其中X是观测到的tensor

  2. qmin、qmax的值如下表

qminqmax
qint8 (reduce_range)-6463
qint8-128127
quint8 (reduce_range)0127
quint80255

其中前两行为对称量化,后两行为非对称量化。

3.1 MinMaxObserver

使用张量最大/最小值来计算量化参数,此模块记录输入张量running的最小值和最大值

CLASS torch.ao.quantization.observer.MinMaxObserver(dtype=torch.quint8, qscheme=torch.per_tensor_affine, reduce_range=False, quant_min=None, quant_max=None, factory_kwargs=None, eps=1.1920928955078125e-07)

参数:

  • dtype :指定输出的量化数据类型,默认为 torch.quint8
  • qscheme:指定量化规模的类型,默认为 torch.per_tensor_affine,即每个张量使用相同的缩放因子和零点。
  • reduce_range:一个bool值,决定是否缩放量化范围以使其尽可能地接近观察到的最小值和最大值之间的范围。默认为 False,量化范围将保持默认的quant_min和quant_max值,不会缩放。可以降低模型的量化误差,但可能会导致更多的权重位被浪费。
  • quant_min:指定量化数据类型的最小值,默认为 None,使用默认值 0。
  • quant_max:指定量化数据类型的最大值,默认为 None,使用默认值 255(对于 torch.quint8 类型)。
  • factory_kwargs:一个字典类型的参数,用于在创建min_val和max_val缓冲区时传递额外的参数,以便其具有与模型张量相同的设备类型、数据类型和其他属性。
  • eps:用于避免除以零的小量,默认为 1.1920928955078125e-07。

3.2 HistogramObserver

HistogramObserver与MinMaxObserver不同,HistogramObserver 并不会记录最小值和最大值,并且不会直接计算缩放因子和零点。

在HistogramObserver 实现中,会记录张量值的分布情况,包括值域范围、数值分布情况等,这些信息可以用于量化操作中选择合适的缩放因子和零点。在量化过程中,通常会使用某种分布函数(例如均匀分布或者正态分布)来模拟输入张量的值的分布情况,并根据模拟的分布函数来选择缩放因子和零点。

CLASS torch.ao.quantization.observer.HistogramObserver(bins=2048, upsample_rate=128, dtype=torch.quint8, qscheme=torch.per_tensor_affine, reduce_range=False, quant_min=None, quant_max=None, factory_kwargs=None, eps=1.1920928955078125e-07)

参数:

  • bins:记录的直方图的bins(组数/直方数/直条数,就是将整个数据范围划分成了多少个小区间,每个小区间为一个bin),默认为 2048。
  • upsample_rate:记录的直方图的上采样率,默认为 128。即对于每个输入张量,会随机选择一部分值作为记录的样本,并将样本数乘上上采样率作为最终记录的数量。这个参数可以控制样本的数量,从而控制记录的精度。
    -dtype:量化后的数据类型,默认为 torch.quint8。
  • qscheme:量化方案,默认为 torch.per_tensor_affine。
  • reduce_range:是否缩小量化范围,默认为 False
  • quant_min:量化的最小值,如果未指定则由 qscheme 和 dtype 来决定。
  • quant_max:量化的最大值,如果未指定则由 qscheme 和 dtype 来决定。
  • factory_kwargs:用于创建缓冲区的参数。
  • eps:用于处理除零错误的小常数,默认为 1.1920928955078125e-07。

HistogramObserver 可以与其他量化观察器(例如 MinMaxObserver)结合使用,以提高量化的准确性。

HistogramObserver的min_val,max_val输入数据 + 权重值根据L2Norm确定。

3.3 PerChannelMinMaxObserver

相比于 MinMaxObserver,PerChannelMinMaxObserver 可以在每个通道上进行单独的统计,从而可以更加精确地计算缩放因子和零点值。

该观测器统计张量最小/最大来计算每个通道的量化参数。该模块记录输入张量的运行最小值和最大值,并计算量化参数。

CLASS torch.ao.quantization.observer.PerChannelMinMaxObserver(ch_axis=0, dtype=torch.quint8, qscheme=torch.per_channel_affine, reduce_range=False, quant_min=None, quant_max=None, factory_kwargs=None, eps=1.1920928955078125e-07)

PerChannelMinMaxObserver 继承自 MinMaxObserver,并添加了一个 ch_axis 参数。

  • ch_axis :用于指定通道轴的位置,默认为 0,即假设输入张量的 shape 为 [N, C, …],其中 N 表示 batch size,C 表示通道数,那么 ch_axis=0 就表示统计每个通道的最小值和最大值,而 ch_axis=1 则表示统计每个像素点的最小值和最大值。

3.4 qscheme:量化方案参数

在pytorch量化中定义的四种量化方案(quantization scheme),以及一个特殊的浮点量化方案。

  1. per_tensor_affine: 每个张量使用相同的缩放因子和零点值,缩放因子和零点值分别存储在 scale 和 zero_point 中,类型为整数(torch.quint8、torch.qint8、torch.qint32 等)或浮点数(torch.float16、torch.float32 等)。
  2. per_channel_affine: 每个通道使用相同的缩放因子和零点值,缩放因子和零点值分别存储在 scale 和 zero_point 中,类型同上。
  3. per_tensor_symmetric: 每个张量使用相同的缩放因子,零点值为 0,缩放因子存储在 scale 中,类型同上。
  4. per_channel_symmetric: 每个通道使用相同的缩放因子,零点值为 0,缩放因子存储在 scale 中,类型同上。
  5. per_channel_affine_float_qparams: 与 per_channel_affine 类似,但缩放因子和零点值以浮点数的形式存储.

3.4.1 scale和zp的计算

  • qscheme是上面方案3或4时(对称
max_val = torch.max(-min_val, max_val)
scale = max_val / (float(qmax - qmin) / 2)
scale = torch.max(scale, torch.tensor(self.eps, device=device, dtype=scale.dtype))
if self.dtype == torch.quint8:
    zero_point = zero_point.new_full(zero_point.size(), 128)
  • qscheme是上面方案1时(非对称
scale = (max_val - min_val) / float(qmax - qmin)
scale = torch.max(scale, torch.tensor(self.eps, device=device, dtype=scale.dtype))
zero_point = qmin - torch.round(min_val / scale)
zero_point = torch.max(zero_point, torch.tensor(qmin, device=device, dtype=zero_point.dtype))
zero_point = torch.min(zero_point, torch.tensor(qmax, device=device, dtype=zero_point.dtype))

即如下公式:
在这里插入图片描述

3.5 量化相关函数

3.5.1 torch.quantize_per_tensor ()

使用torch.quantize_per_tensor函数将一个float tensor转换为quantized tensor,并指定量化参数来使用1.per_tensor_affine方案。

torch.quantize_per_tensor(input, scale, zero_point, dtype)

  • input:浮点tensor或需要量化的tensor列表
  • dtype (torch.dtype) : 返回张量的所需数据类型。必须是量化的数据类型之一:torch.quint8、torch.qint8、torch.qint32

例子dtype=torch.quint8
在这里插入图片描述
使用反量化 xdq = xq.dequantize() 把一个量化tensor转换为float tensor,还可以使用xq.int_repr() 获取量化张量的整数表示。

量化方案3.per_tensor_symmetric与1.per_tensor_affine一样都是使用的quantize_per_tensor函数,不同的是dtype 使用的是dtype=torch.qint8,并且只需要设置scale,zero_point被设为0。

3.5.2 torch.quantize_per_channel()

可以使用quantize_per_channel函数将一个float tensor转换为quantized tensor,并指定量化参数来使用2.per_channel_affine方案。

例子
在这里插入图片描述
通过axis参数可以指定在哪个轴上应用per_channel_affine方案,默认为列。

量化方案4.per_channel_symmetric与2.per_channel_affine一样都是使用的quantize_per_channel函数,不同的是dtype 使用的是dtype=torch.qint8,并且只需要设置scale,zero_point被设为0。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值