想象你开了一家小面馆,生意很好,但你发现每天收银机只记整数账目:一碗面 17.8 元,记账本上只写 18;成本 6.3 元,只写 6。月底算利润时,误差越滚越大,账面一团糟。更气人的是,如果刚开始记账就直接“抹零”,你根本看不出哪碗面挣钱、哪碗面亏钱。你肯定想说:为什么不先用精确记账把生意理清楚,等确定怎么定价之后,再启用“抹零”规则呢? 这就是伪量化要做的事。神经网络的权重就像面馆的账本,全精度(float32)是精确记账,8 位整数(int8)是“抹零记账”。伪量化 FQ(Fake Quantization)就是在训练阶段先“假装抹零”——前向传播时模拟量化带来的误差,让模型感受被压缩的“痛”;但反向传播时依然用全精度梯度更新,就像你心里记着真实成本、只是账面抹零。这样训练出的模型天生适应量化,真正部署时再“咔嚓”一刀切成低精度,误差已经小到可以忽略。 这篇文章就用“面馆记账”的故事,带你拆解伪量化的原理、对称与非对称量化、量化参数怎么来、STE 梯度怎么走,最后看看它在真实 AI 部署中如何大显身手。

岗位关联

量化感知训练是 AI 模型部署最关键的技术之一。掌握伪量化机制,你就能理解各大推理引擎(TensorRT、ONNX Runtime、TFLite)背后的精度恢复密码,也能在面试中从容拆解“为什么 QAT 比 PTQ 更稳”。

目录
  1. 可视化演示
  2. 为什么需要伪量化
  3. 伪量化基本原理
  4. 量化公式分解
  5. 对称与非对称量化
  6. 量化参数的确定
  7. 训练收敛策略
  8. QAT与PTQ对比
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示伪量化前向的量化-反量化循环与scale缩放因子计算、反向传播中round梯度阻断与直通估计器STE的梯度绕过、以及伪量化模型FP32到INT8的部署转换流程。

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

前向量化-反量化循环 浮点x量化到低精度整数 再反量化回浮点引入误差 scale=(max-min)/(2^b-1) 训练中模拟量化信息损失

为什么需要伪量化

面馆的故事继续。假设你已经用精确记账经营了三个月,终于摸清了每道菜的成本和毛利。现在你想省事,改用“只记整数”的账本。如果直接切换,前两周账目会有很大波动——你可能会误判某道菜是亏本的,然后错误地把它下架。神经网络也一样:把一个训练好的 float32 模型直接砍成 int8,精度立刻跌掉一大截,就像精确账本突然抹零,账面和心理都受不了。

伪量化的思路是:别等部署时再“突变”,训练时就让人工神经网络体验抹零的感觉。具体做法是,在训练的前向传播中,把权重和激活值通过一个“模拟量化装置”,让它先经历取整、缩放的损耗;但在反向传播更新梯度时,依然使用全精度数值。这样一来,模型在参数更新时就已经把量化误差当做“环境噪声”去适应了,真正部署时的精度损失可以控制在 1-2% 以内。

这就是“训练感知量化”(Quantization-Aware Training, QAT)的雏形。没有伪量化,QAT 就是无源之水;伪量化是 QAT 的“感受器官”,负责把量化的痛感传递给训练过程。理解它,你才能看懂为什么有些模型量化后几乎无损,有些却崩得稀里哗啦。

所以,训练感知量化的本质就是让模型在训练时提前适应低精度的“饮食环境”,而不是等部署时突然改变饮食习惯。

LossQAT=1N∑i=1Nℓ(f(x~i;w~),yi)\text{Loss}_{\text{QAT}} = \frac{1}{N} \sum_{i=1}^{N} \ell\left(f(\tilde{x}_i; \tilde{w}), y_i\right)
直观说:QAT 的损失函数里,模型看到的是“被抹零”后的权重 $\tilde{w}$ 和激活 $\tilde{x}$,而不是原始的全精度值。模型一边用粗糙的输入做预测,一边用精确的梯度学参数,这就是“感知量化”的含义。
  1. 损失函数本质训练的目标是最小化模型在“量化模拟”条件下的预测误差,让模型学会在粗糙环境下做对事。
    LossQAT=1N∑i=1Nℓ(f(x~i;w~),yi)\text{Loss}_{\text{QAT}} = \frac{1}{N} \sum_{i=1}^{N} \ell(f(\tilde{x}_i; \tilde{w}), y_i)
  2. 量化权重$\tilde{w}$ 表示经过模拟量化后的权重,它在训练中被“抹零”后参与前向计算。
    w~=q(w)\tilde{w} = q(w)
  3. 量化激活$\tilde{x}_i$ 表示第 $i$ 个样本的激活值被量化后的形式,模拟推理时的低精度环境。
    x~i=q(xi)\tilde{x}_i = q(x_i)
  4. 均值求和$\frac{1}{N} \sum$ 表示对 $N$ 个样本的损失取平均,反映整体训练效果。
    1N∑i=1N\frac{1}{N} \sum_{i=1}^{N}

伪量化基本原理

伪量化的核心机制可以拆成两条路线:前向传播走“窄门”,反向传播走“宽桥”。这就像一个聪明的记账员,在前台给客人看整数账单,但在后台算成本时依然用精确到分的真实数据——前台抹零是为了让客人(部署环境)满意,后台精确是为了让自家生意(梯度更新)不亏。

前向传播时,伪量化做三件事:把全精度权重 $w$ 通过量化公式映射到整数范围,再用反量化公式还原到近似浮点值。注意,还原后的值并不是原来的值,而是带了量化误差的“近似值”。这个误差就是模型要感知的“痛”。

反向传播时,如果直接对取整操作求导,梯度几乎处处为零——取整函数(round)的导数几乎处处是 0,就像“抹零”这个动作本身无法微分。怎么办?STE(Straight-Through Estimator,直通估计器)给出一个巧妙的回答:把取整操作的梯度直接“透传”过去,假装它不存在。梯度恒等于 1,于是参数依然能正常更新。

STE 的梯度直通:反向传播时,STE 将取整函数的梯度近似为:

w~=clamp(round(ws)+z,0,255)\tilde{w} = \text{clamp}\left(\text{round}\left(\frac{w}{s}\right) + z, 0, 255\right)
直观理解:虽然“抹零”让账面不精确,但在算梯度这笔账时,我们假装抹零没发生过,让梯度满分通过。
  1. 缩放除 scale把全精度权重除以缩放因子 $s$,映射到整数刻度上,相当于把真实金额换算成“记账单位”。
    ws\frac{w}{s}
  2. 取整 round把缩放后的值四舍五入为整数,这就是“抹零”动作,产生量化误差。
    round(ws)\text{round}\left(\frac{w}{s}\right)
  3. 加零点 z加上零点偏移,让量化区间和非对称分布对齐,相当于调整账本的起始行。
    round(ws)+z\text{round}\left(\frac{w}{s}\right) + z
  4. 剪裁 clamp把超出 $[0, 255]$ 区间的值截断,防止溢出,相当于超出账本页面的金额就写到边界页。
    ∂w~∂w≈1\frac{\partial \tilde{w}}{\partial w} \approx 1

量化公式分解

伪量化的完整闭环包括“正向量化”和“反向反量化”。正向量化把浮点数映射到整数,反量化把整数还原成浮点数。这两个操作合起来,才构成前向传播中的“模拟低精度”。

这里 $r$ 是真实浮点值(面馆里的真实成本),$q$ 是量化后的整数值(账本上的抹零记录),$s$ 是缩放因子(记账单位,比如“每 1 格代表 0.5 元”),$z$ 是零点(账本的起始页偏移)。

其中 $\alpha$ 和 $\beta$ 是量化区间的上下界。这个公式告诉我们:量化区间越宽,$s$ 越大,每个整数代表的浮点范围越大,误差也就越大。就像一个账本如果只有 255 行,却要记 0 到 10000 元的账,每行就得代表约 39 元,精度自然粗糙。

总结:伪量化就是在训练中不断重复“抹零 → 还原 → 对比误差”的循环,让模型对精度损失产生“免疫力”。

r=s×(q−z)r = s \times (q - z)
q=round(rs)+zq = \text{round}\left(\frac{r}{s}\right) + z
s=α−β2b−1s = \frac{\alpha - \beta}{2^b - 1}
  1. 反量化还原$r = s \times (q - z)$ 把整数账本记录还原成浮点金额,但已经损失了抹零部分的信息。
    r=s×(q−z)r = s \times (q - z)
  2. 正向量化$q = \text{round}(r / s) + z$ 是正向的抹零过程,把浮点映射到整数。
    q=round(rs)+zq = \text{round}\left(\frac{r}{s}\right) + z
  3. 缩放因子$s = (\alpha - \beta) / (2^b - 1)$ 决定每个整数刻度代表多大的浮点范围。
    s=α−β2b−1s = \frac{\alpha - \beta}{2^b - 1}
  4. 位宽约束$b$ 是量化位宽(如 8 位时 $2^b - 1 = 255$),位宽越高刻度越密、误差越小。
    2b−12^b - 1

对称与非对称量化

面馆记账有两种策略:一种是从零开始记(非对称),另一种是以 0 为对称中心往两边记(对称)。假设你的账单金额在 -100 到 100 之间波动,对称量化把 0 作为中点,正负分别映射;非对称量化则允许零点偏置,适合分布不均的数据。

对称量化(Symmetric)要求零点 $z = 0$,量化区间关于 0 对称。它的好处是计算简单,卷积运算时不需要额外处理零点,但缺点是如果数据分布偏向某一边,量化区间会浪费一半的表示能力。就好比面馆的菜价基本都是正数,如果你把账本设计成可以记负数,那负数那一半就浪费了。

非对称量化(Asymmetric)允许 $z \neq 0$,可以精准覆盖数据分布的 $[\beta, \alpha]$ 区间,充分利用所有整数刻度。代价是计算时多一步零点偏移。选择哪种方式,取决于你的数据分布:ReLU 后的激活值都是非负的,用非对称更精准;权重一般接近零中心对称分布,用对称更简单。

一句话:对称适合“正负均匀”的权重,非对称适合“一边倒”的激活。选错了,就像用只能记正数的账本去记亏损,账面会严重失真。

qsym=round(rssym),ssym=max⁡(∣r∣)2b−1−1q_{\text{sym}} = \text{round}\left(\frac{r}{s_{\text{sym}}}\right), \quad s_{\text{sym}} = \frac{\max(|r|)}{2^{b-1} - 1}
qasym=round(rsasym)+z,sasym=α−β2b−1q_{\text{asym}} = \text{round}\left(\frac{r}{s_{\text{asym}}}\right) + z, \quad s_{\text{asym}} = \frac{\alpha - \beta}{2^b - 1}
  1. 对称量化公式$q_{\text{sym}}$ 直接缩放取整,没有零点偏移,适合权重这类零中心分布。
    qsym=round(rssym)q_{\text{sym}} = \text{round}\left(\frac{r}{s_{\text{sym}}}\right)
  2. 对称缩放因子$s_{\text{sym}}$ 取绝对值的最大值,确保正负范围都被覆盖。
    ssym=max⁡(∣r∣)2b−1−1s_{\text{sym}} = \frac{\max(|r|)}{2^{b-1} - 1}
  3. 非对称量化公式$q_{\text{asym}}$ 多了零点 $z$,可以处理偏移分布。
    qasym=round(rsasym)+zq_{\text{asym}} = \text{round}\left(\frac{r}{s_{\text{asym}}}\right) + z
  4. 非对称缩放因子$s_{\text{asym}}$ 用区间长度除以刻度数,区间越精准、误差越小。
    sasym=α−β2b−1s_{\text{asym}} = \frac{\alpha - \beta}{2^b - 1}

量化参数的确定

确定了对称还是非对称,下一步是确定缩放因子 $s$ 和零点 $z$——也就是找到最优的 $\alpha$ 和 $\beta$。伪量化训练中,有两种常见的做法:基于统计的初始化、和基于梯度的优化。

基于统计的做法是在训练中持续收集权重和激活的分布,然后取分位数(如 99.9% 分位点)作为 $\alpha$ 的候选值。为什么不用最大值?因为个别极端离群点(outlier)会拉大量化区间,导致大多数值精度降低。就像面馆里偶尔有客人点 9999 元的豪华套餐,如果账本必须覆盖这个极端值,那普通面条的记账精度就惨不忍睹。

基于梯度的做法是把 $\alpha$ 当作可学习参数,让它随训练一起优化。每轮迭代时,用梯度下降更新 $\alpha$,使量化误差最小化。这里的关键是让 $\alpha$ 的梯度可导:

实践中常把两种方法结合:先用统计初始化 $\alpha$,再用梯度微调。就像面馆先按过去的账单统计设定记账范围,再根据实际营业情况微调控价策略。

∂L∂α=∂L∂r~⋅∂r~∂α\frac{\partial \mathcal{L}}{\partial \alpha} = \frac{\partial \mathcal{L}}{\partial \tilde{r}} \cdot \frac{\partial \tilde{r}}{\partial \alpha}
∂r~∂α={−1r<−α0−α≤r≤α1r>α\frac{\partial \tilde{r}}{\partial \alpha} = \begin{cases} -1 & r < -\alpha \\ 0 & -\alpha \le r \le \alpha \\ 1 & r > \alpha \end{cases}
  1. 链式法则$\alpha$ 的梯度通过损失对量化值的依赖关系传递,需要用链式法则。
    ∂L∂α=∂L∂r~⋅∂r~∂α\frac{\partial \mathcal{L}}{\partial \alpha} = \frac{\partial \mathcal{L}}{\partial \tilde{r}} \cdot \frac{\partial \tilde{r}}{\partial \alpha}
  2. 区间外梯度当 $r < -\alpha$ 时,增大 $\alpha$ 能让量化值更接近真实值,所以梯度为 $-1$。
    ∂r~∂α=−1(r<−α)\frac{\partial \tilde{r}}{\partial \alpha} = -1 \quad (r < -\alpha)
  3. 区间内梯度当真实值在区间内时,$\alpha$ 的变化只影响缩放比例、对单个值影响很小,梯度为 0。
    ∂r~∂α=0(−α≤r≤α)\frac{\partial \tilde{r}}{\partial \alpha} = 0 \quad (-\alpha \le r \le \alpha)
  4. 区间上界外梯度当 $r > \alpha$ 时,增大 $\alpha$ 同样能减少溢出误差,梯度为 $+1$。
    ∂r~∂α=1(r>α)\frac{\partial \tilde{r}}{\partial \alpha} = 1 \quad (r > \alpha)

训练收敛策略

伪量化训练最大的痛点是:后训练阶段模型可能不收敛。原因很好理解:量化误差相当于给梯度引入了噪声,训练后期学习率变小,噪声占比变大,模型在损失曲面底部“抖抖索索”下不了台阶。

面馆里的对策是:不要一开业就启用“整数账本”,而是先精确记账一段时间,等生意稳定后再逐步切换到整数账本。这个“软着陆”思想对应三种常用策略:

逐步增强量化(Gradual Quantization):训练初期使用较高精度(如 16 位模拟),后期逐步切换到 8 位。这就像先让胃适应少盐,再变成完全无盐。可以按训练轮数线性调整量化位宽:

冻结底数技巧(Freeze Base):训练后期将量化参数 $\alpha$ 和 $z$ 固定不动,只更新权重。因为量化参数在后期已经稳定,继续更新反而引入额外噪声。就像面馆已经确定了记账区间,后面就不再频繁改动账本格式。

预热度过阶段(Warmup):前几个 epoch 用全精度训练,不启用伪量化,等模型稳定后再开启。让模型先找到一个大致的“好区域”,再引入量化噪声。这就像新员工先跟老员工学习完整工作流程,再被分配到有各种限制的实际岗位。

经验法则是:量化噪声应该在训练中期逐步介入,晚期保持稳定,让模型在“接近部署环境”的条件下收敛。

beff=bhigh−(bhigh−blow)×tTb_{\text{eff}} = b_{\text{high}} - (b_{\text{high}} - b_{\text{low}}) \times \frac{t}{T}
t={T×0.1warmupTfull QATt = \begin{cases} T \times 0.1 & \text{warmup} \\ T & \text{full QAT} \end{cases}
  1. 有效位宽$b_{\text{eff}}$ 在训练过程 $t$ 从 $b_{\text{high}}$ 线性过渡到 $b_{\text{low}}$。
    beff=bhigh−(bhigh−blow)×tTb_{\text{eff}} = b_{\text{high}} - (b_{\text{high}} - b_{\text{low}}) \times \frac{t}{T}
  2. 平滑过渡$t / T$ 是训练进度,从 0 到 1,位宽随之线性递减。
    tT\frac{t}{T}
  3. 预热阶段$t = T \times 0.1$ 表示前 10% 的训练轮次用于预热。
    t=T×0.1t = T \times 0.1

QAT与PTQ对比

最后我们来做一个决策选择:同样是部署到 int8,QAT(量化感知训练)和 PTQ(训练后量化,Post-Training Quantization)到底选谁?

PTQ 的思路是:模型训练完了,直接拿一批校准数据统计权重和激活的分布,确定 $\alpha$ 和 $z$,然后转换到 int8。这个流程快、不用改训练代码,但缺点是模型没有见过量化误差,遇到分布偏移时容易崩。就像面馆的账本突然改版,但老板和伙计都没提前适应新版式,月末对账时错误百出。

QAT 的思路是:训练过程全程使用伪量化模拟,让模型在参数更新时已经适应了量化噪声。QAT 的精度通常比 PTQ 高 1-3%,尤其在低比特(4 位)场景下优势更明显。代价是需要修改训练流程、增加训练时间,并且需要一份训练数据集或与训练分布接近的数据。

实际决策矩阵:如果模型敏感(NLP、推荐系统)或位宽低(4 bit),选 QAT;如果模型足够鲁棒(很多 CNN)且位宽 8 bit,可以先用 PTQ 试水。在工程实践中,通常先跑 PTQ 看精度,不达标再上 QAT。伪量化是 QAT 的核心组件,没有它 QAT 就无从谈起。

| 对比维度 | QAT | PTQ |

|---------|-----|-----|

| 精度 | 高(接近全精度) | 中(可能有损) |

| 训练成本 | 需要重新训练 | 只需校准数据 |

| 适用场景 | 低比特、敏感模型 | 8 bit、鲁棒模型 |

| 核心依赖 | 伪量化机制 | 校准集统计 |

AI 应用场景

移动端 NLP 模型量化部署

阶段:模型压缩与部署优化

解决的问题:手机端部署 BERT 等大模型时,float32 版本占用内存大、推理慢。传统 PTQ 直接量化后,问答准确率暴跌 8%,因为注意力层的激活值分布极其不均匀,量化误差被层层放大。使用包含伪量化的 QAT 流程后,模型在训练时就感知了低精度环境,部署精度损失控制在 1.5% 以内。

具体案例:某手机助手团队在训练阶段插入伪量化算子,用 8 位对称量化模拟权重、非对称量化模拟激活。训练 3 个 epoch 后导出 int8 模型,体积从 420MB 降到 105MB,推理速度提升 2.8 倍,问答准确率从 87.2% 仅降到 85.9%。

边缘设备视觉模型 QAT 优化

阶段:模型训练与部署适配

解决的问题:工业质检摄像头部署 YOLOv8 检测模型时,边缘设备只支持 int8 加速。如果直接用 PTQ,小目标漏检率上升 12%。改用伪量化 QAT 后,模型在训练前向中模拟 int8 计算,检测头对量化噪声变得鲁棒。

具体案例:某工厂质检产线使用伪量化训练 YOLOv8s,重点对前两层和检测头实施“逐步增强量化”,先在 float16 模拟 5 个 epoch,再切到 int8 模拟 10 个 epoch。最终漏检率仅上升 1.8%,帧率从 22 FPS 提升到 63 FPS。

Python 演示

python60 行
import numpy as np def fake_quantize(x, scale, zero_point, bits=8):    qmin, qmax = 0, 2**bits - 1    xq = np.clip(np.round(x / scale) + zero_point, qmin, qmax)    xdq = scale * (xq - zero_point)    return xq, xdq def quantize_params(x, per_channel=False, percentile=0.999):    if per_channel:        shape = x.shape        x_flat = x.reshape(shape[0], -1)        scales = np.zeros((shape[0],))        zero_pts = np.zeros((shape[0],))        for c in range(shape[0]):            vmin = np.percentile(x_flat[c], (1 - percentile) * 100)            vmax = np.percentile(x_flat[c], percentile * 100)            scale = (vmax - vmin) / 255.0            zero_point = max(0, min(255, round(-vmin / scale)))            scales[c] = scale            zero_pts[c] = zero_point        return scales.reshape(-1, 1, 1, 1), zero_pts.reshape(-1, 1, 1, 1)    vmin = np.percentile(x, (1 - percentile) * 100)    vmax = np.percentile(x, percentile * 100)    scale = (vmax - vmin) / 255.0    zero_point = max(0, min(255, round(-vmin / scale)))    return scale, zero_point def ste_gradient(weights, scale, zero_point):    return np.ones_like(weights) def qat_forward(weights, activation, scale_w, zero_w, scale_a, zero_a):    _, w_qdq = fake_quantize(weights, scale_w, zero_w)    _, a_qdq = fake_quantize(activation, scale_a, zero_a)    out = np.dot(a_qdq, w_qdq.T)    return out def qat_training_step(weights, x, y_true, lr=0.01):    scale_w, zero_w = quantize_params(weights)    scale_a, zero_a = quantize_params(x)    w_qdq = fake_quantize(weights, scale_w, zero_w)[1]    a_qdq = fake_quantize(x, scale_a, zero_a)[1]    y_pred = np.dot(a_qdq, w_qdq.T)    loss = np.mean((y_pred - y_true) ** 2)    grad = 2 * (y_pred - y_true) / y_true.shape[0]    grad_w = np.dot(grad.T, a_qdq)    grad_w = ste_gradient(w_qdq, scale_w, zero_w) * grad_w    weights -= lr * grad_w    return loss if __name__ == "__main__":    np.random.seed(42)    n_samples, n_features, n_out = 64, 16, 4    x = np.random.randn(n_samples, n_features).astype(np.float32)    w = np.random.randn(n_out, n_features).astype(np.float32) * 0.5    y_true = np.random.randn(n_samples, n_out).astype(np.float32)    for epoch in range(50):        loss = qat_training_step(w, x, y_true, lr=0.05)        if epoch % 10 == 0:            print("Epoch %d, loss=%.4f" % (epoch, loss))
目的:用 NumPy 从零实现一个包含伪量化的单层 QAT 训练循环,演示前向模拟量化和反向 STE 梯度更新。
  1. def fake_quantize(x, scale, zero_point, bits=8)定义伪量化核心操作:缩放、取整、加零点、剪裁、反量化。
  2. xq = np.clip(np.round(x / scale) + zero_point, qmin, qmax)正向量化过程,模拟“抹零”。
  3. xdq = scale * (xq - zero_point)反量化还原,得到带误差的浮点值。
  4. np.percentile(x, 99.9)用 99.9 分位数确定量化区间,避免极端离群值的影响。
  5. def ste_gradient(weights, scale, zero_point)模拟 STE 梯度直通:梯度恒为 1。
  6. np.dot(a_qdq, w_qdq.T)用量化后的激活和权重做矩阵乘法,模拟低精度推理。
  7. grad_w = ste_gradient(w_qdq, scale_w, zero_w) * grad_w反向传播时用 STE 让梯度通过量化层。
  8. weights -= lr * grad_w全精度更新权重,这是 QAT 的精髓所在。

常见误区

误区训练时用了伪量化,权重就已经是 8 位整数了,导出后直接就是 int8 模型。

正解伪量化的“伪”字正是关键——训练过程中权重始终以全精度浮点存储,只是前向传播时模拟了量化效果。就像面馆记账时虽然给客人看抹零账单,但后台账本依然精确到分。真正的 int8 转换发生在训练完成后的导出步骤,届时才把权重映射为整数。

为何常见:因为前向传播时打印权重确实看到整数,容易误以为模型已经是低精度存储。

误区STE 把梯度原封不动地传回去,等于完全不考虑量化误差对梯度的影响,反正“直通”么。

正解STE 是对取整函数不可导问题的一种近似策略,确实把梯度传回去了,但这不代表误差被忽略。误差已经通过前向传播的损失值影响了梯度的大小和方向。就像面馆的抹零差异已经影响了月底利润,只是你在逐笔计算时不追究每一笔的抹零细节。

为何常见:STE 的名字里有“straight-through“,容易理解成”直接穿过、什么都不管”。

误区QAT 在训练时就模拟了量化,精度永远优于 PTQ,所以无脑选 QAT。

正解QAT 优势明显,但有代价——需要修改训练流程、额外训练时间、还需要一份有代表性的训练数据。对于 8 位量化的鲁棒 CNN 模型,PTQ 往往已经够用;只有低比特(4 位)或对精度极敏感的模型(Transformer、推荐系统)才值得上 QAT。工程上应先试 PTQ,不达标再换 QAT。

为何常见:技术文章里 QAT 的精度数字更漂亮,读者容易忽略成本维度。

误区量化参数的 $\alpha$ 和 $\beta$ 直接用权重的最小值和最大值就行,何必用分位数?

正解极端情况下,个别离群点(outlier)会把 $\alpha$ - $\beta$ 区间拉得极大,导致大多数正常数值对应的刻度变粗、精度变差。比如一个权重是 100,其余都在 -1 到 1 之间,用 min/max 的话,每格代表约 0.78,正常值的量化误差接近 400%——相当于面馆为了一个万豪大单放弃了所有小账单的记账精度。

为何常见:从直觉上,min/max 是最自然的边界选择,容易忽略离群点的破坏性。

误区伪量化是训练阶段的技术,推理部署时就完全用不上了,两者没关系。

正解伪量化让训练时的计算图和部署时的 int8 计算尽量“对齐”——前向模拟的量化误差分布要和真实 int8 推理的误差分布一致,QAT 才有效。如果训练时用对称量化、部署时却用非对称,误差模型就错位了。实践上,需要将训练时伪量化算子的参数(scale、zero point)与推理引擎的量化参数严格对应。

为何常见:看到"Fake"就以为推理阶段无此概念,忽略了训练-部署对齐的必要性。

面试题

Q1. 伪量化中“伪”的含义是什么?

单选题 · mid

  • A.权重在训练中确实是假的低精度
  • B.前向模拟量化但反向用全精度← 正确答案
  • C.量化参数完全随机生成
  • D.训练结果不可用于部署

答案:B。伪量化的核心是“模拟”而非“真实转换”——前向传播让模型体验量化误差,反向传播依然用全精度梯度更新权重。B 正确。A 说权重本身变成低精度是错的,权重始终全精度存储;C 和 D 更是不相关。

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

单选题 · easy

  • A.前向传播速度太慢
  • B.取整函数梯度几乎处处为零← 正确答案
  • C.训练数据不够
  • D.模型过拟合严重

答案:B。取整函数 round 的导数几乎处处为 0,无法直接反向传播。STE 把梯度近似为 1 直接透传,让梯度能“绕开”取整不可导的问题。B 正确。

Q3. 对称量化和非对称量化最大的区别是什么?

单选题 · mid

  • A.对称量化不需要缩放因子
  • B.非对称量化必须使用 16 位
  • C.对称量化零点固定为 0← 正确答案
  • D.非对称量化精度一定更高

答案:C。对称量化强制零点 z=0,量化区间关于 0 对称;非对称量化允许 z≠0,更适合分布偏移的数据。C 正确。两种都是 8 位,A 错误;非对称不一定精度更高,还要看数据分布,D 错误。

Q4. 为什么量化参数 α 常用分位数而不是 max 绝对值?

单选题 · hard

  • A.分位数计算更快
  • B.避免离群点拉大量化区间← 正确答案
  • C.分位数保证无量化误差
  • D.max 值不符合硬件要求

答案:B。个别极端离群点会拉大 α 和 β 的区间,导致每个整数刻度的步长变大、正常数值的量化误差剧增。用分位数(如 99.9%)可以剪掉极端值,让量化范围覆盖绝大多数数据,B 正确。

Q5. 关于 QAT 和 PTQ 的对比,下列说法正确的是?

多选题 · mid

  • A.QAT 的精度通常高于 PTQ← 正确答案
  • B.PTQ 需要校准数据但无需重新训练← 正确答案
  • C.QAT 适用于所有场景且成本为零
  • D.低比特场景下 QAT 优势更明显← 正确答案

答案:ABD。QAT 在训练中感知量化误差,精度通常更高且低比特时优势突出(A、D 正确);PTQ 只需少量校准数据统计分析,无需重新训练(B 正确)。QAT 需要修改训练流程和数据,成本不为零,C 错误。

Q6. 伪量化前向传播的三个核心操作是什么?

单选题 · easy

  • A.缩放、取整、剪裁← 正确答案
  • B.卷积、池化、激活
  • C.归一化、正则化、剪枝
  • D.编码、解码、传输

答案:A。伪量化的前向操作包括:除以缩放因子进行缩放、round 取整模拟抹零、clamp 剪裁到有效整数范围。A 正确。B 是网络层操作,C 是训练技巧,D 是通信概念。

Q7. 训练收敛策略中,哪些手段可以缓解量化噪声带来的不收敛?

多选题 · hard

  • A.逐步增强量化位宽← 正确答案
  • B.冻结量化参数只更新权重← 正确答案
  • C.使用更大的初始学习率
  • D.预热阶段先全精度训练← 正确答案

答案:ABD。逐步量化让模型软着陆(A 正确);冻结底数避免后期噪声干扰(B 正确);预热让模型先收敛到合理区域再引入量化(D 正确)。学习率过大反而加剧不收敛,C 错误。

Q8. STE 反向传播时,量化函数的梯度近似为多少?

单选题 · mid

  • A.0
  • B.1← 正确答案
  • C.无穷大
  • D.与输入成正比

答案:B。STE 将取整函数的梯度近似为常数 1,即“直通”——梯度流过时不做任何缩放。如果梯度为 0,参数将无法更新,A 是错误近似。C 和 D 没有依据。

Q9. 伪量化中反量化操作的作用是什么?

单选题 · mid

  • A.把整数还原成带误差的浮点值← 正确答案
  • B.把浮点值压缩成整数
  • C.消除所有量化误差
  • D.增大模型的存储体积

答案:A。反量化 $r = s \times (q - z)$ 把整数映射回浮点范围,得到的值带量化误差。正向量化+反量化构成完整的模拟低精度环节,A 正确。B 是正向量化;C 错误,误差不可消除;D 与存储无关。

Q10. 训练时伪量化参数 α 作为可学习参数,当真实值小于 -α 时,损失对 α 的梯度是什么?

单选题 · extreme

  • A.0
  • B.1
  • C.-1← 正确答案
  • D.α

答案:C。当 r < -α 时,增大 α 可以让更多值落入量化区间,减少溢出误差,所以梯度为 -1。区间内梯度为 0,区间上界外为 +1。C 正确。