深夜的城东老巷,张记红烧肉馆的后厨亮着灯。墙上挂着一台从未落灰的电子秤——那是三十年前开业时买的,精确到 0.1 克。但真正掌勺的张师傅,撒盐靠手抖,倒酱油看勺沿,糖用两指一捻。电子秤成了“镇店之宝”,真正出菜的,是那一勺半勺的手感。出菜快、味道稳,客人们只知道“老张做的肉就是香”。 深度学习模型,就是一位背着电子秤到处跑的大厨:每个参数都用 FP32(32 位浮点数)存着,精确到小数点后七位,可模型动辄上亿参数,内存吃紧、推理缓慢。而 ONNX Runtime 的量化支持,就是给这位大厨换上一套“一勺、半勺”的轻便量具——把 FP32 换成 INT8(8 位整数)。模型体积缩到四分之一,推理速度提两到四倍,而味道(精度)几乎不变。 这篇文章,我们将拆开这套“量具”:看它怎么做到四倍压缩、两条换量具的路线、标尺上的刻度与零点,以及 ONNX Runtime 这条“中央厨房流水线”如何帮你一键完成量化部署。

岗位关联

量化是 AI 模型走向手机、摄像头、云端高并发服务的“必经之秤”。掌握 ONNX Runtime 的量化支持,你就能用四分之一的内存、两倍的推理速度,把大模型稳稳送上生产环境——这是 AI 工程化能力中最实在的加分项。

目录
  1. 可视化演示
  2. 量化初识
  3. 量化类型
  4. 量化原理
  5. 关键参数
  6. 量化流程
  7. 算子与性能
  8. 部署实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示ONNX Runtime量化:FP32→INT8四阶段工作流(加载/参数计算/图改写/序列化),动态与静态量化分类对比,及INT8内存4GB→1GB与2-4倍加速收益。

本节配有交互动效(进入视口后自动加载,约 3KB)。

量化四阶段工作流 LD模型加载→SC量化参数计算 RW图改写与算子替换→SZ序列化 FP32→INT8 动态量化仅需量化权重,静态量化需校准。

量化初识

张师傅的电子秤,精确到小数点后七位(FP32),但真正决定红烧肉味道的,是盐和糖的“量级”——一勺还是一撮。神经网络也一样:上亿个权重用 FP32 存,每个参数占 4 字节,几百 MB 的模型在手机和服务器上都是沉重的负担。研究发现,大部分权重对精度的敏感度远低于我们的想象——用 8 位整数(INT8)去近似,误差微乎其微。

于是量化(Quantization)登场了:把 FP32 的浮点权重和激活值,映射到 INT8 的整数区间。整数只有 256 个档位(-128 到 127),但每一个档位都像大厨的“一勺”,稳稳接住原始数值的大致位置。模型体积直接从几百 MB 降到几十 MB,推理时整数运算比浮点运算快得多——因为整数乘法不需要复杂的指数对齐。

量化不是免费的午餐,但它是一顿“用勺子换电子秤”的划算午餐——大厨的手感足够好,掉在地上的那零点几克盐,食客根本尝不出来。而省下的内存和算力,换来的是模型在手机、摄像头、边缘设备上“炉火旺盛”地跑起来。

体积比=4  字节 (FP32)1  字节 (INT8)=4:1\text{体积比} = \dfrac{4\;\text{字节 (FP32)}}{1\;\text{字节 (INT8)}} = 4 : 1
FP32 每个参数占 4 字节,INT8 只占 1 字节,所以量化后模型体积自然缩到原来的四分之一。这个 4:1 是量化最直观的红利。
  1. 体积比记号体积比表达量化前后每个参数占用的存储空间之比,是衡量压缩效果的第一指标。
    体积比=FP32 单参数体积INT8 单参数体积\text{体积比} = \dfrac{\text{FP32 单参数体积}}{\text{INT8 单参数体积}}
  2. FP32 单参数体积FP32 是 32 位单精度浮点,占 4 字节,可表示约 \(10^{-38}\) 到 \(10^{38}\) 的数值范围。
    4  字节=4×8  位=32  位4\;\text{字节} = 4 \times 8\;\text{位} = 32\;\text{位}
  3. INT8 单参数体积INT8 是有符号 8 位整数,占 1 字节,只能表示 -128 到 127 共 256 个离散值。
    1  字节=8  位1\;\text{字节} = 8\;\text{位}
  4. 两者相除得 41:4 字节除以 1 字节得到体积压缩为四分之一,这就是量化的“轻便量具”红利。
    4  字节1  字节=4\dfrac{4\;\text{字节}}{1\;\text{字节}} = 4

量化类型

换量具有两条路线。第一条:菜已经做好了,直接端上桌请食客试吃,然后换上新量具“凭手感重做一遍”——这叫训练后量化(PTQ,Post-Training Quantization)。第二条:从备菜开始就坚持用新量具,边炒边调整火候,让手感和电子秤分毫不差——这叫量化感知训练(QAT,Quantization-Aware Training)。

PTQ 的优势是快:不需要重新训练模型,拿现成的 FP32 模型直接转换,几分钟搞定。代价是精度损失不可控,如果模型对数值特别敏感,PTQ 可能“咸了”或“淡了”。QAT 则在训练过程中把量化误差模拟进去,让网络主动适应“勺子的一抖”,精度更高,但需要重新走一遍训练流程,耗时以天计。

量具本身也有讲究。对称量化(Symmetric)的零点在正中间,像常见的天平;非对称量化(Asymmetric)的零点可以偏移,像从容器自重开始计数的台秤。INT8 对称量化的刻度因子(scale)这样算:

动态量化和静态量化则是“何时定刻度”的区别。动态量化在推理时逐层临时算 scale 和零点,灵活但慢;静态量化在部署前用校准数据把所有刻度标定好,推理时只查表,快如闪电。大厨的做法一般是:提前把常用菜的刻度标好挂在墙上(静态),而不是每道菜都临时掏电子秤(动态)。

s=max⁡(∣rmax⁡∣, ∣rmin⁡∣)2b−1−1=max⁡(∣rmax⁡∣, ∣rmin⁡∣)127s = \dfrac{\max(|r_{\max}|,\,|r_{\min}|)}{2^{b-1} - 1} = \dfrac{\max(|r_{\max}|,\,|r_{\min}|)}{127}
对称量化把浮点数的最大绝对值映射到 INT8 的正向极值 127,scale 就是“每个整数刻度代表多大的浮点步长”。
  1. 对称量化的对称区间对称量化把浮点范围对称地映射到以 0 为中心的整数区间,INT8 的完整范围是 -128 到 127。
    [−128, 127][-128,\,127]
  2. 分子的选取取 \(|r_{\max}|\) 和 \(|r_{\min}|\) 中的较大者,保证所有浮点值都能被表示,不溢出。
    max⁡(∣rmax⁡∣, ∣rmin⁡∣)\max(|r_{\max}|,\,|r_{\min}|)
  3. 分母的来由INT8 有符号整数的正半轴范围是 0 到 127,共 127 格,用 \(2^{8-1}-1\) 表示。
    28−1−1=1272^{8-1} - 1 = 127
  4. scale 的物理意义scale 表示每个整数单位对应的浮点跨度,相当于标尺上每格的宽度。
    s=浮点最大绝对值127s = \dfrac{\text{浮点最大绝对值}}{127}

量化原理

标尺上每格的宽度叫 scale(缩放因子),零刻度的位置叫 zero_point(零点)。把一段浮点连续值映射到整数离散值,就像用一把有刻度的标尺去量一块彩色的绸缎:先数出“这块绸缎相当于几格”,四舍五入取整;读回时,用格数乘以刻度宽度,就还原出近似长度。

以 0.7 这个浮点数为例,若 scale = 0.1、zero_point = 0,则量化结果是 round(0.7 / 0.1) + 0 = 7。反量化时 7 × 0.1 = 0.7,完美还原。现实中 scale 不会这么整齐,所以反量化结果会有舍入误差——这是量化精度损失的唯一来源。

这把“标尺”是量化世界的基本语言。ONNX Runtime 内部的一切量化算子,本质都是在这对公式上做文章——选定合适的 scale 和 zero_point,让勺子落下的每一勺都恰到好处。

q=round ⁣(rs)+z,r≈s (q−z)q = \text{round}\!\left(\dfrac{r}{s}\right) + z,\qquad r \approx s\,(q - z)
量化公式把浮点数 r 除以刻度 s、四舍五入后加上零点偏移 z,得到整数 q;反量化用整数减去 z 再乘 s,还原近似浮点值。这就是量化最核心的一对映射。
  1. 除法换算格数浮点值 r 除以刻度 s,得到“该值相当于几格”,例如 0.7 ÷ 0.1 = 7 格。
    rs\dfrac{r}{s}
  2. round 四舍五入格数往往不是整数,用 round 取最近整数,这一步引入舍入误差。
    round ⁣(rs)\text{round}\!\left(\dfrac{r}{s}\right)
  3. 加零点偏移对称量化 z=0 可省略;非对称量化需加 z 把整数区间对准浮点范围。
    q=round ⁣(rs)+zq = \text{round}\!\left(\dfrac{r}{s}\right) + z
  4. 反量化还原解码时先减零点再乘刻度,得到浮点近似值,约等号表示存在舍入误差。
    r≈s (q−z)r \approx s\,(q - z)

关键参数

换上新量具之前,大厨会做一桌菜请老食客试吃,根据“咸了、淡了”的反馈微调刻度——这个过程叫校准(Calibration)。校准数据集(calibration dataset)必须是真实场景中有代表性的样本组合:如果做的是红烧肉,就不能拿青菜来定盐的刻度。校准数据的分布若与真实推理数据偏差太大,量化出的模型精度就会明显下滑。

量化的粒度(granularity)也决定精度。per-tensor 量化:整层张量共用一个 scale 和 zero_point,就像一整锅菜只用一个勺子舀盐,快但粗;per-channel 量化:每个输出通道单独一套刻度,像每种食材各配一把专用小勺,精度更高,计算开销略大。现代模型权重分布不均时,per-channel 几乎是不二之选。

校准方法上,最常用的是 min-max 校准:直接在校准集上统计张量的最大值和最小值,由此确定 scale 和 zero_point:

大厨的经验之谈:校准时不要只用一桌菜定刻度。多准备几组有代表性的样本(不同火候、不同食材),综合出来的刻度才稳。同理,校准数据集越大越多样,量化模型的鲁棒性越好。

s=rmax⁡−rmin⁡qmax⁡−qmin⁡,z=qmin⁡−round ⁣(rmin⁡s)s = \dfrac{r_{\max} - r_{\min}}{q_{\max} - q_{\min}},\qquad z = q_{\min} - \text{round}\!\left(\dfrac{r_{\min}}{s}\right)
min-max 校准用浮点的实际数据范围除以整数的可用范围,得到刻度;再用最小浮点值反推零点。数据范围定得准,标尺才量得准。
  1. 统计极值在校准数据集上跑一遍模型,记录每个张量的最大值 \(r_{\max}\) 和最小值 \(r_{\min}\)。
    rmax⁡,  rmin⁡r_{\max},\;r_{\min}
  2. 计算 scale浮点跨度除以整数跨度(如 255 或 127),即每格的浮点宽度。
    s=rmax⁡−rmin⁡qmax⁡−qmin⁡s = \dfrac{r_{\max} - r_{\min}}{q_{\max} - q_{\min}}
  3. 推算 zero_point用最小浮点值除以 scale 得到最小浮点值对应的整数格数,取整后得到零点。
    z=qmin⁡−round ⁣(rmin⁡s)z = q_{\min} - \text{round}\!\left(\dfrac{r_{\min}}{s}\right)

量化流程

ONNX Runtime 提供了一条完整的“中央厨房流水线”:读菜谱(加载 ONNX 模型)→ 请食客试菜(校准)→ 换量具(quantize_static 量化)→ 重新上菜(保存 INT8 模型)。所有步骤封装在 onnxruntime.quantization 工具链中,开发者只需提供模型路径和校准数据读取器。

核心 API 是 quantize_static,它负责把 FP32 模型转换为静态量化的 INT8 模型。函数内部会遍历计算图,把支持的算子替换为量化版本,并插入 QuantizeLinear / DequantizeLinear 节点——这就是 QDQ 格式(Quantize-Dequantize)。QDQ 在量化后立即反量化,模拟出“真实部署时的精度损失”,让模型在转换阶段就提前适应精度变化。

流水线跑完,厨房里多了一本“轻便量具版菜谱”。接下来只要把它交给 ONNX Runtime 的推理引擎(CPUExecutionProvider 或 GPUExecutionProvider),大厨就能用小勺飞快出菜了。

Y^=QuantizeLinear(Yfp32, s, z),Y^≈DequantizeLinear ⁣(QuantizeLinear(Y, s, z), s, z)\hat{Y} = \text{QuantizeLinear}(Y_{\text{fp32}},\,s,\,z),\qquad\hat{Y} \approx \text{DequantizeLinear}\!\left(\text{QuantizeLinear}(Y,\,s,\,z),\,s,\,z\right)
静态量化利用 QuantizeLinear 算子把 FP32 激活值映射到 INT8,再通过 DequantizeLinear 还原近似值。QDQ 成对出现,是为了在模型图中保留浮点计算接口,同时模拟量化误差。
  1. QuantizeLinear 算子ONNX 标准量化算子,接收浮点张量、scale 和 zero_point,输出整数张量。
    QuantizeLinear(Yfp32, s, z)\text{QuantizeLinear}(Y_{\text{fp32}},\,s,\,z)
  2. DequantizeLinear 算子把整数张量还原为浮点近似值,用于模拟推理时的反量化过程。
    DequantizeLinear(⋅, s, z)\text{DequantizeLinear}(\cdot,\,s,\,z)
  3. QDQ 组合的妙处成对出现做“先量化、再反量化”,让下游算子仍以浮点形式对接,同时对量化误差保持感知。
    Y^=DequantizeLinear(QuantizeLinear(Y, s, z), s, z)\hat{Y} = \text{DequantizeLinear}(\text{QuantizeLinear}(Y,\,s,\,z),\,s,\,z)

算子与性能

不是所有食材都适合切丁。牛肉、土豆切丁炒起来又快又入味;嫩豆腐一碰就碎,必须保留完整的形状。ONNX Runtime 的量化器也有这样一张“支持列表”:Conv(卷积)、MatMul(矩阵乘)、Gemm(全连接)、Add、Relu、Clip 等核心算子都支持 INT8 量化;而一些特殊算子(如某些自定义 Op、动态 shape 敏感的算子)量化器会直接跳过,让它们以 FP32 精度运行——这就是混合精度回退(mixed precision fallback)。

混合精度听起来像“偷懒”,实则是聪明的取舍。敏感算子保留高精度,常规算子用 INT8 提速,整体精度几乎不掉,性能却大幅提升。在 CPU 上,INT8 推理通过 AVX-512 等 SIMD 指令集实现并行整数运算,加速比通常达到 2 到 4 倍:

内存占用的优化同样显著:权重从 4 字节降到 1 字节,带宽压力骤减。对内存带宽敏感的算子(如矩阵乘),性能提升甚至接近理论极限。大厨的厨房变小了,但出菜速度反而快了——这就是量化在工程上的双重红利。

加速比=Tfp32Tint8≈2∼4\text{加速比} = \dfrac{T_{\text{fp32}}}{T_{\text{int8}}} \approx 2 \sim 4
加速比是 FP32 推理耗时与 INT8 推理耗时之比。整数运算指令吞吐更高、内存带宽占用更小,所以量化后推理更快。
  1. T_fp32 定义量化前 FP32 模型在相同硬件上的端到端推理耗时,作为性能基线。
    Tfp32T_{\text{fp32}}
  2. T_int8 定义量化后 INT8 模型的端到端推理耗时,通常显著低于基线。
    Tint8T_{\text{int8}}
  3. 比值即加速倍数两者相除,得到推理加速倍数,工程上一般落在 2 到 4 倍区间。
    Tfp32Tint8\dfrac{T_{\text{fp32}}}{T_{\text{int8}}}

部署实践

量化后的模型,像一位身经百战的“端上大厨”——在手机、摄像头、智能音箱这些“小厨房”里同样游刃有余。某手机厂商的智能相册应用,原本 50MB 的 FP32 图像分类模型在低端机上推理耗时 200ms,用户滑动相册时明显卡顿。经 ONNX Runtime 静态量化后,模型缩至 12.5MB,推理耗时降到 80ms,卡顿消失,而 top-5 准确率仅下降 0.3%——食客完全尝不出差别。

云端场景更看重成本。某 AI 公司的 NLP 推理服务,每天处理千万级请求,GPU 资源吃紧。将 Transformer 模型的关键 MatMul 层量化到 INT8 后,单卡吞吐量提升 2.8 倍,同样的服务量所需的 GPU 数量几乎减半。另一个工业质检场景中,流水线上的相机对实时性要求严苛——量化让检测延迟从 150ms 降到 60ms,产线节拍不再被 AI 卡脖子。

部署中最重要的验证环节是 Benchmark:对比量化前后的推理延迟、吞吐量、模型体积和精度指标,并记录到性能报告。常见的精度调优策略包括:将敏感层从量化中排除(混合精度)、改用 per-channel 量化、扩充校准数据集、或干脆训练一个更强壮的基线模型。量化不是“转换一次就完事”,而是一套“量—测—调”的循环工程。好大厨不会只看一张菜谱就封神,而是在实践中不断调整那一勺的手感。

AI 应用场景

手机端图像分类与相册检索

阶段:模型优化与端侧部署

解决的问题:FP32 图像分类模型在手机上体积大、推理慢,用户滑动相册时卡顿明显,且大模型占用宝贵的存储空间。通过 ONNX Runtime 静态量化,把模型压缩到四分之一并提速 2-3 倍,端侧体验丝滑,用户无感。

具体案例:某相册 App 内置 50MB 的 FP32 分类模型,量化后仅 12.5MB,推理从 200ms 降至 80ms。采用 QDQ 格式 + per-channel 量化,top-5 准确率仅下降 0.3%,用户几乎无感知。

云端 NLP 推理服务降本增效

阶段:推理服务部署与资源优化

解决的问题:Transformer 模型参数量大、推理昂贵,云上 GPU 集群成本高企。对 MatMul、Gemm 等矩阵运算算子做 INT8 量化,在不更换硬件的前提下提升吞吐量,直接降低单位请求的计算成本。

具体案例:某 AI 客服系统的意图识别服务,单卡日处理请求从 300 万提升到 840 万。量化前需 3 台 GPU 支撑峰值流量,量化后仅需 2 台,且 P99 延迟保持稳定。

工业质检相机的实时检测

阶段:边缘推理与实时控制

解决的问题:流水线相机对检测延迟极其敏感,FP32 模型 150ms 的推理时间成为产线节拍的瓶颈。量化让模型在边缘设备(如 Jetson、工控机)上跑得更快,且功耗更低,适应恶劣的工业环境。

具体案例:某电子元件质检线部署 YOLO 风格检测模型,INT8 量化后延迟从 150ms 降至 60ms,产线节拍提升 40%,误检率仅微增 0.1%,仍满足质量要求。

Python 演示

python68 行
import osimport numpy as npimport onnxfrom onnx import helper, TensorProtofrom onnxruntime.quantization import (    quantize_static,    QuantType,    CalibrationDataReader,    QuantFormat,)  def build_fp32_model():    """构造一个简单的 Conv+Relu 的 FP32 ONNX 模型"""    conv = helper.make_node(        "Conv", ["X", "W"], ["Y"],        kernel_shape=[3, 3], pads=[1, 1, 1, 1],    )    relu = helper.make_node("Relu", ["Y"], ["Out"])    w = np.random.randn(1, 1, 3, 3).astype(np.float32).flatten().tolist()    graph = helper.make_graph(        [conv, relu],        "small_cnn",        [helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 1, 8, 8])],        [helper.make_tensor_value_info("Out", TensorProto.FLOAT, [1, 1, 8, 8])],        [helper.make_tensor("W", TensorProto.FLOAT, [1, 1, 3, 3], w)],    )    model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])    onnx.save(model, "model_fp32.onnx")    return "model_fp32.onnx"  class RandomDataReader(CalibrationDataReader):    """提供随机输入样本作为校准数据"""     def __init__(self, num_batches=4):        self.data = []        for _ in range(num_batches):            self.data.append(                {"X": np.random.randn(1, 1, 8, 8).astype(np.float32)}            )        self.iter = iter(self.data)     def get_next(self):        return next(self.iter, None)  def main():    fp32_path = build_fp32_model()     quantize_static(        model_input=fp32_path,        model_output="model_int8.onnx",        calibration_data_reader=RandomDataReader(),        quant_format=QuantFormat.QDQ,        per_channel=True,        weight_type=QuantType.QInt8,    )     fp32_size = os.path.getsize(fp32_path)    int8_size = os.path.getsize("model_int8.onnx")    print("FP32 model size:", fp32_size, "bytes")    print("INT8 model size:", int8_size, "bytes")    print("Compression ratio: {:.2f}x".format(fp32_size / int8_size))  if __name__ == "__main__":    main()
目的:演示 ONNX Runtime 静态量化全流程——构造 FP32 模型、定义校准数据读取器、调用 quantize_static 产出 INT8 模型,并验证四倍压缩效果。
  1. helper.make_graph(...)用 ONNX helper 构造计算图,相当于“写好 FP32 菜谱”。
  2. class RandomDataReader(CalibrationDataReader)校准数据读取器,负责把输入样本按批次喂给量化器“试菜”。
  3. quantize_static(...)量化核心 API,触发整个量化流水线,输出 INT8 模型。
  4. QuantFormat.QDQ选用 QDQ 格式,量化后立即反量化,模拟真实部署精度。
  5. per_channel=True按通道独立计算 scale 和 zero_point,获得更细粒度的精度。
  6. weight_type=QuantType.QInt8权重使用有符号 8 位整数,覆盖正负权重范围。

常见误区

误区我听说量化就是把 32 位小数丢到 8 位整数里,那精度肯定损失惨重,模型根本不能用了。

正解大多数模型对量化非常宽容。研究与实践表明,PTQ 通常只造成不到 1% 的精度损失,QAT 甚至可以把损失压到千分之一。就像大厨从电子秤换成“一勺”,盐的用量误差可能不到 0.5 克,食客根本尝不出来。只有对数值特别敏感的小模型或超长尾分布的任务才需要担心。

为何常见:很多初学者把“精度”理解为“数值精度”——认为 3.14159 变成 3 是巨大损失。但模型精度(如准确率、mAP)和数值精度是两回事,神经网络的冗余性让它可以容忍大量低精度近似。

误区模型里只要有一个算子没有量化,整个模型就废了,推理还是那么慢。

正解ONNX Runtime 的量化器会自动跳过不支持的算子,以 FP32 精度运行它们,其余算子照常走 INT8——这就是混合精度回退。像大厨切丁时碰到嫩豆腐,直接用手掰开就好,不会因为一道食材就放弃整锅菜。关键算子的量化已经贡献了绝大部分加速收益。

为何常见:初学者把量化理解成“全有或全无”的转换,忽略了工程中常见的混合精度设计。

误区per-channel 给每个通道单独配刻度,精度肯定更高,所以无脑选 per-channel 就对了。

正解per-channel 在小规模模型上精度优势明显,但会引入额外的计算和内存访问开销,在部分硬件上反而拖慢推理速度。这就像给每种食材都配一把专用勺,厨房空间和取勺时间也是成本。工程上应该在精度和速度之间做取舍:先跑 benchmark,再决定粒度。

为何常见:学习资料大多强调 per-channel 的精度优势,却很少提它的额外开销和硬件适配问题,容易给人“越细越好”的错觉。

误区量化不就是调一个 API 把 FP32 文件转成 INT8 文件吗?跟压缩图片差不多。

正解量化是一个系统工程:需要准备有代表性的校准数据集、选择合适的量化格式(QDQ)、决定粒度(per-tensor 还是 per-channel)、跑大量 benchmark 验证精度和性能。校准数据选得不好,模型精度可能断崖式下跌。大厨换量具得先试菜、调刻度、回访食客,绝不是“把电子秤换成勺子”这么简单。

为何常见:教程常常只展示 quantize_static 一行代码,隐藏了背后校准、调优、验证的大量工程细节,让初学者误以为量化是“一键转换”。

面试题

Q1. 量化的核心目标是什么?

单选题 · easy

  • A.增大模型的参数量以提升效果
  • B.减小体积并加速推理过程← 正确答案
  • C.将模型从 CPU 搬到 GPU 运行
  • D.把训练数据压缩后存储

答案:B。量化的核心是用低精度整数近似高精度浮点,从而减小模型体积、加速推理。选项 A 方向相反,C 是硬件迁移,D 是训练数据压缩,都与量化目标无关。就像换轻便量具是为了出菜更快更省地,不是为了换灶台。

Q2. FP32 模型量化为 INT8 后,模型体积约为原来的多少?

单选题 · easy

  • A.二分之一
  • B.四分之一← 正确答案
  • C.八分之一
  • D.保持不变

答案:B。FP32 占 4 字节,INT8 占 1 字节,体积比是 4:1,因此量化后约为原来的四分之一。这对应“电子秤换成一勺”后厨房省出的空间。

Q3. 关于静态量化和动态量化,下列说法正确的是?

单选题 · mid

  • A.动态量化前用校准数据定好全部刻度
  • B.静态量化在推理时逐层临时计算刻度
  • C.静态量化依赖校准数据且推理更快← 正确答案
  • D.动态量化不需要任何额外计算开销

答案:C。静态量化在部署前用校准数据确定所有 scale 和 zero_point,推理时只查表,速度快;动态量化在推理时临时计算,灵活但慢。A、B 说法正好颠倒,D 错在动态量化有额外计算,相当于“临时掏电子秤”。

Q4. scale(缩放因子)在量化中表示什么?

单选题 · mid

  • A.每个整数刻度代表的浮点步长← 正确答案
  • B.浮点数值的符号位长度
  • C.整数区间的上下边界差值
  • D.模型权重的稀疏度比例

答案:A。scale 是浮点数和整数之间的映射比例,即每个整数档位代表多大的浮点跨度。C 是量化区间宽度而非刻度;A 正确,就像是标尺上每格的长度。

Q5. 关于 PTQ 和 QAT,下列描述正确的是?

单选题 · mid

  • A.PTQ 需要重新训练模型所以更耗时
  • B.QAT 训练中模拟量化误差以提升精度← 正确答案
  • C.PTQ 精度总是高于 QAT
  • D.QAT 不需要校准数据直接转换

答案:B。QAT(量化感知训练)在训练中模拟量化误差,让模型适应低精度,精度通常更高更稳;PTQ 是训练后直接转换,快但精度可控性弱。A 说反了,C 说反了,D 错在 QAT 也需要量化策略设定。

Q6. 关于对称量化和非对称量化,正确的是?

多选题 · mid

  • A.对称量化的零点固定在 0← 正确答案
  • B.非对称量化需要额外的 zero_point← 正确答案
  • C.对称量化适合分布关于 0 对称的张量← 正确答案
  • D.非对称量化完全不适用于神经网络

答案:ABC。对称量化零点固定为 0,适合权重分布关于 0 对称的情况;非对称量化通过 zero_point 补偿偏移,表达能力更强,也适用神经网络。D 明显错误。就像秤有“从零开始”和“从容器自重开始”两种方式,后者需要记住容器的重量(zero_point)。

Q7. ONNX Runtime 中 QDQ 格式的含义是什么?

单选题 · mid

  • A.Quantize 与 Dequantize 节点成对出现← 正确答案
  • B.量化和蒸馏同时进行的混合训练
  • C.把数据集均匀切分成四段训练
  • D.一种只在 GPU 上运行的推理模式

答案:A。QDQ 是 QuantizeLinear 与 DequantizeLinear 成对出现的格式——先量化成 INT8 再立即反量化回浮点,用于模拟量化误差、保留浮点计算接口。B、C、D 都是对 QDQ 名称的牵强附会。

Q8. 校准数据集选择不当,最可能导致什么后果?

单选题 · hard

  • A.模型体积反而变大
  • B.量化后精度显著下降← 正确答案
  • C.推理运算符错误
  • D.模型无法保存成 ONNX 格式

答案:B。校准数据用于统计张量的数值范围,若分布与真实数据偏离,scale 和 zero_point 就不准,量化误差会被放大,导致精度断崖式下跌。就像用素菜定盐的刻度,做出来的红烧肉必然口味失衡。A、C、D 与校准数据无直接关系。

Q9. 关于 per-channel 量化,下列说法正确的有?

多选题 · hard

  • A.每个输出通道独立计算 scale← 正确答案
  • B.精度通常优于 per-tensor← 正确答案
  • C.没有任何额外计算开销
  • D.可能引入额外内存访问开销← 正确答案

答案:ABD。per-channel 量化给每个通道单独算 scale,精度通常更好,但会引入额外的计算和内存访问开销,在部分硬件上未必更快。C 过于绝对。就像每种食材配一把专用勺,精度高但厨房收纳成本也高。

Q10. 下列哪些是 ONNX Runtime 量化支持的常用类型或格式?

多选题 · easy

  • A.QInt8← 正确答案
  • B.QUInt8← 正确答案
  • C.QFloat16
  • D.QDQ← 正确答案

答案:ABD。ONNX Runtime 静态量化常用权重类型包括 QInt8 和 QUInt8,量化格式常用 QDQ(Quantize-Dequantize)。C 中的 QFloat16 是浮点半精度,不属于整数量化范畴——这里的“量化”特指把 32 位浮点映射到 8 位整数。