fbgemm PTQ 与 per-channel scale
PTQ 校准数据不代表部署分布时 INT8 掉点惨烈——校准集、per-channel 和 sensitive layer fp16 保留要一起调。

1. 校准集不覆盖部署分布,INT8 掉点惨烈
模型 fp32 训好,PTQ 量化后 val acc 掉 5pt+——常见根因是 calibration 数据不代表部署分布。校准集只有白天场景,部署有雨夜;或 calib 只有 50 batch,scale 估计不稳,per-tensor 量化时一个 outlier activation 拉偏整层 scale。INT8 部署不是「convert 一行就完」,是校准集、per-channel scale、敏感层保留三件事一起调。掉点大时先查 calib 覆盖,再查 sensitive layer,最后才考虑 QAT。
量化误差来源:权重舍入、activation 动态范围截断、accumulate 精度(INT32 累加后 requant)。per-channel weight scale 让每层每个 output channel 独立 scale,Conv 默认如此;activation 仍常 per-tensor,敏感层 keep fp32 可绕过 activation 截断。PTQ 假设 fp32 权重已收敛,用 calib forward 统计 activation 的 min/max 或 moving average 估 scale 。部署 slice 与 calib slice 错位时,某 slice 上 activation 超出 calib 见过的范围,INT8 饱和 clipping 增多,metric 断崖式下跌——这不是「模型变差」,是 scale 契约失效。
2. PyTorch PTQ 流程
model.eval()
model.qconfig = torch.quantization.get_default_qconfig('fbgemm') # x86
# mobile CPU 用 'qnnpack'
torch.quantization.prepare(model, inplace=True)
for x, _ in calib_loader: # 100–500 batch,覆盖部署场景
model(x)
torch.quantization.convert(model, inplace=True)fbgemm 用于 server x86,qnnpack 用于 mobile ARM——backend 与部署硬件必须一致,否则 latency 测了也白测。calib 须覆盖部署 slice(白天/夜/雨/室内/室外),缺 night 场景 calib 时 night slice 掉点往往最大。calib 时 model.eval() 固定 BN stats,与训练一致。
prepare 插入 observer 记录 activation 分布;convert 把 float module 换成 quantized module。calib loader 宜 shuffle 关闭、顺序与部署预处理一致——归一化 mean/std 错一位,scale 全偏。动态量化(仅 Linear)几乎不用在 CV deploy;静态 PTQ 才是检测/分割常态。x86 服务器上 fbgemm 对 Conv+ReLU 融合 kernel 成熟;ARM 上 qnnpack 对 depthwise 友好。若训练用 SyncBN、deploy 用 fused BN,convert 前须确认 BN 已 fold 进 Conv 或 stats 已固定,否则 calib 与 deploy graph 不一致。
3. 敏感层 skip 与 QAT
first conv(input 分布特殊,RGB 与归一化边界)和 last fc/head 常 keep fp32:
model.fc.qconfig = None # 该层不量化也可对 named module 遍历,按 skip list 设 qconfig = None。QAT:prepare_qat + fake quant modules 继续训 5–10 epoch,权重与 scale 联合适应,通常优于纯 PTQ;成本是额外训练与 QAT 超参(LR 常为 finetune 的 1/10)。Mobile 部署若 PTQ 掉点 2pt 内可接受,优先 PTQ 省工期。
敏感层判定可自动化:对每层做 量化误差 sensitivity 扫描——单独 keep fp32 其余 INT8,看 val 恢复幅度。first conv 输入动态范围受 RAW/RGB 与归一化影响,last head logits 小幅扰动即改变 argmax/mAP。中间层若某 channel activation 峰度极高(如 attention softmax 前),也可列入 skip。QAT 时 fake quant 在 forward 插入 round-trip,backward 用 STE;须用小 LR,否则 fp32 权重被「拉」向错误 scale 的局部最优。PTQ 与 QAT 的取舍:工期紧、掉点容忍 1–2pt → PTQ;关键 slice 掉点 >3pt → QAT 或 partial fp32。
4. 取舍与失败模式
PTQ:零训练成本,calib 代表性好时够用。QAT:掉点更小,需再训。敏感层 fp32:latency 略增,acc 恢复明显——first/last 是常见 skip list,中间某层 activation 动态范围极大时也可单独 skip。report 量化前后 val acc/mAP diff 和 部署端 latency 实测,不用 FLOPs 估算——INT8 加速依赖 kernel 与 memory bandwidth。
失败:calib 不覆盖部署分布;backend 选错;convert 后未 model.eval() 导致 BN 漂移;ONNX 导出未带 quant 节点,部署端又做一遍 PTQ 导致 double quant。另一类是 per-tensor activation scale 被 outlier batch 拉偏: calib 仅 50 batch 且含一张过曝图,整层 scale 变大,多数正常图量化分辨率下降。缓解:增加 calib 量、用 percentile clipping(如 99.9%)估 scale,或对 activation 做 per-channel(部分框架支持)。latency 验收必须在目标 SoC 上测——笔记本 x86 INT8 加速 3×,同一模型在嵌入式 ARM 可能仅 1.3×,因 memory bound。
5. 案例:缺 night calib 导致 night mAP 崩
某检测模型,白天 calib 500 batch,白天 val mAP fp32 0.78 / INT8 0.77。night val fp32 0.65 / INT8 0.52——差 13pt。补 100 batch night calib 后 night INT8 0.61;first conv + last head keep fp32 后 0.63。说明 night 掉点一半是 scale 估计、一半是 sensitive 层。
复盘时按 slice 画 fp32 vs INT8 的 score 分布:night slice 上 INT8 高分框数量锐减,多为 activation clipping 而非 NMS 阈值问题。补 night calib 后 scale 更接近 night activation 范围;first conv fp32 保留输入动态范围;last head fp32 避免 bbox 回归量化误差放大。若仍不满足业务阈值,再上 QAT 5 epoch,通常还能回收 1–2pt——成本与 deadline 需在项目初期写进 deploy checklist。
6. 验收
- calib 覆盖部署 slice;缺场景故意 ablation 看掉点幅度。
- 量化前后 val metric diff 在业务阈值内(如 mAP 掉 <1pt aggregate,关键 slice 单独报)。
- 目标硬件上测 latency 和 throughput,确认 INT8 真有加速。
- 对比 per-channel vs per-tensor;敏感层 skip 前后 acc 变化。
- ONNX/ORT 导出后抽样 batch 对齐 fp32 与 INT8 输出,排除 convert bug。
INT8 PTQ 的第一道闸是 calib 500 batch 覆盖部署场景。改 calib 或 skip list 时单变量 ablation,才分得清是 scale 问题还是层敏感问题。
相关
也可以看看
- ·3 分钟阅读
Temperature scaling 与 ECE
Softmax 分数不是概率——高置信度仍可能常错时,threshold 和 risk 决策会误导;ECE、温度缩放和 isotonic 要在部署域上评。
- ·3 分钟阅读
Shadow deploy 与 p99 latency SLA
离线涨点不上线,多半是 val 分布与生产不一致、latency 超标或 operating threshold 未重调——A/B 要看业务指标和 slice,不只看 mAP。
- ·4 分钟阅读
KL 蒸馏温度 T 与 feature MSE
大教师教小学生,学生会在教师也错的区域过度自信——温度 T、loss 权重和 hard label 仍要保留,蒸馏不是复制 logits 就完事。
johan's blog