PaddleSlim
PaddleSlim copied to clipboard
量化模型格式解释
问题1
fake_quant_xxx算子的InScale和OutScale,在计算下一个节点的输入Scale的时候,应该取InScale还是OutScale除((1 << (bit_length - 1)) - 1)
问题2
上面这张图里面的各种fake算子,是否有些是废弃掉的?
问题1
fake_quant_xxx算子的InScale和OutScale,在计算下一个节点的输入Scale的时候,应该取InScale还是OutScale除((1 << (bit_length - 1)) - 1)
答:都可以;因为InScale和OutScale是Inplace的Accumulator, 它们指向的是同一个变量,如下图所示:
在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
问题2
上面这张图里面的各种fake算子,是否有些是废弃掉的?
答:现阶段,这些op都有用,只是用在不同的场景;比如动态图和静态图用的op就不太一样。下边我贴出了 @juncaipeng 整理的当前量化模型格式,其中可以看出各个quant op用在什么场景。
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)保存输入权重的量化信息(如下图)。
对于特定需要统计输入激活的非模拟量化OP,fake_quant_dequant op 保存输入激活的量化信息(如下图)。
对于常见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。
对于所有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(静态图)中。具体如下:
指定方式:
保存格式:
静态图模型量化导出后保存在fake_channel_wise_dequantize_max_abs的attributes中:
动态图模型量化导出后保存在fake_channel_wise_quantize_dequantize_abs_max的attributes中:
目前预测端未感知quant axis属性,直接将特定op的quant axis固定。
LSTM GRU量化
静态图下,静态离线量化方法支持常规OP的量化外,还支持量化LSTM OP的权重,只需要在输入参数quantizable_op_type中新增"lstm"。
预测端适配动态图量化模型
在已经适配了静态图量化模型的基础上,适配动态图量化模型时,只需要新增从fake_quant_dequant_op或者OP本身属性中获取输入激活和权重的量化信息。
你好,问下动态图QAT产出的量化模型,paddle-tensorrt无法正常推理,有没有工具进行转换呢转成下图这种形式
@marsbzp 请问在用Paddle-TensorRT推理时,具体报什么错误呢? 理论上,动态图产出的量化模型,也是可以用Paddle-TenorRT推理的。 @yghstill
您好,你提到在动态图下量化训练时,对于模拟量化OP,前后分别插入fake_quant_op和fake_dequant_op,等价于前面插入fake_quant_dequant_op。我想请问能否不将quant与dequant合并,需要做什么修改吗?