把一个训练好的模型塞进手机、摄像头或者一块边缘板卡里最先撞上的往往不是算法问题而是内存和带宽。我遇到过一次很典型的场面一个视觉模型在服务器上跑得好好的单卡推理延迟低得几乎看不见结果换到一台内存只有几百兆的嵌入式设备上权重文件刚加载完运行时就报内存不足。模型本身没有变输入尺寸也没有变唯一变的是——我们把它从 32 位浮点换成了 8 位整数而那一刻我才真正意识到量化这件事不是压缩一下文件这么简单它是在重新定义整个计算过程里的数值表示方式。这篇文章想讲清楚的就是什么是量化。不是那种一句话定义——把浮点转成定点——这种说法对新手几乎没有任何帮助。我会从一个从业者的角度把这件事故意拆得碎一点量化在数轴上到底做了什么动作、省下来的空间和算力从哪里来、精度又是从哪个缝隙里漏出去的、训练后量化和量化感知训练该怎么选、以及真正落地时那些文档上一个字都不会写的坑。适合刚接触模型部署的读者打底子也适合已经在做推理优化、但一直靠试参数过日子的同行补一补底层直觉。1. 量化到底把什么变小了从一次显存爆掉的部署说起1.1 一张特征图带来的疑问很多人第一次接触量化是从模型文件从 100MB 变成 25MB这个现象开始的于是很容易形成一个误解量化就是压缩权重。但只要你自己动手跑一遍完整的推理链路就会发现权重只是整条流水线上的一小段。以一个典型的卷积网络为例假设某一层输入是 1×64×224×224 的特征图输出通道 128卷积核 3×3。这一层的权重参数数量是 128×64×3×3 约 7.4 万个按 FP32 算大概 295KB而它输出的特征图有 128×224×224 约 642 万个元素按 FP32 算是 25MB 左右。权重比激活小了将近两个数量级。这就解释了一个很常见的疑惑为什么有些模型权重压到了 4 位推理速度却没有明显提升甚至反而变慢了。因为推理过程中的访存大头往往在激活值上权重压得再狠中间结果还是 32 位浮点内存带宽该占还是占。量化的收益必须从权重 激活 中间缓存三个方向同时去看只盯着权重文件大小很容易得出错误结论。我在早期做移动端部署时就吃过这个亏看着权重压缩比很漂亮端到端延迟只降了百分之十几后来把激活也一起量化才把延迟真正压下来。1.2 用一组数字把账算清楚把账算细一点会更直观。一个 FP32 数值占 4 字节INT8 占 1 字节理论上的存储压缩比是 4 倍。但真正的收益不止于此因为现代处理器在整数运算上的吞吐通常远高于浮点尤其是在没有独立浮点单元的嵌入式芯片上。一颗典型的边缘 SoCINT8 的峰值算力可能是 FP32 的 4 到 8 倍这个倍数是硬件设计决定的不是软件优化能追上的。还有一个容易被忽略的点是带宽。推理过程本质上是把数据搬进计算单元算完再搬出去很多层是访存受限而不是计算受限。数据宽度从 32 位降到 8 位同样一条内存总线在单位时间内能搬运的元素数量直接翻四倍这对访存受限的层比如逐点卷积、归一化层提升非常显著。所以量化的收益可以拆成三块存储占用下降、计算吞吐上升、访存压力缓解。三者叠加才是我们在端侧感受到的那个变快了很多。不过收益永远伴随代价。浮点数能表示的动态范围极大一个 FP32 最小能到 10 的负 38 次方量级最大能到 10 的 38 次方量级中间还有二十多位有效数字而 INT8 只有 256 个离散取值。把连续分布塞进 256 个格子里必然丢信息。量化的全部技术难点本质上就是如何用 256 个格子尽可能逼近原来的分布。1.3 权重量化、激活量化与缓存量化是三件不同的事我在跟新人沟通时发现最大的概念混乱来自把所有量化混为一谈。实际上按照被量化的对象不同至少要分成三类它们的难度和策略差异很大。权重量化最简单。权重在训练结束后就固定了分布是静态的我们可以慢慢统计每一层的取值范围甚至可以在离线阶段做逐层的误差补偿。这一类量化已经很成熟把权重压到 8 位甚至 4 位精度损失通常可以控制在很小的范围内。激活量化难得多。激活值是输入数据的函数会随着输入内容变化一张明亮的图和一张昏暗的图同一层的激活分布可能完全不同。而且激活是运行时产生的没有离线慢慢算的机会只能靠一小批校准数据去估计范围。估计得保守浪费量化精度估计得激进一旦遇到超范围的值就会溢出截断直接毁掉这一层的输出。缓存量化是近几年随着长上下文场景火起来的。自回归生成时每一步都要把历史的键值对缓存下来供后续使用序列一长这块缓存的开销会超过模型权重本身。把缓存压到 8 位甚至更低是长文本推理的刚需。但它的难点在于缓存是逐步写入的分布随时间漂移而且早期的量化误差会一直留在缓存里被后续所有步骤反复读取。三类对象三种分布特性三套处理策略。搞清楚这一点后面所有的技术细节才有落脚点。2. 浮点映射到整数量化公式里的两个关键参数2.1 仿射量化的完整推导量化的核心动作只有一个建立一个从实数域到整数域的映射。最通用的形式是仿射映射它有两个参数——缩放因子 S 和零点 Z。整数表示与实数表示之间的关系可以写成r S × (q − Z)其中 r 是原始的浮点值q 是量化后的整数。反过来把一个浮点数变成整数就是q clamp(round(r / S) Z, q_min, q_max)这个 clamp 是必须的因为超出整数表示范围的值必须被截断到边界否则会发生数值溢出结果可能从正的最大值直接翻转成负的最大值这种错误比精度损失可怕得多。那么 S 和 Z 怎么确定假设我们统计出某一层某个张量的最小值是 r_min最大值是 r_max目标是映射到 [q_min, q_max] 这个整数区间INT8 对称量化常用 [-127, 127]非对称常用 [-128, 127]。缩放因子就是两段区间的长度之比S (r_max − r_min) / (q_max − q_min)零点则是为了让 r_min 精确落在 q_min 上Z round(q_min − r_min / S)注意 Z 必须取整因为它本身也要以整数形式存储。这个取整会带来一点点偏移但影响很小。用一段最朴素的代码把整个过程写出来比看公式更清楚def calc_params(r_min, r_max, q_min-128, q_max127): # 防止整个张量取值相同导致除零 if r_max r_min: r_max r_min 1e-8 scale (r_max - r_min) / (q_max - q_min) zero_point round(q_min - r_min / scale) zero_point max(q_min, min(q_max, zero_point)) return scale, zero_point def quantize(x, scale, zero_point, q_min-128, q_max127): q round(x / scale) zero_point return max(q_min, min(q_max, q)) def dequantize(q, scale, zero_point): return scale * (q - zero_point)这段代码没有任何框架依赖纯粹是把数学式子翻译成可运行的逻辑。你可以拿一个实际的张量手动跑一遍看着浮点数被压成整数再还原回来那个误差会变得非常具体。2.2 零点存在的意义零点这个参数经常被新手当成可有可无的东西尤其是看到对称量化里 Z 恒等于 0 之后更容易觉得它多余。但它在非对称场景下承担着一个非常实际的职责保证实数 0 能被精确表示。为什么 0 这么重要因为在神经网络里0 有特殊语义。填充padding用的是 0ReLU 的输出下界是 0稀疏化后的权重里大量的 0。如果 0 不能被精确表示那么卷积在边界补零的位置就会引入额外的偏移误差ReLU 之后所有本该严格为零的位置也会变成一个小正数这些误差会一层层累积。对称量化强制 Z 0好处是计算时可以省掉零点校正这一步整数矩阵乘法做完直接乘缩放因子还原即可实现简单、速度快。代价是当数据分布明显不对称时比如 ReLU 之后的激活全部非负范围是 [0, 6]对称量化会把 [-6, 0] 这段根本用不到的范围也留着等于把 256 个格子的一半浪费在空区间上有效精度直接减半。非对称量化能贴合这种偏斜分布代价是每次计算都要处理零点卷积里的零点校正会带来额外开销。这里的选择逻辑很朴素权重通常围绕 0 对称分布用对称量化激活经过 ReLU 之后严重偏斜用非对称量化。这也是绝大多数量化方案默认的组合方式。当然如果你的硬件对非对称支持不好也可以强行把激活改成对称量化代价是需要重新校准并接受一部分精度损失。2.3 粒度选择per-tensor、per-channel 与 per-group确定了映射公式下一个问题是S 和 Z 到底按什么粒度来统计这个问题对精度的影响往往比选对称还是非对称更大。per-tensor是整个张量共享一组参数。实现最简单硬件最友好但只要张量内部各部分的数值范围差异大小范围的那部分就会被挤到很少的几个格子里几乎等于被抹平。这种情况在卷积权重里非常常见不同输出通道的权重量级可能差一个数量级。per-channel是每个输出通道各有一组参数。它精准解决了通道间量级不一致的问题代价是需要存储更多参数且在硬件上做向量化计算时要处理多组缩放因子。这是目前权重量化的主流做法几乎所有成熟的量化工具链都支持。per-group是折中方案把通道再切成若干组每组共享一组参数。在低比特场景比如 4 位、3 位下几乎是标配因为比特数越低格子越少越需要更细的粒度来保住有效精度。常见的组大小是 32、64、128组越小精度越高但元数据的开销也越大。粒度参数开销精度表现硬件友好度典型使用场景per-tensor极低一般最高激活量化、对精度要求不高的场景per-channel中等较好较高8 位权重量化的默认选择per-group较高最好中等4 位及以下的低比特权重量化这里要提醒一个实操细节粒度的收益存在明显的边际递减。从 per-tensor 换到 per-channel精度提升通常很显著从 per-channel 换到 per-group组大小 128在 8 位下提升可能只有零点几个百分点但推理引擎的适配成本会上升。我一般建议 8 位场景老老实实用 per-channel只有在比特数压到 4 位以下时才值得为 per-group 付出额外的工程代价。3. 精度为什么会掉三类典型的误差来源3.1 离群值把整层的刻度撑坏量化误差最凶的来源不是格子太少而是格子被浪费了。假设某一层的激活值绝大多数集中在 [-1, 1] 之间但偶尔冒出几个 40 左右的极端值。按 MinMax 策略统计范围S 会被这个 40 撑大原本 [-1, 1] 这个真正承载信息的区间只能分到 256 个格子里的寥寥几个。信息密度骤降这一层的输出基本就废了。这类离群值在 Transformer 架构里尤其普遍某些注意力头的激活会出现极端的尖峰而且这些尖峰位置在不同输入下还不固定。这就导致了很矛盾的局面为了不溢出必须把范围开大范围一开大有效精度就没了。应对思路大致有几类。一类是截断不追求覆盖全部取值主动放弃极少数离群点用一个分位数比如 99.9%代替最大值。这样绝大部分数据获得了更细的刻度被截断的少数点产生误差但总体误差反而下降。另一类是平滑在量化前把激活的尖峰按通道摊平到权重上让两边的分布都变得温和一些量化完再把补偿还原回去。还有一类是保留高精度把这些离群值所在的位置单独用高比特表示其他位置正常量化。需要强调的是这几类方法都不是免费的。截断需要调分位数阈值调高了没用调低了掉点平滑会改变计算图增加一次逐通道缩放的开销混合精度会让实现复杂度和内核适配难度上升。选哪种取决于你的精度预算和工程预算。3.2 舍入误差沿着网络层层放大除了范围估计的问题还有一个更隐蔽的误差源舍入。把浮点数舍入到最近的整数每次都会引入最多半个格子的误差。单次误差很小但网络有几十上百层每一层的输入都带着上一层传下来的误差而误差会通过权重矩阵被重新组合、放大。更麻烦的是舍入的方式。标准的四舍六入五取偶是无偏的误差期望为零但如果为了硬件实现的简单性采用直接截断向下取整误差就会系统性地偏负几十层累积下来会形成明显的偏移。我在调试一个深层次序模型时就遇到过这种情况单层误差看起来完全可以接受但把十层拼起来一跑输出整体出现了一个肉眼可见的偏移改成对称舍入之后立刻恢复正常。还有一个容易被忽略的地方是累加器。即使输入是 INT8卷积和矩阵乘的中间累加结果通常要用 INT32 保存。因为一次矩阵乘要累加几百上千项每项都是 INT8 的乘积最大约 16129累加几百项就会超过 INT16 的范围。如果你的实现里累加器位宽不够溢出会让结果彻底崩溃。这一点在自研内核时尤其要注意很多推理框架的文档里会明确写着累加位宽但没人提醒你它为什么必须是 32 位。3.3 激活分布随着输入漂移第三类误差来源是分布漂移它只在激活量化时出现但杀伤力很大。校准阶段我们拿一小批数据统计出的范围只代表那批数据的分布。真实线上输入千变万化一旦出现校准集里没见过的数据形态激活可能整体上移或者展宽超出原先估计的范围触发截断。这个问题在视觉任务里表现为某些光照条件下的图片效果明显变差在文本任务里表现为某些领域的长尾输入输出质量下降。它的隐蔽之处在于离线评估时用的测试集往往和校准集同分布指标看起来很漂亮上线之后才发现问题。缓解手段有几种。一是校准集要覆盖多样性别只拿一类样本凑数宁可少一点但要有代表性。二是在推理时做动态范围统计也就是所谓的动态量化每次前向传播时根据当前输入实时计算范围。它的代价是要多一次统计和参数计算但对付分布漂移非常有效在参数量不大的层上开销可以接受。三是在网络设计阶段就做约束比如用归一化层把激活分布控制在一个稳定的区间里这样量化范围就不容易失控。4. 训练后量化与量化感知训练路线选择的判断依据4.1 PTQ 的校准环节才是成败关键训练后量化PTQ的思路很直观模型已经训练好了权重不动只需要统计一下各层的数值范围算出一组缩放参数然后直接换成整数推理。它最大的优势是成本低不需要重新训练不需要原始训练数据只需要一小批校准样本几十分钟就能跑完。但只需要校准这个说法容易误导人。PTQ 的效果高度依赖校准这一步而校准里的门道比想象中多。首先是校准集的规模太少比如几十张图统计出来的范围不稳定太多又会拖长流程实践里通常取几百到一千个样本覆盖主要的数据形态。其次是校准算法MinMax 最简单但对离群值毫无抵抗力百分位法要调阈值基于信息熵的方法会搜索一个让量化前后分布最接近的截断点效果通常更好但计算量大一些。我用下来最稳的一套流程是先用几百个样本做 MinMax 拿到基线跑一遍逐层误差分析找出误差最大的那几层再针对这几层换熵校准或者百分位校准最后用一批完全独立的验证数据确认。这个过程听起来繁琐但它比全模型统一用熵校准要可靠得多因为不同层的最优策略本来就不同。还有一个细节校准数据必须走完整的预处理链路。我见过有人直接拿原始图片去校准跳过了归一化导致统计出来的激活范围和实际推理时完全不是一个量级量化参数全错最后归咎于PTQ 不好用。这类问题排查起来极其痛苦因为每一步单看都对只有拼起来才错。4.2 QAT 的伪量化与直通估计当 PTQ 怎么调都掉点的时候就轮到量化感知训练QAT出场了。它的核心思想是既然量化会引入误差那就让模型在训练阶段就体验这种误差学会在误差存在的情况下依然给出正确结果。具体做法是在前向传播里插入伪量化节点——把浮点数先按量化公式压成整数再还原成浮点数。数据形式上还是浮点但数值已经被限制在量化格点上模型因此能感受到量化带来的失真。反向传播时麻烦来了量化里的 round 和 clamp 操作的导数几乎处处为零梯度传不回去整个网络就没法训练。解决方案是直通估计器STE思路非常朴素反向传播时假装量化这个操作不存在直接把上游梯度原样传给下游。数学上这显然不是真正的导数但实践中效果出奇地好。可以这么理解——我们不告诉模型量化这一步该怎么调而是告诉它你就当量化没发生按原来的方向优化但每次更新后我帮你把参数重新对齐到格点上。这种近似在量化噪声不大的时候是合理的梯度方向基本正确只是幅度可能有偏。QAT 的成本主要在训练资源上。通常需要原始训练数据或者同分布的数据需要几个 epoch 的微调还需要调整学习率——一般要比正常训练小一到两个数量级因为模型已经收敛了大学习率会把预训练学到的特征直接冲掉。我见过有人用原训练的学习率跑 QAT结果精度比 PTQ 还差原因就是这个。4.3 什么情况下必须上 QAT不是所有场景都值得上 QAT判断标准可以归成几条。如果目标是 8 位量化模型是常规的卷积网络或成熟的 TransformerPTQ 通常足够掉点能控制在 1% 以内没必要为此付出训练成本。如果目标压到 4 位甚至更低PTQ 基本撑不住这时候 QAT 几乎是唯一选择因为低比特下量化格点太稀疏靠后处理调参数已经无能为力。如果模型里有大量敏感结构比如检测头、分割头、小目标相关的浅层特征这些地方对数值扰动非常敏感PTQ 往往会在这些位置掉点明显QAT 能让模型主动适应。如果业务对精度几乎是零容忍比如某些测量类任务那 QAT 也是更稳妥的路线。对比维度训练后量化 PTQ量化感知训练 QAT所需数据少量校准样本完整训练数据或同分布数据时间成本分钟到小时级小时到天级典型比特8 位为主8 位、4 位甚至更低精度损失通常 1% 以内可控制在 0.5% 以内工程复杂度低流程标准化高需要改训练代码适用场景快速验证、成熟架构低比特、敏感任务我的实际建议是先花半天时间把 PTQ 跑通拿到一个基线数字。如果这个数字能满足业务要求就直接收工别为了追求极致去上 QAT。如果差得不多比如差一到两个点再考虑优化校准策略。只有当 PTQ 优化到头仍然不达标才启动 QAT。这个顺序能省下大量时间。5. 一次 INT8 量化的完整落地链路5.1 动手前的四项检查很多人一上来就开始调量化参数其实有几项前置检查没做后面全是白费功夫。第一确认硬件的整数算力真的被用上了。有些推理后端在遇到不支持的算子时会自动回退到浮点实现结果就是模型量化了但大部分计算还在跑浮点甚至因为多了量化/反量化的转换开销而变慢。检查方法很简单看推理时的算子分布确认整数算子占比。如果回退比例很高先解决算子支持问题再谈精度。第二确认输入输出的处理链路。模型的输入通常需要量化输出需要反量化。这两处的缩放参数如果和前后处理对不上会导致结果整体偏移。特别是输出端如果下游逻辑默认拿到的是浮点而实际拿到的是整数可能出现非常诡异的错误。第三确认模型的动态shape支持情况。如果业务需要变长输入量化参数的统计方式要根据最大范围来做或者在运行时动态计算。固定shape的量化模型直接喂变长输入会直接崩。第四备份一份浮点基线。这个听起来废话但真的有人量化完之后找不到原始模型了。更重要的是你需要一个可靠的对照基准包括精度指标、逐层输出、延迟数据后面每一步优化都要和它比。5.2 逐层敏感度分析怎么做敏感度分析是整个量化流程里性价比最高的一步但很多人跳过它直接全模型量化然后在掉点之后盲目调参。做法是每次只把一层或一个模块换成量化版本其余保持浮点跑一遍验证集记录指标变化。把所有层的敏感度排序就能清楚看到哪些层碰不得。实际操作中不需要真的逐层切换很多工具链支持插入量化观察点能直接输出每层的量化误差统计效果类似。拿到敏感度排序后策略就清晰了最敏感的几层保留浮点或用更高比特其余层放心量化。经验上网络的第一层直接接触原始输入和最后一层直接决定输出通常最敏感中间的深层反而耐受度更好。残差相加的位置、注意力里的 softmax 前后、以及归一化层附近也经常是敏感区。这里有个反直觉的观察参数量大的层不一定敏感参数量小的层不一定安全。我遇到过某个只有几百个参数的缩放层量化之后指标掉了两个点因为它的输出会被广播到所有通道上误差被成倍放大。所以敏感度必须实测不能靠参数量猜。5.3 混合精度与回退策略敏感度分析的直接产出就是混合精度方案大部分层用 INT8少数敏感层保留 FP16 或 FP32。这个方案在精度上很有效但要考虑两个代价。一是性能代价。混合精度意味着计算图里有类型转换节点每次从 INT8 切到 FP16 再切回来都要做一次数据搬移和格式转换。如果切换太频繁这些转换的开销可能吃掉量化带来的收益。所以我一般会把敏感层尽量集中让图里只有少数几个浮点孤岛而不是到处穿插。二是工程代价。不同推理后端对混合精度的支持程度差别很大有些后端只支持整图统一精度那就只能退而求其次——把敏感层替换成计算量更小的等价结构或者干脆接受这部分精度损失。除了混合精度还有一种更彻底的回退策略只量化权重激活保持浮点。这种做法在访存受限的场景下仍然能拿到可观的加速因为权重的读取量大大减少同时避开了激活量化最难的分布漂移问题。当激活量化怎么调都不理想时这是个很实用的保底方案。5.4 验收标准不能只看准确率量化做完了怎么判断成功只看整体准确率是不够的我通常会看四组数据。整体指标当然是第一位的分类看 top-1/top-5检测看 mAP生成任务看人工评估或者自动指标。但要注意对比的基准必须是同一套测试流程别拿量化模型的测试集结果和浮点模型的验证集结果比。分层指标更重要。整体指标掉 0.5 个点可能看起来没事但如果某一类别的指标掉了 10 个点只是被其他类别的提升掩盖了这在业务上可能是灾难。所以按类别、按场景切片看指标是必须的。最坏情况指标。量化误差在极端输入下会被放大所以要专门构造一批困难样本——过曝的、过暗的、模糊的、长尾的——看量化模型在这些样本上的表现。浮点模型和最坏情况的差距往往就是量化模型翻车的地方。数值稳定性。跑一批数据统计输出里有没有 NaN、有没有异常大的值、有没有全零输出。这些问题在常规指标上可能看不出来但会在线上偶发。我遇到过一次量化模型在某个特定输入下输出全是边界值原因是某一层的激活整体超出范围被全部截断这类问题必须靠专门的数值检查才能发现。6. 文档里不会写的几个坑6.1 首尾两层与残差相加处第一层和最后一层尽量别量化这个结论很多人知道但知道原因的人不多。第一层直接接收原始输入输入的分布范围由业务数据决定方差可能非常大且不稳定量化范围很难估准同时第一层的通道数通常很少RGB 只有 3 通道per-channel 量化的收益有限。最后一层直接产生输出任何误差都没有后续层来消化会原封不动地体现在结果上。残差相加是另一个高危位置。两个分支的输出相加如果它们的量化尺度不一致相加之前必须先对齐到同一尺度这个对齐过程会引入额外误差。更麻烦的是残差结构会把误差直接累加到主路径上深层网络里堆叠几十个残差块误差累积效应很明显。稳妥的做法是在相加之前保持较高精度相加之后再统一量化。6.2 归一化与非线性层的特殊待遇归一化层各种 Norm涉及均值、方差的计算这些统计量本身是浮点语义强行量化会让数值变得很不稳定。尤其是方差它是一个平方量动态范围比均值大得多量化后容易出现精度崩塌。实践中通常是让归一化层保持浮点或者把它折叠进相邻的线性层里再一起量化。非线性层要看具体类型。分段线性且斜率固定的比如 ReLU量化友好因为整数输入直接截断就能得到正确结果。涉及指数、除法的比如 sigmoid、softmax、GELU就麻烦它们的输出范围有限但曲线陡峭量化后容易在饱和区丢失区分度。常见的处理是把这类算子单独拿出来做查表或者用定点近似实现而不是简单套用量化公式。还有一个隐蔽的坑算子融合会改变量化行为。把卷积、批归一化、激活融合成一个算子量化参数需要在融合后的算子上重新计算不能简单沿用融合前的。有些工具链会自动处理有些需要手动指定搞错了会导致结果对不上但没有任何报错。6.3 硬件真的支持你选的低比特吗这一点值得单独强调因为它经常导致实验室效果很好上线完全不是那么回事。不同的硬件对量化格式的支持差异极大。有的芯片原生支持 INT8 且有针对性的指令跑起来飞快有的虽然支持 INT8 存储但计算时还是要展开成更高位宽加速效果打折有的对 4 位只有存储压缩没有对应的计算单元实际推理反而因为反复解包而变慢。所以在选比特数之前一定要先确认目标硬件的指令集能力。查手册的时候重点看三个东西支持的整数位宽、累加器位宽、以及是否有专门的矩阵乘加速单元。如果硬件只对某些特定形状比如 4×8 的分块有加速那你的量化方案最好往这个形状上靠否则拿不到峰值性能。还有个现实问题低位宽往往意味着更激进的误差需要更复杂的补偿算法而复杂算法在端侧可能根本跑不动。我见过一个方案为了在 4 位下保住精度加了一堆运行时校正逻辑最后端到端延迟比 8 位方案还高。量化不是比特越低越好找到精度和速度的平衡点才是目的。6.4 那些看不见的溢出最后一个坑最隐蔽数值溢出。前面提过累加器位宽这里再补几个具体场景。权重的量化参数是用校准数据算的如果线上出现了一个和校准集完全不同的输入激活大幅超出范围clamp 会把它截断到边界。截断本身不报错但结果会变成常数模型相当于对这部分输入瞎了。这类问题在日志里看不出来只能靠监控输出的分布来发现。还有一种是指数运算的溢出。如果某层有 exp 操作量化后的整数输入在接近上限时会算出极大的值超出后续层的表示范围。处理办法是在 exp 之前做范围限制把输入裁到一个安全区间内。整数乘法本身也可能溢出。两个 INT8 相乘最大是 16129还在 INT16 范围内但如果是 INT4 乘 INT4 再累加位宽规划就要更小心。我自己踩过一次某个自定义算子里用了 16 位累加单测数据量小的时候完全正常上了真实数据之后结果全乱排查了大半天才发现是累加溢出。量化这件事做久了会有个体会真正花时间的从来不是把浮点转成整数这个动作而是找到那个既能满足精度又能跑出速度的配置。这个配置没有通用答案它和你用的模型、你的数据分布、你部署的硬件、甚至你的推理框架版本都绑在一起。所以我现在的习惯是每接一个新场景先花两天做一轮完整的敏感度扫描和数据分布摸底把这张地图画出来后面所有的参数调整都有据可依不会再靠运气试。