PaddleSlim icon indicating copy to clipboard operation
PaddleSlim copied to clipboard

量化模型格式解释

Open wanghaoshuang opened this issue 2 years ago • 6 comments

问题1

fake_quant_xxx算子的InScale和OutScale,在计算下一个节点的输入Scale的时候,应该取InScale还是OutScale除((1 << (bit_length - 1)) - 1)

问题2

image 上面这张图里面的各种fake算子,是否有些是废弃掉的?

wanghaoshuang avatar Nov 30 '21 08:11 wanghaoshuang

问题1

fake_quant_xxx算子的InScale和OutScale,在计算下一个节点的输入Scale的时候,应该取InScale还是OutScale除((1 << (bit_length - 1)) - 1)

答:都可以;因为InScale和OutScale是Inplace的Accumulator, 它们指向的是同一个变量,如下图所示: image

在Paddle framework的Pass中,是取的InScale , 代码可参考: https://github.com/PaddlePaddle/Paddle/blob/v2.2.1/paddle/fluid/framework/ir/quant_conv2d_dequant_fuse_pass.cc#L347-L359

wanghaoshuang avatar Nov 30 '21 08:11 wanghaoshuang

问题2

上面这张图里面的各种fake算子,是否有些是废弃掉的?

答:现阶段,这些op都有用,只是用在不同的场景;比如动态图和静态图用的op就不太一样。下边我贴出了 @juncaipeng 整理的当前量化模型格式,其中可以看出各个quant op用在什么场景。

wanghaoshuang avatar Nov 30 '21 08:11 wanghaoshuang

author: @juncaipeng

Paddle2.2 量化模型格式

PaddleSlim产出量化模型功能,是通过多种量化方法,获取、保存OP的量化信息,得到伪量化模型。

**伪量化模型 = 原始模型 + 量化信息 **

产出的伪量化模型,支持在Paddle中直接测试精度,此时不是执行Int8 Kernel,只是模拟了计算Int8 Kernel。

产出的伪量化模型,支持在预测端PaddleInference和PaddleLite中预测部署,此时是执行Int8 Kernel。

背景知识

从量化角度来看,OP分为:

  • 模拟量化OP,有权重可以进行量化训练调整,比如conv2d, con2d_transpose, depthwise_conv2d, matmul, mul
  • 非模拟量化OP,没有权重可以进行量化训练调整,比如pool2d、concat等常规OP

量化中的Fake OP:

  • fake_quant_op: fake_quantize_abs_max, fake_quantize_range_abs_max, fake_quantize_moving_average_abs_max, fake_channel_wise_quantize_abs_max
  • fake_dequant_op: fake_dequantize_max_abs, fake_channel_wise_dequantize_max_abs
  • fake_quant_dequant_op: fake_quantize_dequantize_abs_max, fake_quantize_dequantize_moving_average_abs_max, fake_channel_wise_quantize_dequantize_abs_max

fake_quantize_abs_max是统计当前Tensor的绝对值最大值,通常只应用于权重。如果应用于激活,在预测的时候也是需要再次统计激活的绝对值最大值,比较费时。

per-channel的fake_op只应用于权重,不应用与激活。per-channel的fake_op中有一个参数是quant_axis,指明per-channel量化时输出channel的维度,conv2d和depthwise_conv2d对应的quant_axis是0,conv2d_transpose、mul和matmul对应的quant_axis是1。

量化方法

静态图下,支持量化训练方法、静态离线量化方法、动态离线量化方法

动态图下,支持量化训练方法、静态离线量化方法(两者最近新增、重构,已经提测,但是还需要进行打磨)。

动态图下,还不支持动态离线量化方法。

PaddleLite中opt支持动态离线量化方法。

支持量化的OP

静态图下,量化训练方法支持的模拟量化OP有 conv2d, conv2d_transpose, depthwise_conv2d, matmul, mul;支持统计输入Scale的非模拟量化OP具体见链接,可以便捷地新增;支持统计输出Scale的的OP具体见链接,可以便捷地新增。

静态图下,静态离线量化方法支持上述量化训练支持的OP,此外还支持量化LSTM。

静态图下,动态离线量化方法支持'conv2d', 'depthwise_conv2d', 'mul'。

动态图下,量化训练方法支持的模拟量化OP有 conv2d, depthwise_conv2d, linear;支持统计所有OP的输出Scale。

动态图下,静态离线量化方法支持统计任意OP,目前注册了少量常见OP,后续可以直接新增注册,具体见链接

量化模型存储格式

静态图下量化训练和静态离线量化方法的模型格式

对于需要量化的模拟量化OP,fake_quant_op(fake_quantize_range_abs_max, fake_quantize_moving_average_abs_max)保存输入激活的量化信息fake_dequant_op(fake_dequantize_max_abs, fake_channel_wise_dequantize_max_abs)保存输入权重的量化信息(如下图)。

image

对于特定需要统计输入激活的非模拟量化OP,fake_quant_dequant op 保存输入激活的量化信息(如下图)。

image

对于常见OP会统计OP输出激活的量化信息。为了考虑兼容性,以两种方式将量化信息保存在OP属性中:

  • 以out_threshold为属性名,保存到OP属性中,比如op_desc._set_attr("out_threshold", float(scale_value))
  • 以argname + index + "_threshold"为属性名,保存到OP属性中,比如op_desc._set_attr("Y0_threshold", float(scale_value))

跳过量化的OP会保存skip_quant ,即是op_desc._set_attr("skip_quant", True)

综上:OP的新增属性名称有skip_quant, out_threshold 和 argname + index + “_threshold"。这些属性不可以在裁剪模型时删除。

静态图下动态离线量化方法的模型格式

动态离线量化方法是压缩模型体积,主要应用于移动端模型部署。为了便于用户使用,PaddleLite的OPT中支持相同的动态离线量化方法。

动态离线量化方法只需要保存目标OP('conv2d', 'depthwise_conv2d', 'mul')的权重量化Scale、量化比特数和量化方法到OP属性中,如下:

# 保存量化比特数
op._set_attr('quantize_weight_bits', weight_bits)  
# 保存量化scale,var_name是权重tensor的name
op._set_attr(var_name + "_quant_scale", scales)   
# 保存是perchannel还是perlayer量化,用于标记该OP是动态离线量化方法
op._set_attr('quantization_type', 'post_weight_channel_wise_abs_max') 

此前没有考虑模型属性会被裁剪,所以建议后续完善一下,将quantize_weight_bits改名字为bit_length, 将var_name + "_quant_scale"改名字为argname + index + "_threshold",同时Lite中配套修改一下。 不过PaddleLite的OPT已经提供动态离线量化方法,所以该工作优先级可以定为中等。

经过完善后,OP的新增属性名称有bit_length, quantization_type 和 argname + index + "_threshold"。这些属性不可以在裁剪模型时删除。

动态图下量化训练和静态离线量化方法的模型格式

对于需要量化的模拟量化OP,fake_quant_dequant_op(fake_quantize_dequantize_moving_average_abs_max, fake_channel_wise_quantize_dequantize_abs_max)分别保存输入激活和权重的量化信息(如下图)。 对于模拟量化OP,前后分别插入fake_quant_op和fake_dequant_op,等价于前面插入fake_quant_dequant_op。

image

对于所有OP,会将输入激活和输入权重的量化信息,以argname + index + "_threshold"为属性名保存到OP属性中,比如op_desc._set_attr("X0_threshold", float(scale_value))()。

对于所有OP,会统计输出激活的量化信息。为了考虑兼容性,以两种方式将量化信息保存在OP属性中:

  • 以out_threshold为属性名保存到OP属性中,比如op_desc._set_attr("out_threshold", float(scale_value))
  • 以argname + index + "_threshold"为属性名保存到OP属性中,比如op_desc._set_attr("Y0_threshold", float(scale_value))

跳过量化的OP会保存skip_quant ,即是op_desc._set_attr("skip_quant", True)

上一个OP的输出激活量化信息是下一个OP的输入激活量化信息,而且模拟量化OP的输入量化信息保存在fake_op和OP属性中。所以,上述保存的量化信息存在冗余,这是为了便于预测端获取量化信息(讨论后续是否修改?)。

综上:OP的新增属性名称有skip_quant, out_threshold 和 argname + index + “_threshold"。这些属性不可以在裁剪模型时删除。

##quant axis现状说明 对于有权重op的channel_wise量化,需要quant axis指定权重量化时的channel维度。特定op的quant axis一般不会改变,如conv2d的quant axis为0,mul与conv2d transpose的quant axis为1。

slim端量化时会指定op的quant axis,并保存在fake_channel_wise_quantize_dequantize_abs_max(动态图)或fake_channel_wise_dequantize_max_abs(静态图)中。具体如下:

指定方式:

image image

保存格式:

静态图模型量化导出后保存在fake_channel_wise_dequantize_max_abs的attributes中: image

动态图模型量化导出后保存在fake_channel_wise_quantize_dequantize_abs_max的attributes中: image

目前预测端未感知quant axis属性,直接将特定op的quant axis固定。

LSTM GRU量化

静态图下,静态离线量化方法支持常规OP的量化外,还支持量化LSTM OP的权重,只需要在输入参数quantizable_op_type中新增"lstm"。

预测端适配动态图量化模型

在已经适配了静态图量化模型的基础上,适配动态图量化模型时,只需要新增从fake_quant_dequant_op或者OP本身属性中获取输入激活和权重的量化信息。

wanghaoshuang avatar Nov 30 '21 08:11 wanghaoshuang

你好,问下动态图QAT产出的量化模型,paddle-tensorrt无法正常推理,有没有工具进行转换呢转成下图这种形式 image

marsbzp avatar Apr 01 '22 00:04 marsbzp

@marsbzp 请问在用Paddle-TensorRT推理时,具体报什么错误呢? 理论上,动态图产出的量化模型,也是可以用Paddle-TenorRT推理的。 @yghstill

wanghaoshuang avatar Apr 01 '22 02:04 wanghaoshuang

您好,你提到在动态图下量化训练时,对于模拟量化OP,前后分别插入fake_quant_op和fake_dequant_op,等价于前面插入fake_quant_dequant_op。我想请问能否不将quant与dequant合并,需要做什么修改吗?

mrshaoyang avatar Jul 14 '22 06:07 mrshaoyang