5. PTQ量化
后摩大道® M50 HMQuantool量化工具 支持PTQ(Post-Training Quantization,训练后量化),并支持混合精度等方法对模型调优。
PTQ量化是一种无需重新训练,即可对预训练模型进行量化的方法。HMQuantool量化工具的PTQ量化功能,用于将模型中权重(weight)和激活值(activation)从浮点数转换为具有低尾数位宽的低精度数据类型,并导出HMONNX格式的量化模型文件。生成的量化模型能够在后摩M50设备上实现高效推理,也可在CPU和GPU上进行精度仿真验证。
5.1. 量化策略
随着深度学习模型规模的不断增长,部署模型所需的计算资源和存储空间也急剧上升,因此降低推理过程中的计算开销与存储成本至关重要。HMQuantool量化工具通过灵活的量化策略,将原始FP16 浮点模型中的权重转换为低精度INT4、INT8、bFP16或bFP24格式,并将激活值转换为低精度bFP16或bFP24格式,从而显著提升模型在推理场景下的性能和部署效率。
量化策略主要分为两个部分:
尾数位量化 :通过精确控制浮点数尾数的位宽来权衡数值精度与存储开销,直接影响量化后数值的细节保留程度。
量化格式 :基于缩放因子(scale)机制,提供两种数值表示方式,来进一步优化存储和计算效率:
SSFP(Shared Scale Floating Point,共享缩放因子浮点格式)
SEFP(Shared Exponent Floating Point,共享指数浮点格式)
HMQuantool量化工具量化后的数据类型全面适配后摩M50设备的计算格式,确保在后摩M50设备上实现高效推理。
量化策略可通过 QuantScheme类 中 quant_type 参数设置。
5.1.1. 尾数位量化
一个浮点数通常由以下结构定义:
符号位: 标识数值的正负。
指数位: 定义数值的量级或范围。
尾数位: 确定数值的精确程度,即小数点后的有效数字位数。
在浮点数表示中,尾数位是决定数值精度的核心组成部分。尾数位比特数量与数值精度呈正相关。尾数位比特数越多,数值可表示的粒度越细,舍入误差越小。反之,尾数比特数越少,精度越低,但存储和计算效率更高。
尾数位量化策略中,将模型权重量化为具有8-bit或4-bit尾数位的低精度格式,将激活值量化为具有8-bit至16-bit范围内的尾数位低精度格式。该策略完全满足后摩M50芯片在推理过程中对数据格式的需求,能够有效降低模型的计算和存储开销,在保持模型精度的同时显著提升推理性能和部署效率,特别适用于大规模模型压缩和部署场景。
下表详细阐述各选项对模型精度、内存占用和推理性能的影响。
量化项 |
尾数位 |
量化后内存占用 |
量化后计算性能 |
量化后精度 |
|---|---|---|---|---|
权重 |
8-bit |
中 |
高 |
高 |
权重 |
4-bit |
低 |
最高 |
低 |
激活值 |
8 ~ 16 bit |
中到高 |
高到中 |
高 |
5.1.2. 量化格式
除针对尾数位的量化策略,HMQuantool量化工具还通过缩放因子机制进一步优化存储和计算效率。该机制支持两种共享比例数据格式,能够在降低内存占用的同时尽可能保持数值精度:
5.1.2.1. SSFP
SSFP为共享缩放因子浮点格式。该格式将模型权重按组划分,每组内所有值共享一个FP16 浮点类型的缩放因子。量化时,用该缩放因子对原始权重进行缩放,从而实现内存压缩。
5.1.2.2. SEFP
SEFP为共享指数浮点格式,将模型的权重或激活值按组划分,每组内所有数值共享一个指数部分。组内各值只需要单独存储其尾数部分。
该格式是根据“Microscaling Data Formats for Deep Learning” 论文实现,了解详情可参看 https://arxiv.org/pdf/2310.10537 。
5.2. 量化基本流程
以下步骤以ResNet50模型量化为例,展示了HMQuantool工具在CV模型上的典型量化流程。示例中所用的原始模型为 FP16 类型的 ONNX 浮点模型。由于HMQuantool 仅支持 FP16 类型的 ONNX 模型作为输入,若原始模型为其他数据类型,需用户在使用前自行转换为 FP16 格式。此外,为获得更优性能,推荐使用 ONNX 格式作为输入模型。
导入依赖库。
import argparse from pathlib import Path import onnx import torch from xhquant.api import ( DeviceType, HMONNXGoldenInference, QuantScheme, convert_onnx_to_hmonnx, create_quant_config, xhquant_init, )准备输入参数与模型数据。
设置原始ONNX模型路径。示例如下:
onnx_file = "data/models/resnet/resnet50_224x224.onnx"
设置模型输入。示例如下:
# Load the ONNX model from file onnx_model = onnx.load(onnx_file) # Extract the input shape from the first input of the ONNX model input_shape = [int(dim.dim_value) for dim in onnx_model.graph.input[0].type.tensor_type.shape.dim] # Set the batch size for the input shape batch_size = 1 input_shape[0] = batch_size # Generate a random input tensor with the specified shape and float32 data type input = torch.randn(input_shape, dtype=torch.float32)
设置量化后输出路径。示例如下:
# Build working directory path based on model name and input shape onnx_name = Path(onnx_file).stem str_shape = "x".join([str(dim) for dim in input_shape]) onnx_name = f"{onnx_name}_{str_shape}" work_dirs = Path("work_dirs") / onnx_name work_dirs.mkdir(exist_ok=True, parents=True) # Set output path for quantized HMONNX model target_device = DeviceType.XH2a out_hmonnx_file = work_dirs / "hmonnx" / f"{onnx_name}_{target_device}.onnx" out_hmonnx_file.parent.mkdir(exist_ok=True, parents=True) out_hmonnx_file: str = str(out_hmonnx_file)
调用 xhquant_init 接口,初始化量化环境。示例如下:
xhquant_init(None, debug=args.debug)
通过 Class QuantScheme 设置量化方案。示例如下:
quant_scheme = QuantScheme(target_device=DeviceType.XH2a, quant_type=w8a8h1_sefp)
示例中,设置推理目标设备类型为后摩M50芯片(
DeviceType.XH2a),并设置权重与激活均保留 8-bit尾数位,使用SEFP格式的量化方案(w8a8h1_sefp)。调用 create_quant_config 接口,构建量化配置。示例如下:
quant_config = create_quant_config(quant_scheme)
调用 convert_onnx_to_hmonnx 接口,量化模型并导出量化后HMONNX模型。示例如下:
convert_onnx_to_hmonnx( onnx_file, [input], DeviceType.XH2a, out_hmonnx_file, quant_config=quant_config, input_names=["images"], output_names=["cls_score"], )(可选)调用 HMONNXGoldenInference 接口,生成Goden数据,用于验证量化精度是否达标。示例如下:
device = "cuda" if torch.cuda.is_available() else "cpu" session = HMONNXGoldenInference(out_hmonnx_file) session.to(device) # Set the device for inference session.save_golden = True # Enable saving of golden outputs # Set the directory where golden outputs will be saved session.golden_dir = work_dirs / "hmonnx/golden" session.step = 0 session(input.half().to(device)) # Model inference
完整示例参看 ResNet50模型量化示例。