You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

static quantization能否让节点直接接收前层量化输出?实践困惑

关于ONNX Runtime静态量化节点间直接传递量化值的疑问

我在学习int8量化技术时,希望找到无需对节点输出执行反量化再量化操作,即可将其传递给下一层的方法,因此了解了static quantization与dynamic quantization的定义。根据ONNX Runtime文档,二者的区别在于(反)量化参数的计算时机,而非实际的(反)量化流程本身。但部分文章及论坛内容指出,static quantization可在节点间直接传递量化值,无需转换为float再转回int。

于是我使用onnxruntime.quantization.quantize_static手动量化模型,代码如下:

import torch
import torchvision as tv
import onnxruntime
from onnxruntime import quantization


MODEL_PATH = "best480x640.onnx"
MODEL_OPTIMIZED_PATH = "best480x640_optimized.onnx"
QUANTIZED_MODEL_PATH = "best480x640_quantized.onnx"


class QuntizationDataReader(quantization.CalibrationDataReader):
    def __init__(self, torch_ds, batch_size, input_name):

        self.torch_dl = torch.utils.data.DataLoader(
            torch_ds, batch_size=batch_size, shuffle=False)

        self.input_name = input_name
        self.datasize = len(self.torch_dl)

        self.enum_data = iter(self.torch_dl)

    def to_numpy(self, pt_tensor):
        return (pt_tensor.detach().cpu().numpy() if pt_tensor.requires_grad
                else pt_tensor.cpu().numpy())

    def get_next(self):
        batch = next(self.enum_data, None)
        if batch is not None:
            return {self.input_name: self.to_numpy(batch[0])}
        else:
            return None

    def rewind(self):
        self.enum_data = iter(self.torch_dl)


preprocess = tv.transforms.Compose([
    tv.transforms.Resize((480, 640)),
    tv.transforms.ToTensor(),
    tv.transforms.Normalize(
        mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

ds = tv.datasets.ImageFolder(root="./calib/", transform=preprocess)

# optimisations
quantization.shape_inference.quant_pre_process(
    MODEL_PATH, MODEL_OPTIMIZED_PATH, skip_symbolic_shape=False)

quant_ops = {"ActivationSymmetric": False, "WeightSymmetric": True}
ort_sess = onnxruntime.InferenceSession(
    MODEL_PATH, providers=["CPUExecutionProvider"])
qdr = QuntizationDataReader(
    ds, batch_size=1, input_name=ort_sess.get_inputs()[0].name)
quantized_model = quantization.quantize_static(
    model_input=MODEL_OPTIMIZED_PATH,
    model_output=QUANTIZED_MODEL_PATH,
    calibration_data_reader=qdr,
    extra_options=quant_ops
)

但通过Netron查看量化后模型的图结构时,发现添加的QuantizeLinear/DequantizeLinear节点是量化后立即执行反量化,Conv、Mul等节点的输入仍为float32张量。我想确认:

  1. static quantization是否允许节点直接接收前层的量化输出?
  2. 我的量化流程存在什么问题?

解答

1. 静态量化是否支持节点间直接传递量化值?

是的,ONNX Runtime的静态量化确实支持节点间直接传递int8量化值,也就是常说的「量化节点融合」或「int8通路优化」。这种优化会把相邻的QuantizeLinear -> DequantizeLinear对合并,或者让Conv、MatMul等算子直接接收前层的int8输入,避免反复在int8和float32之间转换。

需要注意的是:这种优化不会体现在导出的ONNX模型结构里——Netron看到的是原始的量化节点布局,而实际推理时,ONNX Runtime的执行器会自动做图优化,把可融合的节点合并,实现int8数据的直接传递。

2. 当前量化流程的问题

你的代码里有几个可能导致「量化后立即反量化」的点:

  • 缺少子图优化配置:默认情况下,ONNX Runtime的静态量化可能只完成基础的权重量化,没有触发子图级的量化通路优化。需要在extra_options里明确开启相关选项,让工具识别并融合可形成int8通路的节点。
  • 量化规则限制:如果模型中存在不支持量化的自定义算子、或者校准数据分布不具代表性,量化工具会自动插入DequantizeLinear节点, fallback 到float32执行以保证精度。
  • 激活量化配置不足:仅设置ActivationSymmetric不够,还需要明确开启激活值的量化传递逻辑。

修正后的量化配置示例

修改extra_options,添加子图优化和量化强制选项:

quant_ops = {
    "ActivationSymmetric": False,
    "WeightSymmetric": True,
    "EnableSubgraph": True,  # 开启子图级量化融合优化
    "ForceQuantizeNoInputCheck": True,  # 强制量化符合条件的节点
    "QuantizeBias": True  # 量化偏置项,完善int8通路
}

量化完成后,可以通过ONNX Runtime的性能分析工具(如onnxruntime.PerfTrace)验证是否真的在执行int8算子,而非float32 fallback。

内容的提问来源于stack exchange,提问作者Andrea Tedeschi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 11:22:07