想象这样一个场景:你要搬家了,屋里堆满了大小不一的物品——衣柜、茶几、台灯、几百本书。新家里只有统一规格的收纳箱,你要把所有东西都塞进去,最后再原样还原。怎么打包?两条路摆在面前:第一条,请个经验丰富的搬家师傅,上门看一圈,量几件代表性家具的尺寸,马上去买对应规格的箱子,当天就能搬完——这就是 PTQ(训练后量化),快,但个别易碎品可能被挤变形。第二条,搬走前先在家里住上一个月,每天拿模拟盒子演练打包,哪个塞不下就当场调整家具摆放方式,直到所有东西都能严丝合缝地装进去才正式搬家——这就是 QAT(量化感知训练),慢,但还原度极高。量化感知训练这场“搬家工程”,对 AI 从业者的价值,就是让你在模型部署时一眼判断该走哪条路:既不想白白损失精度,也不想白白烧掉几周 GPU 算力。

岗位关联

学会 PTQ 与 QAT 的选择与实现,你就能在模型部署时快速定位精度瓶颈,用最合理的成本把模型塞进手机、服务器和推理框架,这是 AI 编译器与部署岗位的核心基本功。

目录
  1. 可视化演示
  2. 量化基础概念
  3. 量化参数确定
  4. PTQ原理流程
  5. 校准方法选择
  6. QAT原理流程
  7. 精度成本权衡
  8. 场景选择实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

PTQ无需重训、速度快但精度略降,QAT在训练中模拟量化、精度更高但成本高。拖动滑块观察两种路径在精度、速度与资源上的权衡对比。

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

量化权衡 PTQ快速部署 QAT高精度 精度↔速度↔成本

量化基础概念

搬家打包的第一直觉是:东西太多,必须用统一规格的收纳盒。模型量化也完全一样——模型里的权重和激活值,原本是用 32 位浮点数(FP32)表示的“大物件”,每个数字占 4 字节;而落地部署时,我们希望把它们塞进 8 位整数(INT8)的“小盒子”里,每个数字只占 1 字节。这个过程把大空间里的浮点数字,映射到一格一格的整数刻度上,就是量化(Quantization)。整个压缩操作,等价于给模型换上更小的“收纳盒”。

“量化”听起来像高深的数学,本质不过是一道映射题:把一个大范围内的浮点数,对齐到一个有限整数集合上。正如搬家师傅给收纳盒设计 256 个格子(INT8 能表示 256 个不同的值),每个格子有一个编号。给定缩放因子(scale)\(s\)——即每个格子代表多大的浮点范围,以及零点偏移(zero point)\(z\)——即浮点最小值对应的格子编号,量化公式可以写成:

第一次量化的误差,就藏在“四舍五入”这一步里——好比把一件 37.2 厘米的摆件硬装进 5 厘米一格、只能按整格算的盒子,必然产生“塞不进或空一截”的余地。量化误差由此而生,而 PTQ 和 QAT 这两条路,本质上都是在想方设法把这个“硬塞”的误差降到最小。

q=round(xs)+z,x^=(q−z)×sq = \text{round}\left(\frac{x}{s}\right) + z, \quad \hat{x} = (q - z) \times s
这条公式是整套量化体系的“总开关”:第一个式子把浮点压缩成整数编号,第二个式子把编号还原成近似的浮点值,两步合起来就是收纳盒的“装”与“拆”。
  1. 浮点除以刻度把要打包的浮点数值 \(x\) 除以每个格子代表的宽度 \(s\),得到它在刻度尺上的粗略位置
    xs\frac{x}{s}
  2. 四舍五入取整把位置四舍五入到最近的整数格,得到格子编号 \(q\)(INT8 的编号范围是 -128 到 127)
    round(xs)\text{round}\left(\frac{x}{s}\right)
  3. 加上零点偏移因为格子编号不一定从 0 开始,需要加上 \(z\) 对齐起点,就像箱子刻度从“第 3 格”开始量起
    q=round(xs)+zq = \text{round}\left(\frac{x}{s}\right) + z
  4. 反量化还原拆包时用编号减去零点 \(z\),再乘以刻度 \(s\),得到近似的浮点值 \(\hat{x}\)
    x^=(q−z)×s\hat{x} = (q - z) \times s

量化参数确定

搬家师傅开工前,要做的第一件事是给每个收纳盒贴上“刻度标签”:这一格代表多宽的范围(缩放因子 \(s\)),起点从哪里开始(零点 \(z\))。\(s\) 的决定方式很直观——把物品尺寸的最大最小值之差,均匀切成 256 段(INT8 的整数范围)。极端一点说,最“笨”的打包师傅也能做到:找最宽的和最窄的物品,中间均分成 256 格,每格宽度就是 \(s\)。

这个思路落到数学上就是 MinMax 定标法:先统计某层权重或激活值的分布,取最大值 \(x_{\max}\) 和最小值 \(x_{\min}\),再对比 INT8 能表示的整数范围 \([q_{\min}, q_{\max}]\),两者一除便得到刻度:

刻度表贴好后,每个盒子怎么用就完全确定了:对称量化让零点固定在 0,适合取值范围天然正负对称的权重;非对称量化允许零点偏移,适合激活值这种“全为正数”的分布。刻度表定得准,拆包还原的物品才不变形——这是后续一切量化工作的基石。

s=xmax⁡−xmin⁡qmax⁡−qmin⁡s = \frac{x_{\max} - x_{\min}}{q_{\max} - q_{\min}}
缩放因子是量化的“核心刻度”,定了它,零点偏移也随之确定。现实中还要根据是否允许格子编号为负数(对称)或必须从 0 开始(非对称)来选择不同的盒子规格。
  1. 找浮点范围统计张量(权重或激活)的最大值 \(x_{\max}\) 与最小值 \(x_{\min}\),相当于量出物品的最长与最短尺寸
    xmax⁡−xmin⁡x_{\max} - x_{\min}
  2. 找整数范围INT8 能表示的整数上下界分别是 \(q_{\max}\) 和 \(q_{\min}\),即盒子总共能容纳的格子跨度
    qmax⁡−qmin⁡q_{\max} - q_{\min}
  3. 两者相除浮点总跨度除以整数总跨度,得到每个整数格子对应的浮点宽度,这就是刻度 \(s\)
    s=xmax⁡−xmin⁡qmax⁡−qmin⁡s = \frac{x_{\max} - x_{\min}}{q_{\max} - q_{\min}}
  4. 零点对齐让浮点最小值恰好落在某个格子编号上,反推得到零点 \(z = q_{\min} - x_{\min} / s\),保证“装”与“拆”严格互逆
    z=qmin⁡−xmin⁡sz = q_{\min} - \frac{x_{\min}}{s}

PTQ原理流程

PTQ 是“师傅上门看一眼就打包”的方案:模型已经训练好(东西已经摆好了),只要拿几百张代表样本当作“参考照片”(校准数据),喂给模型跑一遍前向推理,顺手记录下每一层权重和激活的数值分布,然后按分布直接算出每层的刻度尺和零点,把 FP32 权重一次性换成 INT8。全程不碰训练,不需要反向传播,也不需要更新任何参数。

整个流程可以拆成四步:准备校准数据 → 逐层统计分布 → 计算量化参数 → 转换并校验。因为完全不训练,PTQ 往往几分钟就能跑完。它最大的软肋在于“一次成型,没有后悔药”——如果某层分布里混入了极端离群值,刻度尺会被拉得特别宽,导致大多数格子浪费在空档上,量化误差直线上升。这个误差可以用均方误差来衡量:

PTQ 的哲学是“相信师傅经验”——只要校准数据选得有代表性,分布统计足够准,打包质量通常相当不错。多数常见模型在 INT8 下精度损失不到 1%。但如果你有一批“绝对不能磕碰的易碎品”(对精度极敏感的层),师傅这种做法就容易翻车,这时候就得请出 QAT 了。

ε=1N∑i=1N(x^i−xi)2\varepsilon = \frac{1}{N} \sum_{i=1}^{N} \left(\hat{x}_i - x_i\right)^2
均方误差把每个参数的“变形程度”平方后取平均,是量化前后整体差异的数字化表达。误差越大,说明打包造成的物品形变越严重。
  1. 逐点求差对每个参数 \(x_i\),比较原始浮点值与反量化还原值 \(\hat{x}_i\) 之间的偏差
    x^i−xi\hat{x}_i - x_i
  2. 平方放大对偏差取平方,让大的偏差被更严厉地惩罚,也避免正负偏差互相抵消
    (x^i−xi)2\left(\hat{x}_i - x_i\right)^2
  3. 求和平均把所有参数的平方误差加起来除以总数 \(N\),得到整体平均误差
    1N∑i=1N(x^i−xi)2\frac{1}{N} \sum_{i=1}^{N} \left(\hat{x}_i - x_i\right)^2

校准方法选择

校准数据就是师傅手里的“参考照片”,而怎么根据照片定盒子尺寸,有几种策略。最简单的是 MinMax 法:拿全部样本的真实最大最小值当范围,盒子必须装得下最极端的物品,但代价是——如果屋里有一件 2 米高的落地钟,其他物品都只有 30 厘米,那 256 个格子大多都浪费在钟上,小物件全挤在几格里,变形严重。

于是有了更聪明的策略。百分位法:量尺寸时先排除掉最夸张的 0.1% 极端数据,给“离群大件”留一点牺牲空间,换来大多数物品的还原精度。而 KL 散度法更进一步:不只看最大最小值,而是模拟整个分布的“摆放贴合程度”——找一个量化阈值,让量化后的分布尽可能接近原始分布,使信息损失最小:

实践中还常用滑动平均(EMA)来平滑校准过程中的分布抖动——每次前向都更新一次范围估计,而不是只看最后一批数据。选哪种参考策略,取决于物品的分布形态:分布规整用 MinMax 就够,有零散巨物用百分位,追求极致贴合用 KL 散度。校准数据本身的数量通常几百到上千张足够,关键是有代表性,能和真实部署场景分布一致。

DKL(P∥Q)=∑iP(i)ln⁡P(i)Q(i)D_{KL}(P \parallel Q) = \sum_i P(i) \ln \frac{P(i)}{Q(i)}
KL 散度衡量两个概率分布之间的“距离”:数值越小,说明量化后的分布 \(Q\) 与原分布 \(P\) 越贴合,信息保存得越完整。校准的使命就是挑出一个让 KL 散度最小的量化范围。
  1. 建立分布模型把原始浮点值按区间统计成直方图概率 \(P(i)\),候选量化分布记为 \(Q(i)\)
    P(i),Q(i)P(i), Q(i)
  2. 求对数比值计算 \(\ln(P(i)/Q(i))\),当两个分布在该区间完全一致时为 0,偏离越大数值越大
    ln⁡P(i)Q(i)\ln \frac{P(i)}{Q(i)}
  3. 加权求和以原始概率 \(P(i)\) 为权重,对所有区间求和,得到两分布的整体差异
    DKL(P∥Q)=∑iP(i)ln⁡P(i)Q(i)D_{KL}(P \parallel Q) = \sum_i P(i) \ln \frac{P(i)}{Q(i)}

QAT原理流程

QAT 是“边住边打包”的慢工细活:住户还住在房子里,师傅每天拿着模拟盒子试装每个物品,发现哪个装不进去、哪个空隙太大,立刻现场微调家具的摆放形态(权重),让物品本身去适应盒子,直到所有东西都能严丝合缝装进去,才正式搬家。

QAT 的关键在于“模拟量化”(Fake Quant)节点。训练前向传播时,权重先经过一次“假装量化”:量成 INT8 的格子编号再立刻反量化回浮点,得到带量化误差的张量,后续计算照常用浮点做。这样模型在训练时就能“感知”到未来量化会受到怎样的损伤,并主动调整参数来补偿。但问题来了:量化函数是阶梯状的,几乎处处不可导,梯度没法正常回传。解决之道是直通估计器(STE)——让梯度像“记账员”一样假装量化不存在,直接穿透过去:

QAT 的代价清晰可见:需要完整的训练流程——数据、优化器、算力、时间,通常还要跑几个 epoch 的微调。但收益也很直接:模型在量化状态下被重新优化过一次,实际部署时精度几乎无损。做一次 QAT 微调,相当于让整个房子里所有物品都“为收纳盒量身改造”了一遍。

∂x^∂x≈1\frac{\partial \hat{x}}{\partial x} \approx 1
直通估计器强行规定“量化过程对梯度的贡献约等于 1”,即忽略阶梯形状,让梯度原样穿过量化节点。这是 QAT 得以训练的核心技巧。
  1. 前向模拟前向计算时把权重 \(x\) 送入伪量化节点,输出带误差的 \(\hat{x}\),让损失函数“看见”未来的量化损伤
    x^=fake_quant(x)\hat{x} = \text{fake\_quant}(x)
  2. 反向直通反向传播到量化节点时,不按真实导数 0 处理,而是近似认为量化对输入的影响是 1:1 的
    ∂x^∂x≈1\frac{\partial \hat{x}}{\partial x} \approx 1
  3. 微调恢复梯度正常流向权重参数,优化器小幅更新权重,使权重逐步“长成”量化友好的形态
    θ←θ−η⋅∂L∂x^\theta \leftarrow \theta - \eta \cdot \frac{\partial L}{\partial \hat{x}}

精度成本权衡

现在两张报价单摆在面前。PTQ 师傅说:免费上门一次,当天打包完成,搬运过程可能有少量磕碰,但九成以上物品毫发无损。QAT 师傅说:需要先演习一个月(训练资源),但保证每件物品都严丝合缝,几乎原样还原。你选哪个?项目缺时间缺算力,PTQ 是性价比之王;项目对精度极其敏感,且资源宽裕,QAT 才是正解。

从数据和经验看:多数视觉、语言模型在 INT8 下走 PTQ,精度损失通常在 1% 以内;但遇到激活值动态范围很大、离群点多的模型(如超大模型、某些 Transformer),PTQ 可能损失 3% 甚至更多,这时 QAT 的微调价值就凸显出来了。成本端,PTQ 只需几分钟校准,QAT 需要数小时到数天 GPU 训练。而收益端的存储节省对所有方案一视同仁——这也是量化最实在的甜头:

实践中的常见姿态并非“二选一”:很多团队先用 PTQ 快速上线拿到收益,再挑精度不达标的模型做 QAT 微调或部分层敏感量化——相当于先让师傅粗搬一趟,再对唯一的易碎品定制保护箱。精度与成本的平衡,永远服务于具体的交付目标。

Ssave=(1−bINT8bFP32)×100%=75%S_{\text{save}} = \left(1 - \frac{b_{\text{INT8}}}{b_{\text{FP32}}}\right) \times 100\% = 75\%
把每个权重从 32 位缩到 8 位,模型体积直接变为原来的四分之一,节省 75% 的存储空间。内存占用少了,缓存命中率高了,推理也能获得显著加速。
  1. 写定位宽FP32 每个数占 32 位,INT8 每个数占 8 位
    bFP32=32,bINT8=8b_{\text{FP32}} = 32, \quad b_{\text{INT8}} = 8
  2. 求比例INT8 位宽除以 FP32 位宽,得到后者的四分之一
    832=0.25\frac{8}{32} = 0.25
  3. 算节省1 减去这个比例再乘 100%,得到 75% 的存储节省比例
    (1−0.25)×100%=75%\left(1 - 0.25\right) \times 100\% = 75\%

场景选择实践

搬家方案最终要看“新家”长什么样。目的地是手机(边缘部署):存储和功耗都极其有限,NPU 只认 INT8,这时候 QAT 更保险——因为边缘设备没有重试机会,精度掉 1% 用户立刻能感知。目的地是云端 GPU 集群:模型要频繁迭代、流量大,PTQ 几分钟出结果、当天上线,是最高效的路径。

跨框架互操作是另一个关键场景:PyTorch 训好的模型要交给 ONNX、TensorRT 或各种推理引擎执行。此时量化参数(scale、零点、位宽、量化粒度)必须跟着权重一起保存——这就是模型序列化环节:打包师傅不仅要把东西装进箱子,还要把“刻度标签”(量化参数)写进物流单(模型文件),否则新家主人(推理引擎)收到箱子也不知道怎么拆。量化的成功与否,可以用一个简单的判据来决策:

在 AI 编译器与推理系统的真实流水线上,量化从来不是孤立的“压缩魔法”,而是与模型格式、序列化协议、算子库支持深度绑定的系统工程。把刻度标签写进模型文件、让不同框架读懂同一套量化约定,是跨框架互操作和高效推理的共同地基,也是“模型格式与序列化”这一知识环节存在的意义。

选 PTQ  ⟺  εPTQ−εQAT≤δ容忍\text{选 PTQ} \iff \varepsilon_{\text{PTQ}} - \varepsilon_{\text{QAT}} \le \delta_{\text{容忍}}
决策公式的含义:如果 PTQ 的量化误差比 QAT 的误差多出的部分,在可容忍范围 \(\delta\) 之内,就直接选 PTQ;否则换 QAT。\(\delta\) 由业务对精度的敏感度决定。
  1. 算两种误差分别得到 PTQ 与 QAT 的量化误差 \(\varepsilon_{\text{PTQ}}\) 和 \(\varepsilon_{\text{QAT}}\)
    εPTQ,εQAT\varepsilon_{\text{PTQ}}, \quad \varepsilon_{\text{QAT}}
  2. 求误差差距两者相减,得到“省下训练成本所付出的精度代价”
    εPTQ−εQAT\varepsilon_{\text{PTQ}} - \varepsilon_{\text{QAT}}
  3. 对比容忍度若差距不超过业务可接受的 \(\delta_{\text{容忍}}\),则 PTQ 足够优秀,不必动用训练资源
    εPTQ−εQAT≤δ容忍\varepsilon_{\text{PTQ}} - \varepsilon_{\text{QAT}} \le \delta_{\text{容忍}}

AI 应用场景

端侧检测模型的 QAT 部署

阶段:模型压缩与端侧部署

解决的问题:手机 NPU 只支持 INT8 算子,FP32 模型体积 120MB、推理延迟 80ms,直接硬量化会导致人脸检测精度骤降。传统 PTQ 方案精度损失 3%,用户打卡识别频繁失败。

具体案例:端侧工程师对检测模型做 QAT 微调,训练时插入伪量化节点,用 3 天 GPU 微调恢复精度。最终模型体积压缩到 30MB,推理延迟降至 12ms,mAP 仅下降 0.3%,成功通过验收。相当于先让师傅在家里演练了一个月,每件“易碎品”都量身定制了保护方案。

云端排序模型的 PTQ 快速发布

阶段:模型发布与版本迭代

解决的问题:推荐系统每周要更新排序模型,如果用 QAT 每轮都要重新微调,GPU 成本和发布周期都无法承受。传统做法直接部署 FP32,推理延迟高、带宽压力大。

具体案例:算法团队用 PTQ 方案,训练完成后用 5 分钟校准数据量化,当天完成 INT8 转换并上线。AUC 仅下降 0.1%,服务器推理吞吐提升 3.5 倍,实现了“上午训练、下午上线”的快速迭代节奏,相当于师傅上门看一眼直接打包,完全不影响日常生活。

跨框架模型的量化序列化

阶段:模型格式转换与序列化

解决的问题:PyTorch 训练的 BERT 模型要部署到 TensorRT,中间经过 ONNX。如果量化参数(scale、zero_point、位宽)没有随模型文件一起序列化,目标框架无法正确反量化,推理结果直接错乱。

具体案例:AI 编译器工程师将量化配置写入 ONNX 的自定义元数据字段,TensorRT 加载时读取刻度表并按相同规则执行 INT8 推理,两边结果完全对齐。这相当于搬家师傅把“刻度标签”贴在物流单上,跨城市搬运后新家主人能原样拆包。

Python 演示

python105 行
import torchimport torch.nn as nnimport torch.nn.functional as F torch.manual_seed(42)  class TwoLayer(nn.Module):    def __init__(self):        super().__init__()        self.w1 = nn.Parameter(torch.randn(8, 16) * 0.2)        self.b1 = nn.Parameter(torch.zeros(16))        self.w2 = nn.Parameter(torch.randn(16, 2) * 0.2)        self.b2 = nn.Parameter(torch.zeros(2))     def forward(self, x):        h = torch.relu(x @ self.w1 + self.b1)        return h @ self.w2 + self.b2  def make_data(n=1024):    x = torch.randn(n, 8)    y = ((x[:, 0] * x[:, 1]) > 0).long()    return x, y  def scale_zp(t):    s = (t.max() - t.min()) / 254.0    z = round(-128 - t.min() / s)    return s, z  def fake_q(t, s, z):    q = torch.clamp(torch.round(t / s + z), -128, 127)    return (q - z) * s  class FakeQuant(torch.autograd.Function):    @staticmethod    def forward(ctx, x, s, z):        return fake_q(x, s, z)     @staticmethod    def backward(ctx, grad_output):        return grad_output, None, None  def train(model, x, y, steps=300, lr=0.02):    opt = torch.optim.SGD(model.parameters(), lr=lr)    for _ in range(steps):        opt.zero_grad()        out = model(x)        loss = F.cross_entropy(out, y)        loss.backward()        opt.step()  def qat_train(model, x, y, steps=150, lr=0.002):    opt = torch.optim.SGD(model.parameters(), lr=lr)    for _ in range(steps):        opt.zero_grad()        s1, z1 = scale_zp(model.w1.detach())        s2, z2 = scale_zp(model.w2.detach())        w1q = FakeQuant.apply(model.w1, s1, z1)        w2q = FakeQuant.apply(model.w2, s2, z2)        h = torch.relu(x @ w1q + model.b1)        out = h @ w2q + model.b2        loss = F.cross_entropy(out, y)        loss.backward()        opt.step()  def evaluate(model, x, y, quantized=False):    with torch.no_grad():        if quantized:            s1, z1 = scale_zp(model.w1)            s2, z2 = scale_zp(model.w2)            w1 = fake_q(model.w1, s1, z1)            w2 = fake_q(model.w2, s2, z2)        else:            w1, w2 = model.w1, model.w2        h = torch.relu(x @ w1 + model.b1)        out = h @ w2 + model.b2        pred = out.argmax(dim=1)        return (pred == y).float().mean().item()  def main():    x, y = make_data()    model = TwoLayer()    train(model, x, y)    print("float acc:", evaluate(model, x, y))     ptq = TwoLayer()    ptq.load_state_dict(model.state_dict())    print("ptq acc:", evaluate(ptq, x, y, quantized=True))     qat = TwoLayer()    qat.load_state_dict(model.state_dict())    qat_train(qat, x, y)    print("qat acc:", evaluate(qat, x, y, quantized=True))  if __name__ == "__main__":    main()
目的:用 80 行 PyTorch 脚本直观复现 PTQ 与 QAT 的完整路径,展示“先训练浮点模型 → PTQ 直接量化”与“STE 伪量化微调”两种方式的精度差异,是理解真实量化工具链的迷你模型。
  1. s = (t.max() - t.min()) / 254.0用 MinMax 法计算缩放因子,255 个刻度区间对应 INT8 的 -128 到 127
  2. q = torch.clamp(torch.round(t / s + z), -128, 127)量化核心:映射、取整、截断到合法格子范围
  3. return (q - z) * s反量化还原近似浮点值,量化误差在这一步暴露
  4. return grad_output, None, NoneSTE 直通:梯度原样穿过量化节点,忽略阶梯状导数为 0 的问题
  5. w1q = FakeQuant.apply(model.w1, s1, z1)QAT 训练中插入伪量化节点,让前向传播“看见”未来的量化损伤
  6. ptq.load_state_dict(model.state_dict())PTQ 直接复用浮点训练好的权重,不经过任何训练
  7. qat_train(qat, x, y)QAT 在浮点权重基础上额外微调,让权重逐步适应低比特表示

常见误区

误区量化不就是把 3.14159 变成 3 吗?砍掉小数部分就完事了,所以精度损失是必然且不可控的。

正解量化不是“砍尾巴”,而是“换刻度”。它通过 scale 和 zero_point 把浮点分布整体映射到整数刻度上,反量化后能最大程度还原原值。好的量化(配合校准和 QAT)能把误差控制在极小范围,就像裁剪一张照片到合适尺寸,而不是粗暴地撕掉一角。

为何常见:很多人把“量化”和“取整”混为一谈,忽略了量化中还有一个完整的映射—反映射体系,刻度表(scale/zp)才是决定还原度的关键。

误区要么走 PTQ 快但损失精度,要么走 QAT 慢但保精度,两条路互不相容,选定就不能回头。

正解真实项目常采用混合策略——先 PTQ 快速拿到基线,再对精度敏感的层或整个网络做 QAT 微调。相当于先让师傅粗搬一遍,再对易碎品单独定制保护箱,两种方案可以接力使用。

为何常见:教材总把 PTQ 和 QAT 放在对立面做对比,让初学者直觉认为它们是互斥选项,而忽略了工程上常见的迭代式量化流程。

误区校准阶段使劲塞数据,喂几万张图统计分布,范围一定更准,量化效果肯定更好。

正解校准数据讲究“代表性”而非“数量”。塞入大量分布失真的数据(比如拍摄角度单一的图像),反而会拉偏统计范围,让刻度表失真。通常几百到一千张有代表性的样本就足够,就像量家具尺寸只需量几件典型家具,不必量遍全屋杂物。

为何常见:人的直觉是“样本越多越精确”,但统计分布估计有自己的规律——数据质量比数量更重要,混入噪声样本会直接污染范围的估计。

误区从 32 位压到 8 位,信息少了四分之三,准确率肯定要跌 25% 甚至更多。

正解位宽减少不直接等价于准确率按比例损失。现代校准方法与 QAT 微调能让大多数模型在 INT8 下精度损失控制在 1% 以内,部分模型甚至无损。因为神经网络本身有冗余,量化只是去掉冗余,而非伤筋动骨——就像把被子抽真空打包,体积小四倍但保暖性几乎不变。

为何常见:人们容易把“存储位宽”和“有效信息量”划等号,忽视了模型参数的分布规律和训练本身的鲁棒性。

误区QAT 微调完模型,直接拿训练时的伪量化权重部署到推理引擎就行,反正前向已经模拟过量化了。

正解QAT 只在训练中模拟量化,部署时仍需做真实量化转换:按训练收敛的 scale/zp 把权重真正存成 INT8,并完成 BatchNorm 融合、算子替换等步骤。演练和真正搬家是两回事——模拟再好,物流单和箱子还得真备齐。

为何常见:伪量化节点的存在让前向传播的输出“看起来已经是量化结果”,容易让人误以为训练产物可以直接端到端使用,忽略了训练与部署两条链路的差异。

面试题

Q1. PTQ 最突出的优势是什么?

单选题 · easy

  • A.精度永远无损还原
  • B.无需重新训练模型← 正确答案
  • C.不需要任何样本数据
  • D.训练时间比原来更长

答案:B。PTQ 的核心优势是“不碰训练”——直接对训练好的模型做校准和量化,几分钟完成。A 错在“永远无损”,C 错在 PTQ 仍需要校准数据,D 与事实相反,PTQ 几乎不消耗训练时间。

Q2. 量化公式 \(q = \text{round}(x/s) + z\) 中,缩放因子 \(s\) 的作用是什么?

单选题 · mid

  • A.表示零点偏移偏置量
  • B.把浮点值映射到整数刻度← 正确答案
  • C.决定量化后的位宽大小
  • D.控制模型训练的学习率

答案:B。\(s\) 是每个整数格子代表的浮点范围宽度,是把浮点数映射到整数刻度的关键参数。零点偏移由 \(z\) 负责,位宽由量化类型决定,学习率则与量化完全无关,故选 B。

Q3. 对称量化与非对称量化的主要区别是什么?

单选题 · mid

  • A.是否有零点偏移参数 z← 正确答案
  • B.是否需要对权重做融合
  • C.是否使用浮点数的精度
  • D.是否依赖梯度下降算法

答案:A。对称量化强制零点 \(z=0\),而非对称量化允许 \(z \neq 0\) 来对齐非对称分布。权重融合、浮点精度、梯度下降都与两者的区分无关,故选 A。

Q4. QAT 中伪量化(Fake Quant)节点的作用是什么?

单选题 · easy

  • A.训练时模拟量化误差效果← 正确答案
  • B.把权重直接保存为整数
  • C.用浮点替代整数运算
  • D.跳过所有的反向传播

答案:A。伪量化节点在前向传播中执行“量化再反量化”,让模型在训练时就感知未来的量化误差,从而调整权重适应低比特表示。它不会真正把权重存成整数,也不跳过反向传播,故选 A。

Q5. 直通估计器(STE)解决的核心问题是什么?

单选题 · mid

  • A.让量化函数在反向可导← 正确答案
  • B.消除全部量化误差影响
  • C.加速浮点矩阵的乘法
  • D.代替所有校准数据作用

答案:A。量化函数是阶梯状、几乎处处不可导的,反向传播无法计算梯度。STE 规定梯度近似等于 1 直接穿过量化节点,使训练得以继续。它不能消除误差,也不涉及矩阵乘法加速或校准数据替代,故选 A。

Q6. 使用 KL 散度选择量化阈值的主要目的是什么?

单选题 · mid

  • A.使量化前后分布更接近← 正确答案
  • B.让模型训练速度加快
  • C.减少模型文件的大小
  • D.将浮点转成二进制文件

答案:A。KL 散度衡量量化后分布与原始分布的贴合程度,校准的目标就是遍历候选阈值、挑出 KL 散度最小的那个,让信息损失最少。训练速度、文件大小、二进制转换都不是 KL 校准的目的,故选 A。

Q7. PTQ 精度明显下降的常见原因是什么?

单选题 · hard

  • A.激活值分布过宽且有离群值← 正确答案
  • B.模型网络层数不够深导致
  • C.校准数据样本使用过多
  • D.学习率偏小导致模型欠拟合

答案:A。PTQ 不做训练,自然与学习率无关;校准数据过多也不是问题,关键在分布质量。激活值分布宽且存在极端离群值时,MinMax 类方法会把刻度尺拉得很宽,有效格子被浪费,量化误差骤增——这是 PTQ 翻车的最常见原因,故选 A。

Q8. 以下哪些场景更适合选择 QAT?

多选题 · mid

  • A.模型精度对量化极敏感← 正确答案
  • B.训练算力和时间都很充裕← 正确答案
  • C.模型要在当天快速上线
  • D.推理设备已经支持 INT8

答案:AB。QAT 精度高但成本高,适合精度敏感且资源充裕的场景。C 描述的是 PTQ 的典型优势;D 是两种方案共有的前提条件,不能作为选 QAT 的理由。故选 A、B。

Q9. 模型序列化时需要保存哪些量化信息?

多选题 · mid

  • A.缩放因子 scale 数值表← 正确答案
  • B.零点偏移 zero_point← 正确答案
  • C.量化粒度和位宽设置← 正确答案
  • D.训练时的全部数据增强

答案:ABC。推理引擎要正确拆包,必须拿到完整的“刻度标签”:每层(或每通道)的 scale、zero_point,以及量化粒度(逐层或逐通道)和位宽(INT8/INT4)。训练数据增强属于训练阶段配置,部署后无需保存,故选 A、B、C。

Q10. 关于 PTQ 与 QAT 的说法,正确的有哪些?

多选题 · hard

  • A.PTQ 校准数据可少量快速← 正确答案
  • B.QAT 反向传播用直通估计← 正确答案
  • C.两者都支持跨框架部署← 正确答案
  • D.QAT 训练耗时一定小于 PTQ

答案:ABC。PTQ 用少量校准数据即可分钟级完成;QAT 依赖 STE 让量化在反向传播中可导;两者转换后的 INT8 模型都能导出为 ONNX 等格式供跨框架部署。D 明显错误——QAT 需要完整训练流程,耗时远大于 PTQ,故选 A、B、C。