📦 模型量化与端侧部署

为什么 FP32 模型必须"瘦身"才能上机器人 —— 量化四本账 · scale/zero_point 数学 · PTQ/QAT 两条流水线 · 2:4 稀疏化 · 推理引擎图优化 · PyTorch 量化代码实战 · 量化对 NPU RTL 的反向要求
INT8 量化 PTQ / QAT 2:4 稀疏化 推理引擎 端侧部署 软硬结合
🎯 本页学习目标
1. 能算清量化的"四本账":INT8 相对 FP32 在存储、带宽、功耗、算力面积上各省多少
2. 能写出对称/非对称量化的 scale 与 zero_point 公式,并解释 per-channel 为什么救精度
3. 能画 PTQ(校准)与 QAT(伪量化 + STE)两条流水线,并说出各自的适用场景
4. 能解释推理引擎的算子融合、tensor arena 内存规划,以及 2:4 结构化稀疏的跳零加速原理
5. 能跑通 PyTorch 动态量化 + FX 静态 PTQ + ONNX 导出的完整流水线,并设计 LeNet 量化前后精度对比实验
建议用时:约 50 分钟(本页是「第二阶段 · AI 通识课」的核心页,直接为阶段三 NPU RTL 设计提供算法侧输入)

1 为什么要量化:算力、带宽、功耗、存储四本账

1.1 为什么要量化:把"四本账"算清楚

是什么:量化(Quantization)就是把神经网络里的 FP32 浮点权重和激活,近似映射成低位宽的整数(INT8 甚至 INT4)来存储和计算。为什么:因为在机器人端侧,你被四本账同时卡死 —— 存储装不装得下、带宽喂不喂得动、电池供不供得起、算力转不转得快。只要把下面这本账算清楚,你就明白为什么业界几乎所有 NPU 都把 INT8 当作"第一公民"。

账本FP32 基线量化到 INT8 后为什么省
① 存储ResNet-50 约 98 MB约 25 MB(4× 缩减)每个权重 4 字节 → 1 字节,模型文件/片上 SRAM 占用同步缩小
② 带宽读一个权重 32 bit读一个权重 8 bit(4× 缩减)NPU 是"访存受限"的:MAC 阵列算得快不快,取决于权重/激活喂得进喂不进
③ 功耗从 DRAM 读 32 bit 数据同样数据量能耗约 1/4访存能耗远大于计算能耗(每搬 1 bit 过 DRAM 总线的能耗是乘加的几十上百倍),数据变窄 → 搬运能耗直接按比例降
④ 算力/面积FP32 MAC:对阶、规格化等浮点逻辑,面积大、主频难提INT8 MAC:同样面积可塞下数倍的乘加单元8×8 乘法器面积约为 32×32 浮点路径的零头;整数加法不需要对阶,流水线短、功耗低。所以同一颗 NPU,INT8 TOPS 往往是 FP16 的 2 倍、FP32 的 4~8 倍
量化四本账(以 INT8 = 1× 为基准的相对开销) FP32 FP16 INT8 98MB 49MB 25MB ① 存储占用 ResNet-50 权重大小(近似) ② 带宽需求 搬运同样多权重占总线宽度 ③ 推理能耗(访存主导) 第④本账「算力面积」同理: INT8 TOPS ≈ FP32 的 4~8× 记忆锚点:位宽减半 → 存储/带宽/能耗约减半;FP32 一步到 INT8 → 三本账同时乘 1/4。
图 1:量化四本账——FP32/FP16/INT8 的存储、带宽与能耗对比

机器人端侧的例子:一台人形机器人的头部 SoC(如瑞芯微 RK3588 的 6 TOPS INT8 NPU,或 Jetson Orin Nano),要同时跑双目深度、人体检测、VSLAM、语音 VAD,还可能要给 VLA 小模型留算力。若全部用 FP32:一个检测模型就占上百 MB 内存、NPU 的浮点吞吐只有 INT8 的零头,30 fps 的感知闭环根本转不起来;量化到 INT8 后模型缩小 4×、NPU 跑满 6 TOPS、发热也压得住 —— 这就是"量化是部署的第一课"的原因。

端侧平台(量级参考,官方标称)INT8 算力对量化部署的含义
瑞芯微 RK3588 NPU约 6 TOPS(INT8)国产 SoC 常见配置:量化到 INT8 才"够得着"实时,FP32 只剩零头可用
NVIDIA Jetson Orin Nano约 40 TOPS(INT8)机器人头部/小整机常用;TensorRT 全整型链路成熟,INT8 是默认交付形态
NVIDIA Jetson AGX Orin约 275 TOPS(INT8)整机大脑级平台:留得住 VLA/多路感知,但功耗预算同样逼你上 INT8

一个粗算的"感知预算"例子:三路模型(检测 1.5 TOPS + 深度 1.2 TOPS + VSLAM 1.0 TOPS)按 30 fps 连续跑,平均需求约 (1.5+1.2+1.0) TOPS;在 6 TOPS 的 NPU 上,若全用 FP32(算力按 INT8 的 1/4 计,有效约 1.5 TOPS),连一路都跑不满 —— 全 INT8 量化则是用掉 62% 算力,还给语音和 VLA 留了余量。学会算这本账,你在选型会上就有话语权。

🎤 面试怎么问:"INT8 相对 FP32 能带来多少收益?收益是从哪来的?" —— 标准答法:存储与带宽 4×、访存能耗约 1/4、NPU 的 INT8 算力面积效率数倍于浮点;并补一句"NPU 常见访存受限,所以带宽那一本账往往最关键"。
⚠️ 易错点:①量化 ≠ 线性提速 4×:实际加速比受访存模式、算子是否被引擎支持、是否回退 CPU 执行影响,常见实际只有 2~3×;②"省的是带宽不只是存储" —— 很多初学者只记得模型变小,忽略了 NPU 上真正的瓶颈经常是数据搬运。

2 数值表示与量化数学

2.1 数值表示基础:FP32 / FP16 / BF16 / INT8 到底差在哪

是什么:浮点数用「符号 + 指数 + 尾数」三段表示一个实数:FP32 是 1 位符号 + 8 位指数 + 23 位尾数,解码为 (-1)S × 2E-127 × (1 + M/223);定点整数(INT8/INT16)则只有一段裸整数,靠一个外挂的 scale 系数还原量纲。为什么 FP32 对硬件不友好:浮点加法必须先「对阶」—— 比较两个数的指数、把小指数的尾数右移对齐,再相加、再规格化;这条路径让浮点 MAC 的面积、功耗、流水线深度都远大于纯整数 MAC。这就是为什么 NPU 的 MAC 阵列几乎都做成 INT8/INT16 整数阵列(衔接 08-07 脉动阵列)。

格式位宽(符/指/尾)动态范围有效精度典型用途
FP321 + 8 + 23约 ±3.4×1038约 7 位十进制训练金标准、对拍基准;端侧几乎不用
FP161 + 5 + 10约 ±65504约 3.3 位十进制GPU 混合精度训练、部分 NPU 推理
BF161 + 8 + 7与 FP32 相同(指数同为 8 位)约 2~3 位十进制大模型训练/推理:范围大不易溢出,精度靠累积
INT8纯整数 8 bit + scale256 级台阶相对量程 ≈ 1/256端侧部署主力,NPU 第一公民
INT16纯整数 16 bit + scale65536 级台阶相对量程 ≈ 1/65536MCU 上精度敏感层(CMSIS-NN 支持 int16)、音频/信号类模型

记忆锚点:指数位决定动态范围,尾数位决定分辨率。BF16 把尾数砍到 7 位却保留 8 位指数,就是"牺牲分辨率保范围不溢出"的典型取舍;INT8 更激进 —— 直接把范围交给人工选定的量化区间 [rmin, rmax],256 级台阶全花在分辨率上。

🎤 面试怎么问:"BF16 和 FP16 都是 16 位,有什么区别?为什么大模型训练偏好 BF16?" —— 答:BF16 指数 8 位与 FP32 对齐,动态范围相同、不易上下溢,只是尾数精度低;FP16 范围小,训练中容易溢出需要 loss scaling。
⚠️ 易错点:不要把"定点数"和"浮点数"混为一谈 —— 定点 Qm.n 格式(如 Q1.7)的小数点是约定死的,靠软件记住 scale;TFLite 的 INT8 模型里每个张量都带着自己的 scale/zero_point 元数据,推理时硬件只做纯整数运算。

2.2 量化数学:scale、zero_point 与反量化

是什么:量化就是把实数区间 [rmin, rmax] 线性映射到整数区间,映射关系由两个参数唯一确定:scale(步长 s)决定一个整数台阶代表多大实数,zero_point(零点 z)记录实数 0 落在哪个整数上(保证 0 被精确表示,否则卷积的 padding 全部产生误差)。

s = \frac{r_{max} - r_{min}}{2^b - 1}, \qquad z = \mathrm{round}\!\left(-\frac{r_{min}}{s}\right)
q = \mathrm{clip}\!\left(\mathrm{round}\!\left(\frac{r}{s}\right) + z,\; 0,\; 2^b - 1\right) \qquad \text{(非对称量化,UINT8: b=8)}
\hat{r} = s \cdot (q - z) \qquad \text{(反量化:推理输出、精度对拍时用)}

对称量化是非对称的特例:令 rmin = −rmax,则 z = 0,公式简化为 q = clip(round(r/s), −127, 127),其中 s = max|W| / 127。权重常用对称(有正负、分布近零对称),激活常用非对称(如 ReLU 后全为非负,zero_point=0 的对称量化会浪费一半编码空间)。

FP32 实数轴(连续,例:[-1.0, 2.0]) r_min = -1.0 r = 0.83 r_max = 2.0 UINT8 整数轴(仅 256 级台阶) 0 z = 85 (zero_point) 128 q = 155 255 r_min ↔ 0 q = clip(round(r/s) + z) r_max ↔ 255 s=(2.0−(−1.0))/255≈0.0118 z=round(1.0/0.0118)≈85 反量化: r̂=s·(q−z)=0.0118×70≈0.83
图 2:仿射量化映射示意——浮点区间 [r_min, r_max] 线性映射到 UINT8 整数轴
🎤 面试怎么问:"写一下非对称量化的公式,zero_point 为什么必须存在?" —— 答:q = clip(round(r/s)+z, 0, 255),z 让实数 0 精确落在某个整数上;没有 z,ReLU 的 0 与卷积 padding 会被量化成 ±s/2 的误差并逐层放大。
⚠️ 易错点:①round 是四舍五入不是向下取整;②clip 范围别忘了 +z 之后做 —— 先 clip 再加 z 是经典 bug;③对称量化 INT8 取 ±127 而不用 −128,是为了让量化器对称、硬件省一个特殊 case(TFLite 全整型方案同样如此)。

2.3 量化粒度:per-tensor vs per-channel

是什么:一组 (s, z) 作用在多大范围上,就是量化粒度。per-tensor:整个张量共用一组参数,硬件最友好;per-channel:卷积权重的每个输出通道各有一组参数(激活通常仍用 per-tensor,因为推理时激活的统计范围随输入变化,逐通道 scale 会拖垮硬件调度)。

对比项per-tensor(逐张量)per-channel(逐通道)
参数量1 组 (s, z)输出通道数 Cout 组 (s, z)
硬件实现一次乘法即可完成 requantize,最简单累加后按通道取不同 multiplier,需要小查表逻辑
精度各通道动态范围差异大时明显掉点量化掉点的第一救命稻草,MobileNet 等深度可分离卷积模型几乎必开
典型场景激活张量卷积/全连接权重

为什么 per-channel 救精度:不同输出通道的权重动态范围可能相差一个数量级(有的通道权重普遍偏大,有的接近 0)。per-tensor 用"最大通道的最大值"定 scale,等于让小范围通道只能用 256 级台阶里很窄的一段,分辨率被白白浪费;per-channel 让每个通道都吃满 ±127 的整数刻度,舍入误差整体缩小。这就是 08-07 里说的"硬件要让算法有得选"—— 好的量化方案必须能映射成可实现的 RTL。

🎤 面试怎么问:"per-channel 为什么好?为什么激活一般不用 per-channel?" —— 见上文;加分项:提到 per-channel 权重 + per-tensor 激活是 TFLite/PyTorch 默认组合,硬件侧对应"每个输出通道一个 INT32 multiplier + shift"。
⚠️ 易错点:per-channel 的通道轴是输出通道(权重的 shape [Cout, Cin, k, k] 的第 0 维),别按输入通道切;另外逐通道量化会让"算 scale"从一次统计变成 Cout 次统计,校准代码要按通道分组。

3 PTQ 与 QAT:两条量化路线

3.1 PTQ 训练后量化:不训练,靠校准数据算 scale

是什么:PTQ(Post-Training Quantization)在模型训练完成之后做量化:拿几百张有代表性的数据"喂"进模型跑前向(不训练),统计每层激活的数值分布,据此算出 scale/zero_point,然后一次性把权重和激活转成整数。为什么常用:零训练成本、不需要原始训练代码和数据集,几十分钟就能出结果,是端侧部署的默认第一步。

路线 A · PTQ 训练后量化(不训练,几十分钟) 训练好的FP32 模型已收敛权重 喂校准数据几百张只前向不训练 统计激活分布min-max /KL 熵 / 百分位 算 scale/zp每层每组参数权重+激活 INT8 模型直接导出验证精度 路线 B · QAT 量化感知训练(要微调,精度更高) FP32 模型+ 训练集可微调 插入伪量化节点量化→反量化前向走阶梯 正常训练前向+反向STE 直通梯度 微调恢复精度小学习率几个 epoch convert转真 INT8导出 两条线终点相同:INT8 模型 → 交给推理引擎。策略:先跑 PTQ,精度达标就收工;掉点 >1% 再上 QAT。 经验:小模型/低比特(INT4)/Transformer 注意力层 QAT 几乎必需;CNN 分类/检测用 PTQ+per-channel 常只掉 0.5% 内。
图 3:PTQ 与 QAT 两条量化流水线对比——校准统计 vs 训练中模拟量化

PTQ 的灵魂在校准方法 —— 怎么从激活的统计分布里选出量化区间 [rmin, rmax]。三种主流方法对比:

校准方法原理优点缺点典型使用
Min-Max取校准集上每层激活的最小/最大值作为量化区间最简单、可复现、不留截断对异常值敏感:一个 outlier 把 scale 撑大,正常值的分辨率暴跌PyTorch/TFLite 默认;分布规整的模型首选
熵校准(KL)枚举候选截断阈值 t:把 [rmin, t] 量化再反量化得到近似分布 Q,与原分布 P 算 KL 散度,取 KL(P‖Q) 最小的 t关注整体分布形状,天然抗异常值逐候选阈值算直方图,校准慢;实现复杂TensorRT 早期经典方案(INT8 白皮书思路的工程化)
百分位截断取激活分布的 99.9% / 99.99% 分位数当 rmax,尾部极端值直接丢掉一次统计、实现简单,抗 outlier 效果好分位阈值要调;被截掉的值产生削峰误差激活有稀疏大值的检测/注意力模型常用
🎤 面试怎么问:"PTQ 的完整流程是什么?三种校准方法怎么选?" —— 按上图答流程;选择策略:先 min-max,掉点超标换 KL 或百分位,配合 per-channel;并强调"校准数据必须覆盖实机真实分布"。
⚠️ 易错点:①校准数据量不是越多越好,几百张有代表性的即可,关键在分布覆盖;②KL 校准选的是"量化后信息损失最小"的截断点,不是"最大值"——把熵校准理解成找 min-max 就错了。

3.2 QAT 量化感知训练:伪量化节点与直通估计器 STE

是什么:QAT(Quantization-Aware Training)把量化"搬进"训练过程:在网络的每个权重/激活后面插入伪量化节点(fake-quantize),前向传播时先把数值量化成 INT8 再反量化回浮点 —— 网络每一步都在"亲身体验"量化后的台阶值;为什么精度更高:训练时损失函数已经包含量化噪声,梯度下降会自动把权重调整到"量化后依然正确"的位置,相当于网络对量化误差产生了免疫力。奠基性工作是 Google 的 INT8 白皮书(arXiv:1712.05877),TFLite 全整型方案即源于此。

\text{伪量化(前向):}\quad \hat{r} = s \cdot \left[\mathrm{clip}\!\left(\mathrm{round}\!\left(\tfrac{r}{s}\right) + z,\; q_{min},\; q_{max}\right) - z\right] \qquad \text{STE(反向):}\quad \frac{\partial \hat{r}}{\partial r} \approx 1

STE 为什么必须存在:round 是阶梯函数,梯度几乎处处为 0 —— 反向传播一遇到 round 梯度就"断流"。直通估计器(Steal-Through Estimator)粗暴地把 round 的梯度当成 1,让上层梯度"原样穿过"伪量化节点,训练得以继续。数学上不严格,但工程上极其有效,是 QAT 的标准做法。

怎么做(量化 + 微调流程):① 加载训练好的 FP32 模型;② prepare:插入观测器(统计 min/max)与伪量化节点;③ 用训练集(或其子集)以小学习率(如原学习率的 1/100)微调几个 epoch,BN 统计照常更新;④ convert:把观测器统计转成最终 scale,伪量化替换为真量化算子,导出 INT8 模型。

🎤 面试怎么问:"QAT 和 PTQ 怎么选?STE 是什么、为什么需要它?" —— 答:PTQ 优先、掉点超标再上 QAT;round 不可导,STE 把梯度当 1 直传;补一句"QAT 微调是恢复精度,不是重新训练,所以只需几个 epoch"。
⚠️ 易错点:①QAT 微调用大学习率会把已收敛权重打飞,精度反而更差;②QAT 前先跑一次 PTQ 对比基线,否则你不知道 QAT 到底赚了多少;③不是 epoch 越多越好 —— 过度微调会让权重过拟合到训练集的量化噪声上,泛化变差。

3.3 量化误差从哪来:舍入、截断与"敏感层"

是什么:量化误差主要有两笔:舍入误差 —— round 把实数吸附到最近的台阶,误差均匀分布在 ±s/2 内,温和且各层互相抵消;截断(削峰)误差 —— 超出校准区间的异常值被 clip 到边界,单个值的误差可达数个 s,是掉点的主要元凶。误差还会逐层累积放大:上一层的输出误差成为下一层的输入扰动。

怎么做(救精度清单):① 权重 per-channel 量化(第一救命稻草);② 首层、末层保持 FP16/INT16 高精度(混合精度量化);③ 换校准方法(min-max → 百分位/KL);④ 补充校准数据覆盖实机真实分布;⑤ 用逐层余弦相似度/SQNR 定位误差最大的层,精准 QAT 只救那一两层。

🎤 面试怎么问:"量化为什么掉精度?哪些层最敏感、为什么?" —— 按舍入/截断/累积三层作答,再用首层(分布不稳)末层(logit 差距小)举例,最后给救精度清单,基本就是满分答案。
⚠️ 易错点:①掉点就无脑全模型 QAT —— 先做逐层误差分析,90% 的情况是一两个敏感层在作怪;②用 ImageNet 归一化均值做校准、却在实机摄像头(不同白平衡/分辨率)上部署,分布错配是端侧项目最常见的翻车点;③偏置(bias)不量化 —— 标准做法是 INT32 存储、在 requantize 阶段累加,把 bias 也压到 INT8 属于自找掉点。

4 稀疏化与推理引擎

4.1 稀疏化性能优化:剪枝、2:4 结构化稀疏与硬件跳零

是什么:稀疏化(Sparsity)把权重矩阵中"不重要"的连接置零 —— 剪枝依据通常是权重大小(magnitude):数值接近 0 的权重对输出贡献小,砍掉后微调几步即可恢复精度。它与量化正交:量化压"每个数的位宽",稀疏化压"有多少个数",两者收益可以叠加。

对比项非结构化剪枝结构化剪枝2:4 半结构化稀疏
剪法单个权重任意置零,稀疏度可达 90%+整通道/整滤波器剪除,模型直接变小一号每连续 4 个权重至少 2 个为 0,固定 50% 稀疏度
硬件加速难:稀疏位置不规则,索引开销吃掉收益,通用 CPU/GPU 基本白剪好:剪完是稠密小模型,任何硬件直接受益NVIDIA Ampere 起 Sparse Tensor Core 原生支持,数学吞吐 2×
压缩存储最好(值 + 索引稀疏格式)与剪枝比例成正比50% + 每值 2 bit 元数据
典型用途模型压缩存储、学术研究端侧模型瘦身GPU/TensorRT 推理加速(NVIDIA 三步法:稠密训练 → 按 magnitude 剪到 2:4 → 微调恢复)

硬件跳零加速原理:权重按"非零值 + 2 bit 位置索引"压缩存放,MAC 阵列读数时遇到 0 直接跳过乘法——乘法器不翻转,省动态功耗;对 2:4 这种规则模式,硬件还能成对复用加法树,等效数学吞吐翻倍。NVIDIA 官方数据:A100 的 Sparse Tensor Core 下 FP16 达 624 TOPS(稠密 312)、INT8 达 1248 TOPS(稠密 624);TensorRT 8.0 实测稀疏 ResNeXt-101 推理性能提升约 20%、小批量场景性能/功耗比提升约 36%,且精度与稠密基线持平。

# 2:4 稀疏微观示例:每 4 个连续权重为一组,每组保留绝对值最大的 2 个
dense  = [ 0.42, -0.03,  0.51,  0.11 ]     # 剪枝前的稠密权重组
sparse = [ 0.42,  0.00,  0.51,  0.00 ]     # 剪掉 -0.03 和 0.11(幅度最小)
indices= [   00,     --,     10,     -- ] # 每个非零值附带 2 bit 位置索引
# 硬件视角:Y = 0.42*x0 + 0.51*x2,两次乘加完成"四次乘加"的等效工作量 → 2× 吞吐
# 注意:剪完必须微调,让剩余 2 个权重"接管"被剪权重原本承担的输出分量

剪枝-量化组合流水线:顺序必须是先剪后量 —— 剪枝会改变权重分布,先量化再剪会让 scale 全部作废。标准流水线:① 稠密训练 → ② 按 2:4 / 通道粒度剪枝 → ③ 微调恢复精度 → ④ INT8 量化(PTQ 校准或 QAT 微调)→ ⑤ 导出部署。收益叠加:2:4 让权重数减半,INT8 让每权重位宽减为 1/4,合计权重量约 1/8。

🎤 面试怎么问:"非结构化剪枝压缩率那么高,为什么端侧很少用?" —— 答:稀疏索引解码开销与不规则访存让通用硬件无法加速,收益只在存储端;要"算得快"得用结构化或 2:4 这类硬件友好的规则稀疏。
⚠️ 易错点:①剪枝后必须微调再评估,剪完直接测精度掉成渣是正常现象不是方法错误;②2:4 稀疏只在支持 Sparse Tensor Core 的硬件(Ampere 及以后 + TensorRT/PyTorch 对应路径)上有加速,部署到 NPU/CPU 前先确认硬件支持;③量化与剪枝同时一步做,几乎必掉点 —— 分步走、每步给恢复期。

4.2 推理引擎原理:计算图、图优化与内存规划

是什么:推理引擎(Inference Engine)是把训练框架导出的模型,变成目标芯片上高效可执行程序的那套运行时。它拿到的是一个计算图(算子为节点、张量为边),核心工作有三件:图优化内存规划算子调度

与编译器的关系(一句话定位):TVM 是"把计算图编译成目标硬件高效代码"的深度学习编译器(图优化 + 自动算子代码生成);MLIR 是"提供多层级中间表示"的编译器基础设施,让各家芯片厂在自己的 IR 层上接入 —— 前者是产品,后者是造产品的框架。

① 模型格式层 ONNX(.onnx,通用中间格式) · TFLite(.tflite,flatbuffer+量化元数据) · NCNN(.param 结构 + .bin 权重) ② 图优化 / 编译层(引擎的大脑) 算子融合 conv+BN+ReLU · 常量折叠 · 布局变换 · 内存规划(tensor arena 静态内存池) · 量化信息落地 一句话定位:TVM = 深度学习编译器(图优化+代码生成);MLIR = 多层级 IR 的编译器基础设施 ③ 算子库层(手工优化的 kernel) ncnn / MNN / XNNPACK(移动 CPU) · CMSIS-NN(Cortex-M) · cuDNN/TensorRT(GPU) · NPU 专用算子库 ④ 硬件层 CPU(NEON/SVE 向量) · GPU · DSP · NPU(INT8 MAC 阵列,见 08-07 脉动阵列) 一次推理 = ①的模型被 ② 重排融合后,按拓扑序逐算子调用 ③ 的优化 kernel,最终落在 ④ 上执行。
图 4:端侧推理引擎四层栈——模型格式/图优化编译/算子库/运行时
🎤 面试怎么问:"推理引擎为什么能加速?conv 后面的 BN 为什么可以直接融掉?" —— 答:推理时 BN 是固定仿射变换可折叠进权重,融合消灭中间张量访存;再列常量折叠/布局变换/内存规划,体现"引擎赚的主要是访存和调度,不是发明新数学"。
⚠️ 易错点:①图优化发生在离线转换阶段,不要和运行时动态批处理混为一谈;②融合的前提是引擎支持对应融合模式 —— 你在 PyTorch 里手写的花式结构,导出后可能根本融不起来,写模型时就要照顾部署;③tensor arena 是静态单块内存, arena 给小了直接分配失败,给大了浪费珍贵 RAM,必须实测校准。

5 端侧部署实战与代码

5.1 端侧部署实战要点:格式、算子支持度、arena 与时延

是什么:从"量化好的模型"到"机器人上跑起来的推理",中间隔着模型格式、转换工具、运行时内存和实测时延四道坎。怎么做:按下面的流水线走,每一步都有明确的检查物。

1训练 FP32 基线
固化精度/大小/时延三项基线指标,量化后一切对比以它为准
2量化(PTQ/QAT)
先 PTQ 后 QAT;逐层对拍余弦相似度,锁住掉点层
3导出 + 检查图
导出 ONNX(opset 13+),用 Netron 打开逐算子检查
4转换引擎格式
onnx2ncnn / MNN 转换 / tflite 转换;核对算子支持度表
5板端集成
静态内存 arena、输入预处理对齐训练、初始化引擎
6实测与上线
板端时延(预热+取均值)、内存峰值、精度回归三表齐全
格式文件构成特点与适用
ONNX单文件 .onnx(protocol buffer)框架无关的通用中间格式,生态最广;转换链路的"枢纽站":PyTorch → ONNX → 各引擎私有格式
TFLite / TFLite Micro单文件 .tflite(flatbuffer,内嵌 scale/zero_point 量化元数据)全整型 INT8 方案成熟;Micro 版面向 MCU,配 tensor arena 静态内存,是 Cortex-M 上的事实标准
NCNN两个文件:.param(网络结构)+ .bin(权重)腾讯开源,手机端老牌强者;纯 C++ 零依赖、ARM NEON/汇编手工优化,INT8 与 FP16 都支持;PyTorch 走 pnnx/ONNX 转入
MNN转换后的 .mnn 模型阿里开源,淘宝/天猫等 30+ App 生产验证;CPU/GPU/NPU 多后端,MNN-LLM 可端侧跑大模型
🎤 面试怎么问:"一个 PyTorch 模型要部署到机器人 SoC,说出完整链路和每步的风险点。" —— 按上面六步流水线作答,风险点:算子回退 CPU、arena 估错、预处理与训练不一致、时延测量不预热。
⚠️ 易错点:①Netron 里图"能打开"≠引擎"能执行";②训练用 NCHW、NPU 爱 NHWC,布局转换忘了做会慢一截;③量化模型上线前必须做端到端精度回归(实机数据集),离线对拍通过不代表真机不掉点。

部署翻车案例速查(现象 → 根因 → 修复)

现象最可能根因修复动作
量化后精度暴跌(掉 5% 以上)激活有异常值,min-max 校准被撑爆 / 敏感层没保护换百分位或 KL 校准;权重 per-channel;首末层 FP16;逐层对拍定位
板端时延是预期 3~5 倍部分算子不被 NPU 支持,回退 CPU 执行看引擎日志逐算子核对设备;替换/改写不支持算子
tflite-micro 报 arena 不足tensor arena 估算过小或模型含 int16 大张量内存报告定位峰值层;降输入分辨率;二分调 arena
离线对拍全过,真机精度却掉端侧预处理(归一化/色域/插值)与训练不一致逐算子比对预处理输出;统一到训练侧的数值管线
推理结果偶发"花屏/全黑"累加器溢出回绕,或输入溢出量化区间被饱和到边界检查硬件饱和逻辑;检查输入 scale 是否按实机数据范围定标
同模型不同次测量时延波动大CPU 降频/热节流,测量前未预热锁频、预热 N 帧再取均值;记录板温
导出 ONNX 后精度与 PyTorch 不一致动态算子(如 view/reshape)trace 错误,或 opset 过低提高 opset 版本;用 torch.onnx.export 前做数值对拍
QAT 微调精度反而低于 PTQ学习率过大打飞权重,或未从 PTQ 好的参数出发学习率降到原来的 1/100 量级;QAT 初始化用 PTQ 结果
INT8 模型跑出的却是 FP32 速度引擎没识别量化参数,静默回退浮点执行检查 Q/DQ 节点或量化参数表格式与引擎约定一致;看日志确认 kernel 类型
多线程/多帧并发时结果错乱引擎上下文/arena 被多路推理共享冲突每路独立 arena 与 context,或加锁串行化推理入口
📋 上线前五分钟检查清单:① 量化模型与 FP32 基线在实机数据集上的精度回归报告;② 引擎日志确认所有算子落在目标设备(无 CPU 回退);③ 板端时延 = 预热后 100+ 帧均值,含 P95;④ 峰值内存(权重 + arena + 引擎)实测值;⑤ 输入预处理与训练侧逐算子一致(归一化/色域/插值)。五项齐全才允许合入发布分支。

5.2 实战:PyTorch 量化流水线代码 + LeNet 精度对比实验

动态量化最简单:权重提前转 INT8,激活在推理时动态量化,只适合 Linear/LSTM 类算子。FX/静态 PTQ 是端侧标准做法:整图捕捉 → 插观测器 → 校准 → 转换。下面是一套可直接跑的完整流水线(MNIST LeNet 为例):

import torch, torch.nn as nn
from torch.ao.quantization import quantize_dynamic, get_default_qconfig

# ---------- 1) 动态量化:3 行搞定,只量 nn.Linear / LSTM 等算子 ----------
model_fp32 = my_lenet.eval()                     # 训练好的 FP32 模型
model_dyn  = quantize_dynamic(
    model_fp32.cpu(), {nn.Linear}, dtype=torch.qint8)

# ---------- 2) FX 静态 PTQ:校准数据 → 统计分布 → 算 scale ----------
from torch.ao.quantization import quantize_fx, fuse_fx
example_input = torch.randn(1, 1, 28, 28)        # 校准样本,与训练同分布
qconfig_dict  = {"": get_default_qconfig("qnnpack")}  # Arm 端用 qnnpack,x86 用 x86
model_fused   = fuse_fx(model_fp32.cpu().eval(), example_input)  # conv+BN+ReLU 先融合
model_pre     = quantize_fx.prepare_fx(model_fused, qconfig_dict)
with torch.inference_mode():                     # 校准:只前向,统计 min/max
    for _ in range(100):
        model_pre(torch.randn(1, 1, 28, 28))
model_int8 = quantize_fx.convert_fx(model_pre)   # 伪量化 → 真 INT8 模型

# ---------- 3) 导出 ONNX,交给 NCNN / MNN / TensorRT ----------
torch.onnx.export(
    model_int8, example_input, "lenet_int8.onnx",
    opset_version=13, input_names=["input"], output_names=["logits"])
# 之后:onnx2ncnn / pnnx 转 NCNN;或 MNNConvert 转 .mnn 上板

LeNet INT8 量化前后对比实验设计(建议当成你的第一次量化实验,所有指标自己测、自己填表):

步骤操作记录指标通过标准(参考)
① 基线训练 LeNet 至收敛,测 FP32 精度、模型大小、PC 端时延Top-1 ≈ 99.2%,约 1.7 MB作为三列基线
② 动态量化quantize_dynamic 只量 Linear精度/大小/时延精度几乎无损(−0.1% 内)
③ 静态 PTQFX prepare → 100 张校准 → convert,全模型 INT8精度/大小/时延;逐层余弦相似度精度掉点 ≤ 0.5%,模型 ≈ 0.45 MB(4×)
④ 逐层对拍FP32 与 INT8 同输入跑前向,比对每层输出余弦相似度 / SQNR各层相似度 ≥ 0.999,末层重点盯
⑤ 上板导出 ONNX → NCNN(MNN/tflite-micro)→ 板端跑测速板端时延、内存峰值相对 FP32 提速 2× 以上(实测为准)
⑥ (选做)QAT若 PTQ 掉点超标:prepare_qat → 小学习率微调 2~3 epoch → convert与 PTQ 对比精度恢复到 −0.2% 内
# 实验主循环:三个模型同一套测试集,输出对比表
import os
def evaluate(model, loader):
    model.eval(); correct = total = 0
    with torch.inference_mode():
        for x, y in loader:
            correct += (model(x).argmax(1) == y).sum().item()
            total   += y.size(0)
    return correct / total

def size_mb(model):
    torch.save(model.state_dict(), "_tmp.p")
    s = os.path.getsize("_tmp.p") / 1e6; os.remove("_tmp.p"); return s

for name, m in [("FP32", model_fp32), ("Dynamic", model_dyn),
                ("PTQ-INT8", model_int8)]:
    print(f"{name:10s} acc={evaluate(m, test_loader):.4f}  "
          f"size={size_mb(m):.3f}MB")   # 期待:精度微降,大小降到约 1/4
🎤 面试怎么问:"说说 PyTorch 里 PTQ 的五步流程" —— fuse(可选)→ prepare_fx(插观测器/伪量化)→ 喂校准数据前向 → convert_fx(统计值落地为 scale)→ export;再加一句"qnnpack/x86 后端要与目标硬件一致,否则数值和性能都对不上"。
⚠️ 易错点:①量化前必须 model.eval() 且模型在 CPU(BNB 后端限制),带 BN 的模型先 fuse 才能贴近真实部署;②校准的 torch.randn 要换成真实测试图像(同归一化),随机噪声校准出的 scale 是错的;③导出 INT8 ONNX 后,接收方引擎的量化算子格式(Q/DQ 节点 vs 量化参数表)要对上,否则会静默按 FP32 跑。
✅ 通关建议:本页的产出物就是那张你自己填出来的"LeNet 量化对比表"(精度/大小/时延 × FP32/动态/静态 INT8 三列)。表填不出来,说明流水线没跑通;表填出来了,你就具备了 NPU 团队实习生级别的量化实操能力。下一步把测试集换成自己摄像头拍的图,体验"分布漂移导致掉点"的真实感。

5.3 NPU 工程师视角:量化如何反向塑造 RTL 设计

是什么:量化不只是算法侧的事 —— 你选定的量化方案,直接决定 NPU 数据通路的每一根线。为什么:算法侧的"精度承诺"要靠硬件侧的"位宽预算"兑现,两端必须共同设计(衔接 08-07 脉动阵列与本板块后续 MAC 阵列 RTL 页 08-11)。

ACC_{bits} = 8 + 8 + \lceil \log_2 K \rceil \;\le\; 32 \qquad q_{out} = \mathrm{saturate}_{int8}\!\left(\dfrac{acc \times M + 2^{shift-1}}{2^{shift}}\right)
🎤 面试高频双连(算法 × 硬件各答一半):"量化为什么掉精度?" —— 表示精度从连续降到 256 级台阶(舍入),异常值撑大 scale(截断),误差逐层累积(首层末层敏感);"per-channel 为什么好?" —— 各通道独立定标吃满整数刻度,硬件代价只是每通道一组 multiplier/shift 寄存器,收益/代价比极高。能从算法和 RTL 两侧同时作答的候选人,在 NPU 团队面试里非常稀缺。
⚠️ 易错点:①别把累加器做成 INT16 "省面积" —— 20 bit 的真实需求摆在那里,溢出在验证后期才暴露是灾难;②per-channel 的 (M, shift) 切换时机在输出通道粒度,流水线里要用旁路寄存器预取下一组参数,否则每通道都要插入气泡;③硬件饱和逻辑要在中间累加阶段就位,不是只在最终输出端做一次。

6 高频疑问 FAQ 与术语速查

本节收录量化/部署学习中最常被问到的"边缘问题",以及大模型时代的新概念与本页 CNN 量化的关系,建议通读一遍再进入自测。

量化参数(scale/zero_point)最终存在哪里?硬件怎么拿到?
存在模型文件里:ONNX 里是 Q/DQ 节点携带的量化参数,TFLite flatbuffer 里是每个张量的 quantization 元数据,NCNN/MNN 转换后写进各自的权重文件;推理引擎加载时读出并交给运行时,NPU 上则由编译器把 per-channel 的 (multiplier, shift) 编译进指令流或常量表。所以"量化方案"从来不是只属于算法或只属于硬件 —— 它是双方共同遵守的数据契约。
为什么不能直接在 INT8 上训练(原生低比特训练)?
整数没有连续梯度、round 不可导,反向传播在数学上无法直接进行;现有方案要么像 QAT 一样"浮点训练 + 伪量化模拟",要么用随机舍入、低精度梯度累加等技巧逼近(如 8-bit 训练、二值网络等研究方向)。端侧工程界的共识:训练归浮点世界,部署归整数世界,QAT 是两者之间的桥 —— 这也是为什么量化参数必须作为"契约"从训练侧一路传递到硬件侧。
INT4、FP8 又是什么?和 INT8 什么关系?
INT4把整数刻度压到 16 级,模型再小一半以上,常见做法是"权重 INT4 + 激活保持 FP16"(权重-only 量化),靠 per-channel / 分组(group-wise)量化兜住精度;FP8(E4M3/E5M2 两种格式)是新一代 GPU 的 8 位浮点类型 —— 位宽与 INT8 相同但保留指数位,动态范围更大,主要服务大模型训练/推理。一句话:INT8 是 NPU 端 CNN 部署主力,INT4/FP8 是大模型时代向更低位宽的自然延伸。
大模型量化常听到的 GPTQ / AWQ / llama.cpp,和本页讲的 PTQ/QAT 是什么关系?
它们都属于训练后量化(PTQ)大家族,只是"怎么选量化参数"更聪明:GPTQ 用二阶信息逐层最小化量化误差;AWQ 依据激活统计保护少数"重要权重通道";llama.cpp 的 GGUF 用混合位宽(不同层不同 bit)。与本页 CNN 全整型方案的最大差异:LLM 量化通常只量权重、激活保持高精度,因为 Transformer 激活分布有大量离群值,全整型极难。原理根基( scale、round、clip )完全一致。
量化后的模型还能继续训练吗?
真整型模型不能训练:整数没有梯度,round 不可导。QAT 的本质是在浮点副本上插入伪量化节点"模拟"量化效果做训练,训练完成后 convert 成真整型模型用于部署。记住分工:训练在浮点世界,推理在整数世界,QAT 是两个世界之间的"预演"。
为什么激活不做 per-channel 量化?
权重的取值范围是静态的(部署前就定死),per-channel 只是给每个输出通道存一组常量 (multiplier, shift),寄存器堆就能放下;激活的范围随输入变化,逐通道 scale 意味着推理时逐通道在线统计与存储,硬件代价和调度复杂度剧增。所以业界默认:权重 per-channel + 激活 per-tensor。
校准数据可以用随机噪声凑数吗?为什么强调"与实机同分布"?
不能。校准的全部产出就是每层激活的 min/max(或分布直方图),scale 由分布决定 —— 拿噪声校准等于给一个不存在的输入分布定标,实机数据一进来大量值被 clip,精度崩掉。正确做法:从实机传感器采集几百张覆盖典型工况(不同光照/姿态/场景)的数据做校准。
tflite-micro 的 tensor arena 一直分配失败,怎么排查?
三步:① 打开框架的内存报告(记录每算子 arena 占用),看峰值出现在哪一层;② 常规手段:输入分辨率降一档、深度可分离卷积替换标准卷积、确认全模型 INT8(而不是 int16);③ arena 大小二分逼近:先给大跑通,再逐步缩小到刚好通过。注意 arena 除模型激活外还要留算子临时缓冲,别按"激活总量"贴地估算。
量化、剪枝、蒸馏怎么组合成完整压缩流水线?
典型顺序:蒸馏(大模型教小模型)→ 剪枝(2:4 或通道级)→ 微调恢复 → 量化(PTQ/QAT)→ 导出部署。原则:越靠近训练的变换先做(蒸馏/剪枝都在训练期),量化放最后收尾;每两步之间留出恢复期,每步都和 FP32 基线对拍。切忌三件套一步到位同时做。
PTQ Dynamic 和 PTQ Static 有什么区别?(面试常问)
PTQ Dynamic(动态量化)只提前量化权重,激活在推理时按每个 tensor 的实际范围在线算 scale —— 零校准、精度好,但"在线算 scale"本身有开销,且多数 NPU 不支持动态激活量化,主要用在 CPU 上加速 Linear/LSTM。PTQ Static(静态量化)权重和激活都提前定死 scale(靠校准数据),是 NPU/端侧部署的标准形态 —— 硬件全程纯整数运算。记忆:动态省校准、静态上板子。
什么是混合精度量化?什么时候用它?
混合精度量化 = 不同层用不同位宽:对误差敏感的少数层(首层、末层、深度可分离卷积通道数少的层)保持 FP16/INT16,其余层 INT8,在精度与收益间逐层做权衡。典型工作流:先全模型 INT8 → 逐层对拍找"最伤精度的层" → 只把这几层升位宽 → 复测。它也是排查掉点的诊断工具:如果"只保护首末层"就能恢复大部分精度,说明误差确实集中在两端。
机器人端侧推理为什么一般 batch=1?这和量化部署有什么关系?
机器人感知是流式任务:相机一帧来一帧,推理必须跟着帧率走,攒不满 batch;而且 batch 越大时延越高,控制闭环等不起。这决定了端侧优化方向与服务器不同:不追求吞吐,追求单帧时延与确定性 —— 所以 kernel 要针对 batch=1 手工优化(如 ncnn 的做法),量化收益也要按"单帧省多少毫秒"来评估,而不是服务器那边"每秒多处理多少张图"。

术语速查表(随用随查)

术语一句话解释
scale / zero_point量化的两个参数:s 是一个整数台阶代表的实数步长,z 记录实数 0 落在哪个整数上
对称 / 非对称量化对称:z=0、区间取 ±max(权重常用);非对称:z≠0、区间任取(激活常用)
per-tensor / per-channel一组 (s,z) 作用于整个张量 / 每个输出通道各一组(s,z)
PTQPost-Training Quantization,训练后量化:喂校准数据统计分布,不训练
校准(Calibration)PTQ 中用代表性数据统计每层激活的 min/max 或分布,以确定 scale
KL / 熵校准枚举截断点选量化前后分布 KL 散度最小者,TensorRT 经典方法
QATQuantization-Aware Training,训练中插入伪量化节点让网络适应量化噪声
伪量化(fake quant)前向时"量化→反量化"模拟整数台阶,权重仍存 FP32
STE直通估计器:round 梯度近似为 1,让反向传播穿过伪量化节点
2:4 稀疏每 4 个连续权重至少 2 个为 0,Ampere Sparse Tensor Core 原生 2× 加速
算子融合conv+BN+ReLU 等合成一个算子,BN 折叠进权重,消灭中间张量访存
tensor arenaTFLM 的静态内存池:编译期规划所有张量、生命周期复用,零 malloc
requantizeINT32 累加结果回到 INT8 的过程:乘定点 multiplier、移位、饱和
对拍(bit-exact / 余弦相似度)量化模型与 FP32 模型同输入比对输出,验证数值正确性的手段
opset / Q-DQ 节点ONNX 算子集版本;QuantizeLinear/DequantizeLinear 节点是 ONNX 里表达量化的标准方式
混合精度量化敏感层 FP16/INT16、其余层 INT8,逐层权衡精度与收益
ARM NEON / Helium(MVE)Arm 的 SIMD 向量扩展:NEON 服务 Cortex-A,MVE 服务 Cortex-M55/M85,是移动端/MCU 算子库提速的底层依赖
权重-only 量化只量化权重、激活保持高精度的方案(INT4 权重 + FP16 激活),大模型量化主流形态
动态量化(PTQ-Dynamic)只量权重、激活推理时在线算 scale:零校准,适合 CPU 加速 Linear/LSTM,NPU 基本不支持
静态量化(PTQ-Static)权重与激活的 scale 都靠校准提前定死:NPU/端侧部署标准形态,硬件全程纯整数
🔗 板块内衔接:量化方案最终要落到 08-07 NPU 体系结构与脉动阵列 的 MAC 数据流上执行,RTL 落地与累加位宽的工程细节见本板块后续 MAC 阵列实战页;下一步学芯片怎么把这一切造出来 → 08-09 芯片设计流程与 AHB/AXI 总线

7 精选资源与延伸阅读

以下 12 条资源均已于 2026-09 逐一核实可访问(论文/官方文档/官方仓库/官方博客/可播放视频),按"理论 → 工具 → 中文社区"排序,⭐ 为强烈推荐。

📄
⭐ Google INT8 白皮书:Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference
量化领域奠基论文(Jacob et al., 2017):提出全整型 INT8 量化方案与量化感知训练(QAT),TFLite 的 scale/zero_point 设计即源于此。本页 2.2/3.2 节的理论源头。
arXiv 论文⭐ 必读进阶
📘
⭐ PyTorch 官方量化文档(torch.ao.quantization)
动态量化 / FX 静态 PTQ / QAT 三条路径的官方权威文档(新版本正向 torchao/PT2E 迁移)。本页 5.2 节代码的直接依据,动手前先通读一遍。
官方文档⭐ 配套代码中级
📄
⭐ NVIDIA 官方博客:Accelerating Inference with Sparsity Using the NVIDIA Ampere and TensorRT
2:4 结构化稀疏的官方定义与实测:A100 Sparse Tensor Core 理论吞吐 2×(INT8 1248 TOPS),TensorRT 实测 ResNeXt-101 性能 +20%、性能/功耗比 +36% 且精度无损。本页 4.1 节数据出处。
NVIDIA 官方⭐ 2:4 权威进阶
🔧
⭐ Tencent ncnn:手机端极致优化推理框架(23k+ Star)
纯 C++ 零第三方依赖,ARM NEON 手工优化,支持 INT8 量化与 FP16,走 pnnx/ONNX 从 PyTorch 转入;QQ/微信等腾讯产品生产验证。端侧 CPU 部署首选之一(.param + .bin 双文件格式)。
GitHub 开源⭐ 上手部署中级
🔧
Alibaba MNN:轻量级推理引擎(淘宝/天猫等 30+ App 验证)
CPU/GPU(OpenCL/Vulkan/Metal)/NPU 多后端,支持训练与 MNN-LLM 端侧大模型;Apache 2.0 协议,转换器支持 ONNX/TensorFlow/Caffe/TorchScript 输入。
GitHub 开源中级
🔧
TensorFlow Lite Micro(TFLM):MCU 级推理框架
TFLite 的微控制器移植版,静态内存 tensor arena、零动态分配,支持 Cortex-M/RISC-V/Hexagon/Xtensa;全整型 INT8 量化规格与 CMSIS-NN 位精确对齐。Cortex-M 部署标准答案。
官方开源中级
🔧
Arm CMSIS-NN:Cortex-M 神经网络优化内核库
int8/int16 高效 kernel,自动利用 M4 的 DSP SIMD 与 M55/M85 的 Helium(MVE)指令,目标"最大化性能、最小化内存";与 TFLM 量化规范位精确一致,配合使用。
Arm 官方中级
📘
ONNX 官网:开放神经网络交换格式
框架无关的标准算子集 + 文件格式,是"训练框架 → 端侧引擎"转换链路的枢纽;部署前了解 opset 版本与 Q/DQ 量化算子约定必备。
官方文档入门
🔧
AMD/Xilinx Vitis AI:FPGA/自适应平台 AI 推理开发栈
含 DPU IP、模型 zoo、量化器与编译器,完整走通"量化 → 编译 → DPU 部署";想理解"NPU 工具链长什么样"最好的开源参照(与 08-11 节 RTL 视角互补)。
GitHub 开源进阶
📄
PyTorch 官方博客:Accelerating Neural Network Training with Semi-Structured (2:4) Sparsity
PyTorch 原生 2:4 稀疏支持(to_sparse_semi_structured)官方教程:剪枝到 2:4 掩码、转稀疏格式、加速推理的完整代码路径,配合本页 4.1 节食用。
官方博客进阶
📄
⭐ 知乎·老潘:老潘的 AI 部署以及工业落地学习之路
知乎专栏⭐ 中文实战中级
🎬
⭐ B站·ZOMI酱:训练后量化 PTQ 深度解读!与量化部署核心原理【推理引擎】
模型压缩系列第 4 集(14 分钟):PTQ Dynamic/Static 两条路线、校准原理与量化部署核心逻辑,动画讲解直观,适合配合本页 3.1 节食用;同系列还有剪枝/蒸馏/量化训练各集。
B站视频⭐ 入门视频入门

8 本节自测

1. 模型从 FP32 量化到 INT8 后,关于"四本账"的收益,下列说法正确的是?
💡 每权重 4 字节→1 字节,存储/带宽同步约 4×;访存能耗随数据宽度近似线性下降,所以搬运能耗约 1/4;此外 INT8 乘加逻辑远小于浮点路径,同面积可放数倍 MAC。注意 A 错在忽略了带宽与算力收益,D 错在 NPU 常是访存受限,带宽那本账往往最关键。
2. 非对称量化 q = clip(round(r/s) + z, 0, 255) 中,zero_point z 的作用是?
💡 z = round(−r_min/s),它把实数域的 0 映射到整数域的某个刻度,使 0 被无损表示;若 0 映射有误差,卷积 padding 和 ReLU 的 0 都会被量化成 ±s/2 的偏移并逐层放大。z 与"扩范围""替代 bias"无关。
3. 某模型 per-tensor 权重量化后精度明显下降,改用 per-channel 权重量化后恢复,原因最可能是?
💡 通道间权重范围可能差一个数量级,per-tensor 按最大通道定标会让小范围通道只占 256 级刻度中很窄一段,舍入误差占比升高;per-channel 让每个通道独立吃满 ±127。注意:通常只有权重做 per-channel,激活仍用 per-tensor(C 错),累加器位宽不变(D 错)。
4. 关于 PTQ 的校准方法,下列说法错误的是?
💡 KL/熵校准的目标是"量化后信息损失最小":它会在多个候选截断点里选一个往往小于最大值的点,主动牺牲极端尾部换取主体分布的分辨率 —— 正是它抗异常值的原因,结果通常不等于 Min-Max,D 错误。A/B/C 均为正确描述。
5. QAT 中必须使用直通估计器(STE),是因为?
💡 伪量化前向是 量化→反量化(含 round),round 的导数几乎处处为 0,梯度链在节点处断裂;STE 在反向时把 ∂r̂/∂r 近似为 1,梯度原样回传,训练才能进行。它与学习率、正则化、数值格式转换无关。
6. 关于 2:4 结构化稀疏,下列说法正确的是?
💡 2:4 = 每组 4 个连续值至少 2 个为 0(50% 稀疏度),Ampere Sparse Tensor Core 硬件跳零、成对计算,INT8 吞吐达稠密 2 倍(A100 上 1248 TOPS);NVIDIA 三步法要求剪枝后微调恢复精度(D 错),且加速依赖专用硬件支持(A/C 错)。
7. 端侧部署时,模型在引擎上"能跑通但时延比预期慢好几倍",最常见的原因是?
💡 转换后必须核对算子支持度表/引擎日志,确认每个算子落在哪个设备上:不支持的自定义或冷门算子会静默回退 CPU,精度不坏但时延爆炸。这是部署实战(5.1 节)的第一大坑,配合 Netron 查图 + 引擎 verbose 日志定位。
📌 本节小结:①量化是端侧部署第一课:INT8 相对 FP32 存储约 4× 缩减、带宽约 4×、访存能耗约 1/4,且 NPU 的 INT8 算力面积效率数倍于浮点;②量化数学只靠两个参数 —— scale 定步长、zero_point 定零点,权重常用对称 + per-channel,激活常用非对称 + per-tensor;③两条量化路线:PTQ(校准数据统计分布,min-max/KL/百分位三法)先跑,QAT(伪量化 + STE 微调)救场;④误差来自舍入、截断与逐层累积,首层末层最敏感,per-channel 与混合精度是第一救命稻草;⑤稀疏化与量化正交:2:4 结构化稀疏有 Ampere Sparse Tensor Core 硬件跳零 2× 加速,先剪后量收益叠加;⑥推理引擎靠图优化(算子融合省访存)、tensor arena(静态内存规划)与算子调度赚钱;⑦量化反向塑造 NPU RTL:INT8 MAC、INT32 累加器 + ⌈log2K⌉、饱和与 per-channel multiplier/shift,算法与硬件必须共同设计。
🤔 思考题: 1. 你的机器人头部 SoC 的 NPU 是 6 TOPS INT8:如果模型坚持用 FP32,等效算力大约还剩多少?这对你同时跑"检测 + VSLAM + 语音"三路感知意味着什么?
2. 一个检测模型 PTQ 后 mAP 从 0.512 掉到 0.465:请给出你的排查顺序(逐层对拍?校准数据分布?首末层混合精度?per-channel?),并说明每一步在验证什么假设。
3. 如果让你为 08-11 节的 NPU MAC 阵列写 RTL:量化方案选 per-channel INT8 后,你的数据通路需要额外增加哪些部件?位宽账怎么算?
4. 2:4 剪枝 + INT8 量化的模型,权重存储理论上压缩到 FP32 基线的几分之一?为什么实际文件往往达不到这个数(提示:想想哪些部分没法稀疏化、索引元数据占多少)?

9 参考来源

以下链接均于 2026-09 逐一核实可访问。理论以 Google INT8 白皮书与 PyTorch 官方文档为纲,工具以官方仓库为准,中文社区选高赞实战文/视频。