假设你接手了一栋老房子的电路改造:原本所有电器都按 220V 交流电设计,体积大、发热多、电费高。你想把整栋楼改成 12V 直流电,让每台设备体积更小、跑得更快、能耗更低。但直接换电源会烧掉设备——每台电器都需要一台合适的“变压器”,把电压调到它能承受的范围。太粗会损坏精密仪器,太细又浪费成本。AI 模型推理时面临同样的问题:默认的浮点数(float32)精度极高但体积大、计算慢;量化就是把模型从“220V 浮点”改造成“12V 整数”的过程。而 MLIR 量化 Pass,就是这场改造工程中的“总施工队”。MLIR 像一套标准化的施工图纸系统,Pass 则是一支支专业施工队——有的负责测量每台电器的供电需求(校准),有的负责安装变压器(插入 Q/DQ 节点),有的负责优化电路布局(算子融合)。本文沿这条主线,从量化基础原理讲到 MLIR 机制,从静态/动态量化 Pass 讲到精度优化与工程集成,带你完整看懂编译器如何把大模型“安全降压”。

岗位关联

对 AI 从业者而言,掌握 MLIR 量化 Pass 意味着你能亲手为模型做“电路改造”,把部署成本降到几分之一,这是从“会训练模型”迈向“能优化系统”的关键一步。

目录
  1. 可视化演示
  2. 量化原理基础
  3. MLIR核心机制
  4. 静态量化Pass
  5. 动态量化Pass
  6. 精度优化策略
  7. 工程实践集成
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示MLIR量化Pass:浮点图转定点图的仿射量化映射q=clamp(round(r/scale)+zp),PTQ与QAT两种统计注入工作流,及量化沿算子传播的scale推导。

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

仿射量化映射 浮点实数r映射到整数q 量化阶梯clamp限制到qmin..qmax q=clamp(round(r/scale)+zp) 反量化用scale与zero_point还原近似浮点值。

量化原理基础

改造电路之前,工人们必须先理解“电压”这个最基础的概念。一台电器的铭牌上写着额定电压,变压器把 220V 降到 12V 靠的是一个固定的“倍率”。如果倍率选错了,要么电压太低设备罢工,要么电压太高直接烧毁。量化也是一样,它要把模型里连续变化的浮点数,映射到只有 256 个取值的 8bit 整数空间,这个映射的“倍率”就是 scale,“零位对齐”就是 zero point。

从数学上看,量化是一种线性仿射变换。假设真实值是 $r$,量化后的整数是 $q$,步长 $S$ 表示整数每走一格对应的实数大小,零点 $Z$ 表示实数零落在哪个整数刻度上,那么完整公式为:

量化还分对称与非对称两种风格。对称量化令 $Z=0$,整数范围正负对称,适合权重这种正负值都有的张量;非对称量化保留 $Z$,适合 ReLU 之后全为非负的激活值。精度损失主要来自三处:舍入误差(取整时丢掉的小数)、裁剪误差(超出范围被强行截断)、以及溢出误差(累加时整数越界)。理解这些“电力损耗”来源,才能在设计 Pass 时知道该保护什么。

回到电路改造的比喻:变压器倍率定得准不准,决定了所有电器能否安全运行。scale 与 zero point 就是整栋楼变压器的基础参数,后面的所有施工流程,都建立在这两个数值之上。

q=clip⁡(⌊rS⌋+Z,  0,  2b−1)q = \operatorname{clip}\left(\left\lfloor \frac{r}{S} \right\rfloor + Z,\; 0,\; 2^b - 1\right)
通俗地讲:先把实际电压除以变压器倍率得到“刻度数”,四舍五入取整,再加上零点偏移,最后掐头去尾保证不超出 0~255 的量程。
  1. 尺度换算用真实值除以步长 S,算出它对应几个整数刻度。
    rS\frac{r}{S}
  2. 取整运算floor 把结果下取整到最接近的整数,这一步会引入舍入误差。
    ⌊rS⌋\left\lfloor \frac{r}{S} \right\rfloor
  3. 零点平移加上 Z,让实数零点与整数位置对齐,避免负值映射错位。
    ⌊rS⌋+Z\left\lfloor \frac{r}{S} \right\rfloor + Z
  4. 边界裁剪clip 把超出 $[0, 2^b-1]$ 的值拉回边界,防止溢出。
    clip⁡(⋅,  0,  2b−1)\operatorname{clip}\left(\cdot,\; 0,\; 2^b - 1\right)

MLIR核心机制

一栋楼的改造工程不可能让每个施工队各画各的图纸——电工用一套符号,水管工用另一套,最后根本对不上。MLIR 的价值,正是提供了这样一套“统一的施工图纸标准”。它把不同抽象层级的计算表示都收编成统一的 IR,每种表示称为一个“方言”(dialect):浮点计算用 F32 方言,量化后的计算用 Q 方言,底层指令又属于 LLVM 方言。每支施工队只认自己专业的方言图纸,但所有图纸都遵循同一套规范。

在 MLIR 里,Pass 就是一支支独立施工队。编译器把一个又一个 Pass 串成流水线(pass pipeline),就像总承包商排出施工顺序:先拆旧电路、再装变压器、最后验收测试。Pass 管理机制负责决定每支队伍何时进场、分析哪些信息、输出怎样的新图纸。其中最重要的技术是“模式重写”(pattern rewrite):在 IR 图上搜索某个子图模式,一旦匹配就整体替换成新模式。例如把“组合插座 + 变压器”两个零件合并成一个“带变压器的组合插座”,这就是一条重写规则。

Pass 遍历 IR 的方式也有讲究。代码结构上,MLIR 的 IR 呈模块→函数→块→操作的树状层级,Pass 可以自上而下或自下而上地扫描。比如“常量折叠”会先处理叶子节点再向上传播结果,而“内联”则要自顶向下找到调用点。遍历顺序直接影响重写规则能否高效触发,好比施工队进场顺序错了,刚装好的墙又要凿开重走管线。

这就像总承包商的调度室:PassManager 拿着施工计划表,逐个调用施工队,每队看完图纸改完图,交给下一队。MLIR 的强大在于这套调度机制高度规范化,谁先谁后、依赖什么分析结果,都在流水线里写得明明白白,量化 Pass 只是其中一支被调度的队伍。

静态量化Pass

静态量化好比施工前把所有电器的额定功率都测量一遍,统一去市场上订购合适规格的变压器。它把最耗时的“测量”工作放在部署之前完成,推理时就按固定规格直接运行。这个“测量”环节在量化里叫校准(calibration):准备一批有代表性的输入数据,跑一遍前向推理,记录每层激活值的分布,统计出最小值与最大值。有了极值就能算出 scale:

静态量化 Pass 在拿到 scale 之后做三件事:第一,权重离线量化——把每层权重直接转成 INT8,因为权重在推理中恒定不变;第二,激活量化——按校准统计的 scale 把激活范围压到整数,但为了让图中其他算子接口不变,会插入 Q(Quantize)与 DQ(Dequantize)成对节点,Q 负责把浮点转整数,DQ 负责把整数还原成浮点;第三,算子图变换——把 F32 方言的计算图重写为带 Q/DQ 节点的量化方言图,整张图从“220V 标称”变成“12V 标称接线”。

校准数据的选择就像抽样测量电器功率一样,取样的代表性决定了 transformer 是否合适。如果校准数据与真实推理分布差异过大,scale 就会偏,量化误差被放大。静态量化的核心优势是运行时零校准开销:所有“测量”都在施工前完成,推理时只按定好的规格跑整数运算。

S=rmax⁡−rmin⁡qmax⁡−qmin⁡S = \frac{r_{\max} - r_{\min}}{q_{\max} - q_{\min}}
公式的含义是:把实数的动态范围 $[r_{\min}, r_{\max}]$ 均匀摊到整数的 $2^b$ 个刻度上,每一格代表多大的实数,就是步长 S。
  1. 极值采样校准阶段收集该层所有激活值,找出最小值 $r_{\min}$ 与最大值 $r_{\max}$。
    rmax⁡,  rmin⁡r_{\max},\; r_{\min}
  2. 实数区间两者相减得到实数动态范围,它决定变压器要覆盖多宽的电压区间。
    rmax⁡−rmin⁡r_{\max} - r_{\min}
  3. 整数跨度$q_{\max} - q_{\min}$ 等于 $2^b - 1$,即整数刻度总格数。
    qmax⁡−qmin⁡=2b−1q_{\max} - q_{\min} = 2^b - 1
  4. 求取步长实数范围除以整数格数,得到每个刻度对应的实数步长。
    S=rmax⁡−rmin⁡qmax⁡−qmin⁡S = \frac{r_{\max} - r_{\min}}{q_{\max} - q_{\min}}

动态量化Pass

有些电器的负载波动非常大——比如一台忽开忽关的工业电机,给它装固定变压器,要么在高峰时功率不足,要么在低峰时大马拉小车。动态量化就是“自适应变压器”:不提前测死规格,而是运行时根据实时负载临时算 scale。在模型里,权重仍然离线量化好,只有激活值在推理时“边算边调”。

动态量化最常用的粒度是 per-token:把激活张量按序列位置拆成一行一行,每一行独立算 scale。设某一行(一个 token)为 $x_{i*}$,量化位宽为 $b$,则该行的步长为:

除了 token 粒度,还有 per-channel 粒度:按卷积的输出通道逐通道计算 scale。通道粒度更细,对权重分布不均的模型更友好,但会引入更多元的 scale,增加存储和计算开销。MLIR 中实现动态量化 Pass 时,Q 节点被设计成携带“运行时计算”逻辑的小型子图:每次前向都执行一次极值扫描与除法。这个子图本身有开销,所以 Pass 要控制额外成本,例如尽量让 scale 计算复用已有的归约算子、避免引入不必要的内存拷贝。

动态量化像一位聪明的电工:遇到功率波动大的设备,就装一个能实时调节的变压器,电费省了,设备也稳了。代价是变压器本身要消耗一点能量——运行时算 scale 就是那点额外开销。什么时候值得用?当激活内存带宽成为瓶颈、而校准数据又很难覆盖多变场景时,动态量化是比静态更稳健的选择。

Si=max⁡j∣ xij ∣2b−1−1S_i = \frac{\max_{j} |\,x_{ij}\,|}{2^{b-1} - 1}
每一行用自己绝对值的最大值除以整数上限,得到专属步长。负载大的行(值波动剧烈)步长大,负载小的行步长小,真正做到“一行一策”。
  1. 逐行扫描取出第 $i$ 个 token 对应的一行向量 $x_{i1}, x_{i2}, \dots$。
    xijx_{ij}
  2. 求峰值对该行所有元素取绝对值并找最大值,衡量这一行的最大偏离。
    max⁡j∣ xij ∣\max_{j} |\,x_{ij}\,|
  3. 整数上限$2^{b-1} - 1$ 是 INT 正半轴的最大值,对应 8bit 时为 127。
    2b−1−12^{b-1} - 1
  4. 计算步长峰值除以整数上限,得到该行的专属 scale。
    Si=max⁡j∣ xij ∣2b−1−1S_i = \frac{\max_{j} |\,x_{ij}\,|}{2^{b-1} - 1}

精度优化策略

工地里总有几台精密仪器——比如做化学实验的电子天平,对电压波动极其敏感。硬把它们塞进 12V 低压系统,测出来的数据就全废了。量化也有这样的“敏感层”:某些层的权重分布特别集中,或激活值跨度特别大,一旦量化误差就会被放大,导致整个模型输出漂移。因此需要对每一层做“敏感度体检”。

体检方法很直观:单独把某一层量化,其他层保持浮点,对比量化前后模型在验证集上的损失变化。设原权重为 $W_i$,量化后权重为 $\hat{W}_i$,该层的敏感度定义为:

根据体检结果,编译器制定三套保护措施。第一是混合精度:敏感层保持 F32,普通层用 INT8,整张图形成“高压区”与“低压区”共存的混合电路。第二是算子融合:把 Conv、BatchNorm、ReLU 三个连续算子融合成一个大算子,同时把夹在中间的 Q/DQ 节点消掉——就像把三台独立电器集成一台一体机,省去了中间反复变压的损耗。第三是精度回退:量化完跑一遍验证集,如果整体精度下降超过阈值,就自动把最敏感的那几层回退到浮点,类似备用电源自动切入。

这套策略背后是一套“成本账”:每量化一层省下多少内存与算力,又可能损失多少精度,编译器不断在两者之间权衡。融合算子是把多个环节合并减少插拔损耗,回退则是把高价值设备留在高压专线上。最终交付的模型,既不是全 220V 的奢侈配置,也不是全 12V 的冒险方案,而是一份“该省的省、该保的保”的优化电路图。

ΔLi=∣ L(Wi)−L(W^i) ∣\Delta L_i = \left|\, L(W_i) - L(\hat{W}_i) \,\right|
该层的量化让模型损失增加了多少,绝对值越大越敏感,越需要特殊保护。
  1. 原始精度用原始浮点权重在验证集上计算损失 $L(W_i)$。
    L(Wi)L(W_i)
  2. 量化精度把该层权重替换为量化版本 $\hat{W}_i$,重新计算损失。
    L(W^i)L(\hat{W}_i)
  3. 差值绝对值两者相减取绝对值,得到该层量化的敏感度 $\Delta L_i$。
    ΔLi=∣ L(Wi)−L(W^i) ∣\Delta L_i = |\,L(W_i) - L(\hat{W}_i)\,|

工程实践集成

单支施工队再专业,最终也要融入整栋楼的总包工程。MLIR 量化 Pass 的实战价值,体现在它与主流 AI 框架的深度集成——XLA 和 PyTorch 编译链路中,量化 Pass 都扮演着“核心施工队”的角色。

在 XLA 中,模型先被翻译成 HLO 中间表示,再经 MLIR 的桥接层逐步降级到 LLVM。量化 Pass 穿插在这个降级过程中:HLO 层面标记哪些算子可以量化,MLIR 层面的量化 Pass 负责插入 Q/DQ 并完成图重写。在 PyTorch 一侧则是另一条路径:用 torch.fx 捕获模型计算图,转成 torch-mlir 的方言表示,再进入同一个 MLIR 量化流水线。这样,无论模型来自 PyTorch 还是 TensorFlow,最终都能走进同一支“施工队”。

工程落地离不开性能调优与调试排障。性能调优关注吞吐量、延迟、模型体积三个核心指标:量化后 INT8 模型通常只能跑到原来的几分之一延迟,但前提是算子实现了低精度内核,且内存布局对缓存友好。调试排障则靠“留图纸”:MLIR 支持把每个 Pass 执行前后的 IR dump 到文件,开发者可以用 mlir-opt 的 --pass-pipeline 参数逐段回放,检查 Q/DQ 是否成对、scale 是否溢出、融合是否生效。一旦发现某层量化后输出异常,就在 dump 出的 IR 里定位到具体算子。

整栋楼的改造工程最终验收时,电工、水管工、空调工各司其职又协同有序。MLIR 量化 Pass 的价值正在于此:它不是一个孤立的工具,而是一个标准化流程中可插拔的施工队。前端框架负责“翻译需求”,Pass 负责“按图施工”,LLVM 负责“交付验收”。掌握了这支施工队的工作方式,你就能在任意主流框架上,用同一套方法论完成模型的高效量化部署。

AI 应用场景

大语言模型推理服务降本

阶段:推理部署与性能优化

解决的问题:大语言模型推理时,激活值与 KV cache 占用大量显存和带宽。传统 FP16 推理在长序列场景下易显存爆炸,且访存速度严重拖慢首 token 延迟。动态量化 Pass 按 token 实时计算激活 scale,把激活与缓存压到 INT8,大幅缩减显存占用与访存量。

具体案例:某公司用 vLLM 部署 7B 对话模型,长上下文(8K)并发推理时 GPU 显存吃紧。接入 MLIR 动态量化 Pass 对激活做 per-token 量化后,单卡并发数提升约 1.8 倍,首 token 延迟下降约 30%。

边缘端视觉模型部署

阶段:模型转换与端侧编译

解决的问题:手机和 IoT 设备没有强大的 GPU,FP32 模型既大又慢。静态量化 Pass 通过校准集统计激活范围,插入 Q/DQ 节点并融合算子,将 CNN 模型转为 INT8 定点模型,适配端侧 NPU 的低精度指令。

具体案例:某安防厂商在人脸检测摄像头里部署 YOLOv5s,原 FP32 模型 14MB、单帧 120ms。使用 MLIR 静态量化 Pass 转 INT8 后体积降到 4MB,单帧耗时降到 35ms,精度仅损失 0.7% mAP。

跨框架编译器工具链研发

阶段:编译器中端优化

解决的问题:企业内部同时使用 PyTorch 与 TensorFlow,两套模型的量化逻辑若各自维护,成本极高。MLIR 统一 IR 后,量化 Pass 只需实现一份,供 XLA 与 torch-mlir 两条链路复用。

具体案例:某芯片公司开发自研 NPU 编译器,基于 torch-mlir 接入 MLIR 量化 Pass,统一处理来自 PyTorch 和 JAX 的模型,再把量化后的图下降到自研后端指令集,整套流程从 3 个月缩短到 3 周。

Python 演示

python50 行
"""模拟 MLIR 量化 Pass 的核心步骤:校准、量化、反量化、动态量化与误差评估。"""import math  def static_calibrate(values, bits=8):    """根据校准数据求 scale 与 zero point(对应章节3的校准+scale公式)。"""    q_min, q_max = 0, 2 ** bits - 1    v_min, v_max = min(values), max(values)    scale = (v_max - v_min) / (q_max - q_min)    zero = round(q_min - v_min / scale)    return scale, zero  def quantize(values, scale, zero, bits=8):    """把浮点值量化到 [0, 2^bits-1] 的整数范围。"""    q_max = 2 ** bits - 1    return [max(0, min(q_max, round(v / scale + zero))) for v in values]  def dequantize(quants, scale, zero):    """反量化:从整数还原回浮点,用于评估误差。"""    return [scale * (q - zero) for q in quants]  def dynamic_quantize_row(row, bits=8):    """per-token 动态量化:每行独立算 scale,对应章节4公式。"""    q_max = 2 ** (bits - 1) - 1    peak = max(abs(v) for v in row)    scale = peak / q_max    return [round(v / scale) for v in row], scale  def mse(a, b):    """均方误差,衡量量化前后激活值的偏离程度。"""    return sum((x - y) ** 2 for x, y in zip(a, b)) / len(a)  if __name__ == "__main__":    # 模拟某一层激活值的校准集采样    activations = [0.12, 0.55, 1.43, 2.01, 3.77, 5.20, 4.66, 1.99]    scale, zero = static_calibrate(activations)    q = quantize(activations, scale, zero)    r = dequantize(q, scale, zero)    print("静态量化 scale=%.4f zero=%d 误差=%.6f" % (scale, zero, mse(activations, r)))     # 模拟一批 token,每行一个 token,走动态量化    tokens = [[0.1, 0.9, 1.8], [0.4, 1.5, -0.2], [0.9, 0.2, 3.2]]    for idx, row in enumerate(tokens):        q_row, s_row = dynamic_quantize_row(row)        print("第%d个token动态scale=%.4f 量化结果=%s" % (idx + 1, s_row, q_row))
目的:用一个轻量脚本完整演示静态量化的“校准→量化→反量化”流程与动态量化的 per-token 计算方式,并输出量化误差,帮助理解 MLIR 量化 Pass 背后的核心数学逻辑。
  1. def static_calibrate(values, bits=8):对应校准流程,收集激活值的 min/max,算出 scale 与 zero point。
  2. scale = (v_max - v_min) / (q_max - q_min)实数值域均匀摊到 255 个整数刻度上,得到步长。
  3. quantize(...)` 中的 `round(v / scale + zero)对每个浮点值做尺度变换、零点平移与取整。
  4. zero = round(q_min - v_min / scale)用实数最小值反推零点,保证实数零有对应的整数刻度。
  5. dynamic_quantize_row(...)` 中的 `peak = max(abs(v) for v in row)找该 token 行的绝对值峰值作为动态范围上限。
  6. q_max = 2 ** (bits - 1) - 1对称量化的整数正半轴最大值,8bit 时为 127。
  7. mse(a, b)用均方误差度量量化前后激活分布的偏离,作为精度损失指标。

常见误区

误区初学者认为量化很简单,把 float32 数值直接四舍五入转成 int8 就行,编译器只是做了个类型转换。

正解量化必须建立在 scale 与 zero point 的仿射映射上,先缩放、再平移、后裁剪,缺一步都可能让数值范围错位甚至溢出。直接四舍五入等于把 220V 电器硬插进 12V 插座,不是电压不对就是彻底烧毁。MLIR 量化 Pass 的核心工作之一,就是为每个张量谨慎地计算和分配这两组参数。

为何常见:因为小数转整数最直观的联想就是 round,人们容易忽略“压缩”之前还要解决“范围对齐”的问题。

误区动态量化免校准、够灵活,看起来比静态量化“智能”,所以有人默认动态方案全面胜出。

正解动态量化在推理时实时计算 scale,每来一个 token 都要做归约扫描,这部分运行时开销在低延迟场景下不可忽视。静态量化把成本提前到部署阶段,推理时零额外计算,对 shape 固定的 CNN 模型通常更快。两者是不同场景的取舍:动态吃“灵活红利”,静态吃“速度红利”。

为何常见:只看到动态量化的自适应优点,没有把运行时计算开销算进总账,犯了“免费智能”的直觉错误。

误区既然已经量化成整数,Q/DQ 节点看起来是多此一举,有人试图在量化 Pass 里直接删掉它们,认为图会更简洁更快。

正解Q/DQ 节点承担两个职责:一是让各算子的输入输出接口保持浮点,从而复用现有浮点算子实现而不必为每个算子单独写 INT8 版本;二是在图优化阶段模拟量化误差,供编译器评估精度影响。真正的高性能整数代码,是由 LLVM 后端在消除 Q/DQ 对之后生成的。

为何常见:把“优化后的中间表示”误当成“最终执行代码”,忽略了编译器中端与后端的分工边界。

误区INT8 只有 256 个取值,表达能力远小于 FP32,很多人认定量化后模型精度必然崩盘。

正解只要 scale 校准合理、分布裁剪得当,8bit 量化对多数模型的精度损失可以控制在 1% 以内。配合敏感层分析与混合精度,关键层保留 FP32、普通层转 INT8,甚至能做到近乎无损。整数表示不是“不够用”,而是“你没把范围对好”。

为何常见:人们习惯于拿“信息量”做直觉判断,忽略了量化误差还取决于数据分布与动态范围是否匹配,而编译器恰恰擅长做这种适配。

面试题

Q1. MLIR 中 Pass 的主要职责是什么?

单选题 · easy

  • A.负责遍历改写中间表示图← 正确答案
  • B.直接编译 Python 源码文件
  • C.训练更新神经网络权重
  • D.管理 GPU 显存申请释放

答案:A。Pass 是 MLIR 中独立的图优化单元,职责是遍历 IR 并按规则改写,类比施工队按图纸改造电路。编译 Python 源码属于前端,训练权重属于框架,显存管理属于运行时,都与 Pass 的核心职责无关。

Q2. 对称量化中 zero point 的值是多少?

单选题 · easy

  • A.通常直接固定为零值← 正确答案
  • B.等于激活值中的最大值
  • C.由训练过程自动学习出
  • D.要求与 scale 相乘为常数

答案:A。对称量化假设实数范围关于零对称,因此整数零点也固定在零,不需要额外偏移。这就像把 220V 的火线零线对称转换,不必做平移。B 是最大值,C 是 QAT 里的可学习参数(非对称场景),D 是编造的关系。

Q3. 静态量化与动态量化最核心的区别是什么?

单选题 · easy

  • A.激活值 scale 计算时机的不同← 正确答案
  • B.权重存储的数据类型不同
  • C.模型结构图是否发生改变
  • D.是否能够显著加速训练

答案:A。两者权重都可以离线量化,差别在于激活值:静态量化提前用校准集算好固定 scale,动态量化在推理时实时计算。B 说反了,C 的图结构都会变,D 与训练无关。

Q4. 线性量化中 zero point 的数学意义是什么?

单选题 · mid

  • A.实数零所对应的整数刻度← 正确答案
  • B.每个整数刻度的实数步长
  • C.激活张量中绝对值最大的数
  • D.浮点运算中被舍入掉的误差

答案:A。zero point 是把实数零点对齐到整数刻度的偏移量,保证浮点零能被无损表示。B 描述的是 scale,C 是校准统计的峰值,D 是舍入误差,三者都不是 zero point 的定义。

Q5. 静态量化中“校准”阶段的主要目的是什么?

单选题 · mid

  • A.收集统计值确定 scale 和 zero point← 正确答案
  • B.把模型加载到显存并做预热推理
  • C.对权重做随机扰动寻找最优点
  • D.将模型导出为 ONNX 标准中间格式

答案:A。校准是用代表性数据跑前向,统计每层激活的 min/max,从而算出 scale 与 zero point,相当于施工前测量电器功率。预热推理与参数量化无关,随机扰动是优化算法,导出 ONNX 是格式转换。

Q6. 动态量化中"per-token"粒度指的是什么?

单选题 · mid

  • A.按输入的每个独立位置算 scale← 正确答案
  • B.对整个张量统一计算一个 scale
  • C.按权重矩阵的每一列计算 scale
  • D.对每个卷积核独立计算一个 scale

答案:A。per-token 指激活张量按序列位置切成一行行,每行独立求 scale,适合 LLM 中 token 间分布差异大的场景。B 是 per-tensor,C 和 D 是权重侧的 per-channel 变化形式。

Q7. IR 中量化 Pass 为何保留 Q 与 DQ 成对节点?

单选题 · hard

  • A.模拟量化误差并保持算子浮点接口← 正确答案
  • B.让量化图同时拥有两套权重参数
  • C.避免改算子实现就直接加速运行
  • D.使整张图同时适配 CPU 与 GPU

答案:A。Q/DQ 对先把浮点转整数再转回浮点,让每个算子输入输出仍是浮点,接口统一且量化误差被显式模拟,供编译器评估。B 是误解,C 中保留 Q/DQ 并不会直接加速,真正的加速在 LLVM 后端消除 Q/DQ 后,D 与硬件适配无关。

Q8. per-token 动态量化相比 per-tensor 的主要优势是什么?

单选题 · hard

  • A.能适配输入分布剧烈波动的场景← 正确答案
  • B.几乎不增加任何运行时计算量
  • C.可以直接降低权重的存储空间
  • D.不需要再使用任何校准数据集

答案:A。per-token 为每个 token 行单独算 scale,对分布波动大的序列更鲁棒。B 说反了,per-token 需要额外归约计算;C 的权重存储由离线量化决定,与激活粒度无关;D 描述的是动态量化相对静态的优势,而非 per-token 相对 per-tensor 的区别。

Q9. 静态量化 Pass 的典型流程包含哪些步骤?

多选题 · mid

  • A.校准并确定激活的动态范围← 正确答案
  • B.在算子周围插入量化反量化节点← 正确答案
  • C.用真实标签迭代训练模型权重
  • D.把量化权重直接改写为汇编代码

答案:AB。静态量化的核心是“测量→插入 Q/DQ→图重写”,A 与 B 正是校准与节点插入步骤。C 属于量化感知训练,不在推理图优化 Pass 的流程内;D 是 LLVM 后端的工作,量化 Pass 只作用于中端 IR。

Q10. 关于 scale 公式 S=(r_max-r_min)/(q_max-q_min),正确的理解是?

多选题 · extreme

  • A.S 表示整数刻度对应的实数步长← 正确答案
  • B.与 zero point 共同定义完整映射关系← 正确答案
  • C.直接取校准数据的统计平均值即可
  • D.动态量化场景下 S 可在推理时实时算出← 正确答案

答案:ABD。S 是每格整数代表的实数大小,需要与 Z 配合才能完成完整映射,且动态量化中 S 可以在推理时根据实时数据计算,所以 A、B、D 均正确。C 错误:S 由极值范围相除得到,不是统计平均值,用平均值会让大部分数值超出量化量程。