Skip to main content

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芯片在推理过程中对数据格式的需求,能够有效降低模型的计算和存储开销,在保持模型精度的同时显著提升推理性能和部署效率,特别适用于大规模模型压缩和部署场景。

下表详细阐述各选项对模型精度、内存占用和推理性能的影响。

表 5.1 量化权重和激活值尾数位说明 量化项尾数位量化后内存占用量化后计算性能量化后精度
权重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 格式作为输入模型。

  1. 导入依赖库。
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,
)
  1. 准备输入参数与模型数据。

    • 设置原始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)
  1. 调用 xhquant_init 接口,初始化量化环境。示例如下:
xhquant_init(None, debug=args.debug)
  1. 通过 Class QuantScheme 设置量化方案。示例如下:
quant_scheme = QuantScheme(target_device=DeviceType.XH2a, quant_type="w8a8h1_sefp")

示例中,设置推理目标设备类型为后摩M50芯片(DeviceType.XH2a),并设置权重与激活均保留 8-bit尾数位,使用SEFP格式的量化方案(w8a8h1_sefp)。

  1. 调用 create_quant_config 接口,构建量化配置。示例如下:
quant_config = create_quant_config(quant_scheme)
  1. 调用 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"],
)
  1. (可选)调用 HMONNXGoldenInference 接口,生成Golden数据,用于验证量化精度是否达标。示例如下:
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模型量化示例