十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

飞桨(PaddlePaddle)QAT INT8 MKL-DNN 量化推理全指南:Quant2Int8OnednnPass 模型转换与基准复现

飞桨(PaddlePaddle)QAT INT8 MKL-DNN 量化推理全指南:Quant2Int8OnednnPass 模型转换与基准复现 飞桨PaddlePaddleQAT INT8 MKL-DNN 量化推理全指南Quant2Int8OnednnPass 模型转换与基准复现【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice 『飞桨』核心框架深度学习机器学习高性能单机、分布式训练和跨平台部署项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle本文以飞桨PaddlePaddle官方测试文档 test/quantization/README.md 为核心脉络系统讲解如何将 PaddleSlim 量化感知训练QAT产出的 Quant 模型通过Quant2Int8OnednnPass即文档所称 Quant2 方案转换为可在 CPU 上利用 oneDNNMKL-DNNINT8 内核高速推理的模型并给出精度/性能基准数据与完整复现命令。读完本文你将掌握 Quant 模型 → INT8 MKL-DNN 模型的完整转换原理、Quant2Int8OnednnPass各阶段实现细节、可配置算子列表以及图像分类与 NLPErnie模型的精度、性能复现方法。0. 背景从 Quant1 到 Quant2 的演进PaddlePaddle 在 CPU 侧基于 MKL-DNNoneDNN的 INT8 量化推理经历了两个阶段Release 1.5 —— Quant1首次打通 MKL-DNN 上的 INT8 量化推理为 GoogleNet、MobileNetV1、MobileNetV2、ResNet50、ResNet101、VGG16、VGG19 等 QAT 模型启用conv2d、mul的 INT8 MKL-DNN 内核精度差约 0.05%。Release 1.6 —— Quant2引入新的转换方案支持更多性能优化与更多 INT8 MKL-DNN 内核推理性能较 Quant1 大幅提升代价是精度差略有增大。Release 1.7 —— Quant2 支持 Ernie新增对 ErnieNLPQAT 模型的 INT8 转换支持。Release 2.0 —— Quant2 继续增强新增 INT8matmul内核、elementwise_add与激活的 inplace 执行并更广泛地兼容 PaddleSlim 的量化感知策略。本文只聚焦 Quant2 方案。在当前的仓库源码中Quant2 方案的实现即为python/paddle/static/quantization/quant2_int8_onednn_pass.py中的Quant2Int8OnednnPass类历史名称Quant2Int8MkldnnPass已自 3.1.0 起标记为 deprecated并建议迁移到Quant2Int8OnednnPass见 quant2_int8_onednn_pass.py。1. 前置条件PrerequisitesPaddlePaddle 2.0 及以上版本文档编写时以 2.0 为基线当前仓库中的实现仍沿用该 Pass 体系使用前请以你所安装的飞桨版本对应的官方安装文档为准。MKL-DNNoneDNN与 MKLINT8 量化推理依赖 MKL-DNN 内核库需在安装/编译飞桨时启用。CPU 指令集最高的性能增益需要支持AVX512指令的 CPU 服务器最佳的 INT8 精度需要支持AVX512 VNNI扩展的 CPU例如 CLX 级别 Intel 处理器。Linux 下可用lscpu查看Flags段是否包含avx512_vnniWindows 下可用coreinfo工具检测。2. 量化方法总览PTQ 与 QATPaddlePaddle 支持两种 INT8 量化路径方案全称特点PTQPost-Training Quantization训练后量化更加自动化对模型准备要求更低QATQuantization-Aware Training量化感知训练通常在同性能下获得更好的精度两条路径都能把 PaddleSlim 产出的模型转成 INT8 模型并在 CPU 上推理。本文聚焦于 QAT 流程中的中间产物Quant 模型→ MKL-DNN INT8 模型这一转换步骤即 Quant2 流程。关于如何用 PaddleSlim 把 FP32 模型训练成 Quant 模型可参考 PaddleSlim 仓库中的 mkldnn_quant demo 文档本文在仓库中的对应测试脚本位于 test/quantization核心对比脚本为 quant2_int8_image_classification_comparison.py。3. Quant2 转换原理Quant2Int8OnednnPass 的八个阶段一个 Quant 模型可以被转换为 INT8 量化模型的前提是图中每个待量化算子都带有足够的量化 scale 信息。整个转换由Quant2Int8OnednnPass完成它通过apply(graph)串联起一系列内部步骤对应源码 quant2_int8_onednn_pass.py。3.1 收集量化 ScaleGathering scales量化 scale 信息来自两个来源可量化算子的out_threshold属性包含算子输出的单值量化 scale对应源码_gather_output_scales_from_attr见 quant2_int8_onednn_pass.py伪量化/反量化算子fake quantize/dequantize它们模拟 FP32→INT8 的量化或反向反量化过程但把量化后的张量值仍以浮点形式保存。伪量化/反量化算子分为三类输入侧置于被量化算子如conv2d之前fake_quantize_moving_average_abs_max、fake_quantize_range_abs_max为算子输入收集单值 scale源码_gather_input_scales_from_fake会断言bit_length 8目前仅支持 8 bit见 quant2_int8_onednn_pass.py权重反量化侧置于被量化算子之后fake_dequantize_max_abs权重张量的单值 scale与fake_channel_wise_dequantize_max_abs权重每个输出通道一个 scale 的向量对应源码_gather_weight_thresholds_from_fake见 quant2_int8_onednn_pass.py输出侧置于被量化算子之后fake_quantize_dequantize_moving_average_abs_max模拟立即量化再反量化为算子输出取得 scale 值。源码中还额外兼容了fake_quantize_dequantize_abs_max与fake_channel_wise_quantize_dequantize_abs_max见 quant2_int8_onednn_pass.py。优先级规则从伪量化/反量化算子收集到的 scale 优先于out_threshold属性中的 scale源码中_add_scale_for_vars采用“不覆盖”策略先写入的伪量化 scale 不会被out_threshold覆盖见 quant2_int8_onednn_pass.py。注意事项面向优化后的图收集 scale量化最终作用于 FP32 优化后的模型因此每个被量化算子输入/输出张量的 scale必须针对优化或融合后算子的输入/输出张量收集。例如原始子图为... → input1 → conv2d → output1 → batch_norm → output2 → relu → output3 → ...FP32 优化后conv2d、batch_norm、relu会被融合为... → input1 → conv2d → output3 → ...则必须为input1与output3收集 scale。支持量化的算子conv2d、depthwise_conv2d、mul、fc、matmul、pool2d、reshape2、transpose2、concat。在源码的量化配置中含权重量化算子还包括conv2d_transpose、matmul_v2见 quant_config.py激活量化算子覆盖elementwise_add、softmax、relu、sigmoid、batch_norm、layer_norm、gru、lstm等更广范围见 quant_config.py。量化算子的连续性决定性能收益图中连续可量化算子序列越长量化收益越大如... → conv2d → conv2d → pool2d → conv2d → conv2d → ...孤立于其他可量化算子之外的单算子量化可能毫无收益甚至拖慢推理如... → swish → fc → softmax → ...。3.2 移除伪算子Removing fake operators图中所有fake_quantize_*与fake_dequantize_*算子被移除。源码中_remove_fake_ops分别对三类伪算子执行节点摘除与输入重连_swap_inputssafe_remove_nodes见 quant2_int8_onednn_pass.py。3.3 反量化权重Dequantizing weightsQuant 模型中conv2d、depthwise_conv2d、mul及matmul的权重被假定为“伪量化”状态——数值落在 INT8 范围内但仍以 float 保存。此处利用fake_dequantize_max_abs与fake_channel_wise_dequantize_max_abs提供的 scale把权重伪反量化回完整 float 数值范围。此刻模型成为一个未优化的干净 FP32 推理模型。源码中_dequantize_weights首先通过_is_int8_weights判断权重张量是否全为整数np.all(np.mod(weight, 1) 0)再按 scale 向量与权重维度匹配关系单值、等于通道数、等于输入维度三种情形执行反量化并写回 scope见 quant2_int8_onednn_pass.py。3.4 优化 FP32 图Optimizing FP32 graph对 FP32 图应用一系列标准优化 Pass。从源码_optimize_fp32_graph可以看到完整的优化流水线见 quant2_int8_onednn_pass.py包括基础清理onednn_placement_pass、simplify_with_basic_ops_pass、is_test_passRNN 融合fc_lstm_fuse_pass、mul_lstm_fuse_pass、fc_gru_fuse_pass、mul_gru_fuse_pass、multi_gru_fuse_pass等卷积融合conv_bn_fuse_pass、conv_eltwiseadd_bn_fuse_pass、conv_bias_onednn_fuse_pass、conv_activation_onednn_fuse_pass等对应文档中conv2d bn → conv2d的说明矩阵乘法融合matmul_v2_scale_fuse_pass、matmul_transpose_reshape_onednn_fuse_pass、matmul_elementwise_add_onednn_fuse_pass、fc_fuse_pass等。最终得到优化后的 FP32 推理模型为 INT8 量化做准备。3.5 计算权重 ScaleComputing weight scales优化融合之后conv2d或fc算子的权重张量数值已发生变化需要重新计算量化 scale。由于权重是静态的推理过程中不变化scale 可直接取张量绝对值的最大值。为提升精度飞桨按输出通道分别计算 scale得到权重张量的 scale 数组per-channel 量化。对应源码_compute_var_scales对conv2d/depthwise_conv2d的Filter沿 axis1、对fc的W沿 axis0 计算1 / max(abs(weights))见 quant2_int8_onednn_pass.py。同时源码还针对fusion_gru/multi_gru、fusion_lstm分别实现了分组权重 scale 计算_compute_gru_weight_scales、_compute_lstm_weight_scales为 NLP 模型如 Ernie 之外的 GRU/LSTM 结构提供支持。3.6 激活的符号处理Taking activations into accountINT8 推理的基本数据类型是有符号 INT8取值范围 -128 到 127。但如果conv2d或fc算子内融合了relu或relu6激活输出已知非负此时输出张量改用无符号 UINT8取值范围 0 到 255为正值提供更宽的表示范围进一步改善精度。源码中_update_relu_output_scales即为此逻辑将 scale 放大 2 倍以填满 uint8 范围见 quant2_int8_onednn_pass.py。注意该函数当前在apply中被注释停用源码注释注明其会导致量化行为不确定最终输出 scale 类型由 scale 传播与量化 Pass 决定。3.7 Scale 传播Propagation of scales部分算子如reshape2、transpose2、最大池化的pool2d变换数据但不改变量化 scale因此 scale 可以原样穿透传播scale算子则按scale属性值成比例更新量化 scale源码_propagate_scales中_update_scale_op_in_scale将 scale 乘以算子scale属性见 quant2_int8_onednn_pass.py。源码中列出的 scale 不变算子包括transpose2、reshape2、pool2d、slice、shape、nearest_interp、nearest_interp_v2、split见 quant2_int8_onednn_pass.py并额外对concat进行输出到输入的 scale 反向回填。该策略最大化减少了图中伪量化/反量化算子的数量使量化所需 scale 信息在量化算子间扩散。3.8 应用量化 PassApplying quantization passes收集齐所有数据后依次应用cpu_quantize_placement_pass标记待量化算子传入ops_to_quantize列表cpu_quantize_pass量化整图传入quant_var_scales与data_layoutcpu_quantize_squash_pass优化 INT8 图int8_scale_calculation_onednn_pass与params_quantization_onednn_pass完成 scale 计算与参数 INT8 化。对应源码_quantize_fp32_graph见 quant2_int8_onednn_pass.py。数据布局方面_get_data_layout在量化conv2d时返回NHWC、否则返回NCHW见 quant2_int8_onednn_pass.py。4. 代码示例在 Python 中应用 Quant2Int8OnednnPass以下是文档给出的最小可运行示例完整实现见 quant2_int8_onednn_pass.pyimport paddle import paddle.static as static from paddle.static.quantization import Quant2Int8OnednnPass from paddle.base.framework import IrGraph from paddle.framework import core # 由 Program 创建 IrGraph graph IrGraph(core.Graph(static.Program().desc), for_testFalse) place paddle.CPUPlace() # 将 IrGraph 转换为 MKL-DNN 支持的 INT8 IrGraph。 # 需要传入待量化算子列表、全局 scope、place、core 与是否开启 debug。 onednn_pass Quant2Int8OnednnPass({conv2d, pool2d}, static.global_scope(), place, core, False) # 对 IrGraph 应用 Quant2Int8OnednnPass onednn_pass.apply(graph)Quant2Int8OnednnPass的构造参数见 quant2_int8_onednn_pass.py说明如下参数含义_ops_to_quantize待量化算子类型集合。为空集合时尝试量化所有可量化算子_op_ids_to_skip量化中跳过的算子 id 集合默认{-1}_scope存放参数的全局作用域用于读取/写回权重_place运行设备经_get_paddle_place归一化通常为paddle.CPUPlace()_core底层 C core 模块_debug是否在每个 Pass 后输出*.dot图文件除apply(graph)外该类还提供prepare_and_optimize_fp32(graph)方法可单独执行 FP32 图优化阶段见 quant2_int8_onednn_pass.py。5. 精度与性能基准以下结果测量自Intel(R) Xeon(R) Gold 6271支持 AVX512 VNNI。性能基准的环境设置export KMP_AFFINITYgranularityfine,compact,1,0 export KMP_BLOCKTIME1并将 Turbo Boost 关闭echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo5.1 图像分类模型精度Top1 / Top5FP32 vs INT8 QuantModelFP32 Top1INT8 Top1Top1 DiffFP32 Top5INT8 Top5Top5 DiffMobileNet-V170.78%70.71%-0.07%89.69%89.41%-0.28%MobileNet-V271.90%72.11%0.21%90.56%90.62%0.06%ResNet10177.50%77.64%0.14%93.58%93.58%0.00%ResNet5076.63%76.47%-0.16%93.10%92.98%-0.12%VGG1672.08%71.73%-0.35%90.63%89.71%-0.92%VGG1972.57%72.12%-0.45%90.84%90.15%-0.69%性能单线程images/sRatio INT8/FP32ModelFP32 (images/s)INT8 Quant (images/s)RatioMobileNet-V174.05196.982.66MobileNet-V288.60187.672.12ResNet1017.2026.433.67ResNet5013.2347.443.59VGG163.4710.202.94VGG192.838.673.06可以看到INT8 推理普遍获得2.1x3.7x 的吞吐提升而 Top1 精度差基本控制在 ±0.5% 以内VGG 系列 Top5 略大。FP32 性能数据来自飞桨 INT8 MKL-DNN 训练后量化PTQ基准文档。5.2 NLP 模型Ernie精度准确率ModelFP32 AccuracyQuant INT8 AccuracyDiffErnie80.20%79.44%-0.76%性能Latency msRatio FP32/INT8ModelThreadsFP32 Latency (ms)INT8 Latency (ms)RatioErnie1 thread237.2179.262.99xErnie20 threads22.0812.571.76xErnie 单线程延迟降低约 3 倍多线程下仍有 1.76x 的加速。6. 复现基准结果以下以 ResNet50 为例说明图像分类模型的精度与性能复现步骤NLP 的 Ernie 复现另有专门说明。6.1 准备数据集cd /PATH/TO/PADDLE python paddle/fluid/inference/tests/api/full_ILSVRC2012_val_preprocess.py转换后的数据二进制文件默认保存在$HOME/.cache/paddle/dataset/int8/download/int8_full_val.bin。6.2 准备模型下载并解压 Quant 模型mkdir -p /PATH/TO/DOWNLOAD/MODEL/ cd /PATH/TO/DOWNLOAD/MODEL/ export QUANT_MODEL_NAMEResNet50 export QUANT_MODEL_ARCHIVE${QUANT_MODEL_NAME}_qat_model.tar.gz wget http://paddle-inference-dist.bj.bcebos.com/int8/QAT_models/${QUANT_MODEL_ARCHIVE} mkdir ${QUANT_MODEL_NAME} tar -xvf ${QUANT_MODEL_ARCHIVE} -C ${QUANT_MODEL_NAME}将QUANT_MODEL_NAME换成ResNet101、MobileNetV1、MobileNetV2、VGG16、VGG19可下载对应模型。此外还有使用不同 scale 采集方式的 Quant 模型变体export QUANT_MODEL_NAMEResNet50_qat_perf export QUANT_MODEL_ARCHIVE${QUANT_MODEL_NAME}.tar.gz wget http://paddle-inference-dist.bj.bcebos.com/int8/QAT_models/${QUANT_MODEL_ARCHIVE} mkdir ${QUANT_MODEL_NAME} tar -xvf ${QUANT_MODEL_ARCHIVE} -C ${QUANT_MODEL_NAME}可选的QUANT_MODEL_NAME取值及特点ResNet50_qat_perf、MobileNet_qat_perf输入/输出 scale 记录在fake_quantize_moving_average_abs_max算子中权重 scale 记录在fake_dequantize_max_abs算子中ResNet50_qat_range输入/输出 scale 记录在fake_quantize_range_abs_max算子与out_threshold属性中权重 scale 在fake_dequantize_max_abs中ResNet50_qat_channelwise输入/输出 scale 记录在fake_quantize_range_abs_max算子与out_threshold属性中权重 scale 在fake_channel_wise_dequantize_max_abs算子中per-channel 权重量化。下载用于精度对比的干净 FP32 模型cd /PATH/TO/DOWNLOAD/MODEL/ export FP32_MODEL_NAMEresnet50 export FP32_MODEL_ARCHIVE${FP32_MODEL_NAME}_int8_model.tar.gz wget http://paddle-inference-dist.bj.bcebos.com/int8/${FP32_MODEL_ARCHIVE} mkdir ${FP32_MODEL_NAME} tar -xzvf ${FP32_MODEL_ARCHIVE} -C ${FP32_MODEL_NAME}FP32_MODEL_NAME还可取Res101、mobilenetv1、mobilenet_v2、VGG16、VGG19。6.3 精度基准使用quant2_int8_image_classification_comparison.py复现精度结果该脚本源码位于 test/quantization/quant2_int8_image_classification_comparison.py必选参数--quant_model将被转换为 INT8 模型的 Quant 模型路径--fp32_modelFP32 模型路径其精度将与 INT8 模型对比--infer_data验证数据集路径。可选参数--ops_to_quantize逗号分隔的待量化算子类型列表。若不指定则尝试量化所有可量化算子且只有 Quant 模型中带有量化 scale 的算子才会被量化。选择该列表时需注意只考虑支持量化的算子列表中出现在模型中的每个可量化算子都必须在模型中有量化 scale否则该算子的量化会被跳过并提示缺失 scale 的变量名有时量化全部可量化算子并非最优参考上文“收集量化 Scale”一节的注意事项 3。可以通过多次以不同算子列表运行基准并比较结果来寻找最优配置。上述图像分类模型通常使用conv2d和pool2d。--op_ids_to_skip量化中跳过的算子 id 列表。先以--debug运行打开生成的int8_number_cpu_quantize_placement_pass.dot文件找到算子名称旁括号内的 id 编号即可。--debug生成一系列*.dot文件记录转换各步骤之后的模型图DOT 格式说明见 Graphviz 文档。文件保存在当前目录可用 Graphviz 工具打开Linux 可用xdotWindows 可用dot。脚本还支持--batch_size、--batch_num、--acc_diff_threshold、--skip_batch_num、--targets等参数见 quant2_int8_image_classification_comparison.py。运行命令cd /PATH/TO/PADDLE OMP_NUM_THREADS28 FLAGS_use_onednntrue python python/paddle/static/quantization/slim/tests/quant2_int8_image_classification_comparison.py --quant_model/PATH/TO/DOWNLOADED/QUANT/MODEL --fp32_model/PATH/TO/DOWNLOADED/FP32/MODEL --infer_data$HOME/.cache/paddle/dataset/int8/download/int8_full_val.bin --batch_size50 --batch_num1000 --acc_diff_threshold0.01 --ops_to_quantizeconv2d,pool2d注意int8_full_val.bin数据集包含约 50000 张图片精度基准耗时较长。建议把OMP_NUM_THREADS设为服务器物理核心数以加速。6.4 性能基准性能复现需设置OMP_NUM_THREADS1与--batch_size1。步骤 1将 Quant 模型转换为 INT8 模型并保存使用save_quant_model.py同样支持--ops_to_quantizecd /PATH/TO/PADDLE/build python ../python/paddle/static/quantization/slim/tests/save_quant_model.py --quant_model_path/PATH/TO/DOWNLOADED/QUANT/MODEL --int8_model_save_path/PATH/TO/SAVE/QUANT/INT8/MODEL --ops_to_quantizeconv2d,pool2d步骤 2运行 C-API 性能基准测试cd /PATH/TO/PADDLE/build OMP_NUM_THREADS1 paddle/fluid/inference/tests/api/test_analyzer_quant_image_classification ARGS --enable_fp32false --with_accuracy_layerfalse --int8_model/PATH/TO/SAVED/QUANT/INT8/MODEL --infer_data$HOME/.cache/paddle/dataset/int8/download/int8_full_val.bin --batch_size1 --paddle_num_threads16.5 与当前仓库的对应关系需要说明的是上述命令中的脚本路径如python/paddle/static/quantization/slim/tests/是文档编写时期的布局。在当前仓库中Quant2Int8OnednnPass的实现位于 python/paddle/static/quantization/quant2_int8_onednn_pass.py精度对比测试脚本位于 test/quantization/quant2_int8_image_classification_comparison.pyNLP 相关脚本包括 test/quantization/quant2_int8_nlp_comparison.py 与 test/quantization/quant2_int8_lstm_model.py量化配置支持量化的算子字典、量化位宽、量化范围等位于 python/paddle/static/quantization/quant_config.py其中BaseQuantizer默认quant_bits8、量化范围为 -128127与本文 INT8 语义一致。若以源码方式编译飞桨基准命令应在构建目录build下执行并确保编译时启用了 MKL-DNN 支持。7. 小结与最佳实践Quant2Quant2Int8OnednnPass为 QAT 用户提供了一条“训练期伪量化 → 推理期真量化”的完整链路从 Quant 模型中收集 scale、摘除伪算子、反量化并优化 FP32 图、按通道计算权重 scale、传播 scale最终通过cpu_quantize_pass与cpu_quantize_squash_pass生成 INT8 MKL-DNN 图。实践要点可归纳为硬件先行确认 CPU 支持 AVX512最好含 VNNI否则收益与精度都会打折算子选择优先量化连续的可量化算子链conv2d/pool2d组合孤立算子宁可跳过配合--op_ids_to_skip精度验证用--acc_diff_threshold设定可接受精度差阈值配合--debug输出的 DOT 图定位问题性能复现性能基准务必固定线程数OMP_NUM_THREADS、批大小与 CPU 频率策略关闭 Turbo Boost保证对比可重复关注废弃 API新代码应使用Quant2Int8OnednnPass避免使用已标记 deprecated 的Quant2Int8MkldnnPass。【免费下载链接】PaddlePArallel Distributed Deep LEarning: Machine Learning Framework from Industrial Practice 『飞桨』核心框架深度学习机器学习高性能单机、分布式训练和跨平台部署项目地址: https://gitcode.com/GitHub_Trending/pa/Paddle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表