
模型量化这个词这两年出现的频率越来越高。只要你在做模型部署不管是端侧、边缘盒子还是服务端推理加速几乎绕不开它。但很多人对量化的理解停留在“把 float32 换成 int8”这一句话上真到动手的时候发现精度掉了、速度没起来、算子不支持一堆问题冒出来。我自己在几个实际项目里踩过不少坑从最开始照着文档跑 PTQ 结果精度崩掉到后来慢慢摸清楚浮点表示、量化误差来源、PTQ 和 QAT 各自的适用边界才算真正把这条链路走通。这篇内容我打算把模型量化这件事从头讲透重点放在两个地方一是浮点数到底是怎么表示的为什么量化会带来误差二是 PTQ训练后量化和 QAT量化感知训练这两种范式各自的逻辑、适用场景和实操细节。整篇会偏原理加实操结合适合已经做过模型训练、准备做部署优化的同学也适合刚接触量化、想把底层逻辑搞明白的工程师。读完你应该能自己判断我手上这个模型该用 PTQ 还是 QAT量化位宽怎么选精度掉了该往哪个方向排查。1. 浮点表示量化误差的源头就在这里要理解量化为什么会产生误差得先搞清楚浮点数在计算机里是怎么存的。很多人跳过这一步直接调 API结果遇到精度问题完全不知道从哪下手。我一开始也是这样后来被一个激活值溢出问题卡了两天才回头把浮点表示重新捋了一遍。1.1 IEEE 754 浮点格式的组成现在主流的浮点表示遵循 IEEE 754 标准单精度 float32 占 32 位分成三段1 位符号位、8 位指数位、23 位尾数位。双精度 float64 则是 1 位符号、11 位指数、52 位尾数。这个结构决定了浮点数能表示的动态范围非常大但精度是有限的。符号位决定正负指数位决定数量级尾数位决定有效精度。举个具体的例子float32 能表示的数值范围大约在 1.4e-45 到 3.4e38 之间但有效数字只有大约 7 位十进制。也就是说一个 float32 数能精确到小数点后六七位再往后就是近似了。这里有个关键点浮点数的精度是相对的不是绝对的。数值越大两个相邻可表示浮点数之间的间隔就越大。比如在 1.0 附近float32 的最小间隔大约是 1.19e-7但到了 1000000 附近最小间隔就变成了 0.0625 左右。这个特性直接影响了量化的设计思路。1.2 定点与浮点的本质差异量化本质上是把浮点数映射到定点整数上。定点数的特点是小数点位置固定整数部分和小数部分的位数是预先分配好的。int8 就是 8 位定点整数能表示 -128 到 127 这 256 个离散值。浮点和定点的核心差异在于浮点用指数来动态调整精度定点用固定的缩放因子来映射。量化的时候我们需要确定一个 scale缩放因子和一个 zero_point零点把浮点区间线性映射到整数区间。公式大致是这样real_value scale * (quantized_value - zero_point)这个映射是线性的意味着原本在浮点空间里分布不均匀的数值被强行拉到了均匀的整数网格上。误差就来自这里原本两个很接近的浮点数量化后可能落到同一个整数格点上信息就丢了。1.3 为什么量化误差不可避免理解了浮点的相对精度特性就能明白量化误差为什么不可避免。假设一个层的权重分布范围是 [-3.5, 3.5]用 int8 表示256 个格点平摊到 7 的区间上每个格点间隔约 0.027。原本浮点能区分 1e-7 级别的差异量化后只能区分 0.027 级别的差异精度损失是必然的。但这里有个反直觉的地方量化误差不一定导致模型精度下降。神经网络本身有一定的冗余和鲁棒性很多权重对最终输出的影响很小量化带来的扰动在可接受范围内。真正的问题在于那些对精度敏感的层比如某些归一化层、注意力里的 softmax 输入量化后误差会被放大。提示判断一个模型能不能量化先看它的权重和激活值分布。如果分布很集中、离群点少量化友好如果分布很散、有极端离群值量化难度就大。1.4 对称量化与非对称量化的选择逻辑量化方案分对称和非对称两种。对称量化把零点固定在 0正负区间对称映射公式简单计算快适合权重这种大致对称分布的张量。非对称量化的零点可以移动能更好地拟合非对称分布比如 ReLU 之后的激活值全是非负的用非对称量化能减少浪费。实际项目里权重通常用对称量化激活值用非对称量化这是大多数推理框架的默认策略。原因很直接权重的分布一般围绕 0 对称对称量化不浪费整数区间激活值经过 ReLU 后偏向一侧非对称量化能更充分利用 256 个格点。选择哪种方案核心看数据的分布形态。你可以先统计一下张量的 min、max、均值、标准差如果均值接近 0 且分布对称对称量化更合适如果分布明显偏斜非对称量化误差更小。2. 量化粒度从 per-tensor 到 per-channel 的取舍确定了量化方案接下来要决定量化粒度。粒度决定了 scale 和 zero_point 是每个张量共享一个还是每个通道、每个组各有一个。这个选择对精度影响很大也是很多人调量化时容易忽略的一环。2.1 per-tensor 量化的适用与局限per-tensor 量化是整个张量共用一个 scale。优点是简单、存储开销小、计算快因为所有元素用同一套参数。缺点是如果张量内部各通道的数值范围差异很大用统一的 scale 会导致小范围的通道精度严重损失。我遇到过一个卷积层某些通道的权重范围是 [-0.1, 0.1]另一些是 [-2, 2]。用 per-tensor 量化scale 由最大的范围决定小范围通道的权重几乎全被压到 0 附近精度直接崩了。这种情况就得换更细的粒度。2.2 per-channel 量化的精度优势per-channel 量化给每个输出通道单独算 scale能显著缓解通道间范围差异带来的精度问题。代价是存储和计算开销增加因为每个通道都要存一组量化参数。对于卷积层per-channel 通常按输出通道维度来做。实测下来per-channel 量化在大多数视觉模型上能把精度损失从几个百分点降到零点几个百分点代价是推理时多一点点反量化开销。现在主流的推理引擎对 per-channel 的支持都比较好除非有特殊硬件限制我一般优先选 per-channel。2.3 分组量化与更细粒度的探索再往细走还有分组量化group-wise把通道分成若干组每组一个 scale。这在一些大模型量化里比较常见比如按 128 个元素一组。粒度越细精度越好但元数据开销越大计算也越复杂。粒度选择本质上是在精度、存储、速度之间做权衡。我的经验是小模型、通道差异不大的per-tensor 够用中等模型、通道差异明显的per-channel 是甜点大模型或者对精度极敏感的考虑分组量化。量化粒度精度存储开销计算复杂度适用场景per-tensor低最小最低通道分布均匀的小模型per-channel中高中等中等大多数卷积/线性层group-wise高较大较高大模型、精度敏感场景2.4 粒度选择的实操判断方法怎么判断该用哪种粒度我的做法是先跑一遍 per-tensor看精度掉多少。如果掉得在可接受范围内就用 per-tensor省事。如果掉得厉害换成 per-channel 再测。还不行再考虑分组。具体操作上可以逐层统计权重的通道间范围差异算一个比值最大通道范围除以最小通道范围。如果这个比值超过 10per-tensor 大概率会出问题直接上 per-channel。这个判断方法我在好几个项目里验证过比较靠谱。3. PTQ训练后量化的完整链路与踩坑记录PTQ 是最常用的量化方式因为不需要重新训练拿训练好的浮点模型直接量化就行。但“直接量化”这四个字背后有不少细节处理不好精度会掉得很难看。3.1 PTQ 的基本流程PTQ 的核心步骤是拿一批校准数据跑一遍模型统计各层的激活值分布据此算出 scale 和 zero_point然后把权重和激活都量化成整数。校准数据不需要标签通常几百张图片或者几百条样本就够。流程大致是准备浮点模型 → 准备校准数据集 → 插入观测节点统计分布 → 计算量化参数 → 转换模型 → 验证精度。每一步都有坑下面逐个说。3.2 校准数据集的选择策略校准数据的分布必须和真实推理数据接近这是 PTQ 精度的关键。我见过有人图省事拿训练集的前 100 张图做校准结果那 100 张恰好都是某一类量化后其他类的精度全崩了。正确的做法是从验证集或真实业务数据里随机采样保证类别均衡、场景覆盖全面。数量上几百到一千条通常够用太少统计不准太多没必要。如果业务数据有多个分布比如白天和夜间图像校准集要都覆盖到。注意校准集不要用训练集训练集可能有过拟合痕迹统计出的分布和推理时不一致。用验证集或独立采样的业务数据更稳。3.3 观测节点的插入与分布统计观测节点负责在推理时记录张量的 min、max 或者直方图。不同框架的观测方式不一样有的用 min-max有的用移动平均有的用 KL 散度。min-max 简单但对离群值敏感一个极端值就能把 scale 拉大导致其他值精度损失。KL 散度方法会找一个最优的截断阈值把离群值裁掉让主体分布映射得更充分。实测下来KL 散度在激活值量化上通常比 min-max 好尤其是激活值有长尾分布的时候。但 KL 计算慢一些需要更多校准样本。3.4 精度掉点后的排查链路PTQ 精度掉了别急着换 QAT先按这个链路排查第一步看是哪一层掉的。逐层对比量化前后的输出找到误差最大的层。通常问题集中在少数几层不是全局问题。第二步看那层的数值分布。如果是激活值有极端离群值考虑换 KL 散度或者加截断。如果是权重的通道差异大换 per-channel。第三步看是不是某些算子不支持量化被回退成了浮点。这种混合精度的情况有时反而更慢因为要来回转换。第四步检查校准集是否代表性不足。换一批校准数据再试。我遇到过一次精度掉了 5 个点排查半天发现是某个 concat 操作的输入范围差异太大量化后小范围那路信息全丢了。后来给那一路单独做了处理精度就回来了。3.5 PTQ 的适用边界PTQ 不是万能的。对于大模型、对精度极敏感的模型、或者权重分布特别不友好的模型PTQ 可能怎么调都达不到要求。这时候就得上 QAT。判断标准很简单如果 PTQ 调完精度还是差 2 个点以上且业务不能接受就转 QAT。4. QAT量化感知训练为什么能救回精度QAT 的思路是在训练过程中模拟量化误差让模型学会适应这种误差。相比 PTQ 的“事后补救”QAT 是“事前适应”精度通常更好但代价是要重新训练成本高。4.1 QAT 的核心机制伪量化QAT 的关键是伪量化fake quantization节点。前向传播时它把浮点值量化再反量化模拟量化误差反向传播时用直通估计器STE把梯度直接传过去绕过量化操作的不可导问题。这个设计很巧妙模型在训练时“感受”到了量化带来的扰动权重会朝着对量化更鲁棒的方向更新。训练完再把伪量化节点替换成真正的量化算子精度损失就小很多。4.2 直通估计器的作用与局限STE 解决了量化不可导的问题但它本身是个近似。梯度直接穿过量化操作没有考虑量化函数的真实导数这会导致梯度估计有偏差。实践中这个偏差通常可以接受但在某些敏感层上可能引起训练不稳定。我的经验是QAT 训练时学习率要比正常训练小通常小一个数量级。因为伪量化引入了噪声学习率太大会震荡。另外QAT 的 warm-up 阶段可以先不插入伪量化让模型先适应一下再逐步开启。4.3 QAT 的训练配置要点QAT 训练有几个关键配置学习率比原始训练小通常 1e-5 到 1e-4 量级训练轮数不需要从头训在预训练权重基础上微调几个 epoch 就行伪量化插入位置权重和激活都要插但某些敏感层可以跳过Batch Norm 处理QAT 时 BN 的统计量要冻结或者特殊处理否则会引入额外误差4.4 PTQ 与 QAT 的选型决策到底用哪个我总结了一个判断流程先跑 PTQ看精度。如果满足要求直接用 PTQ省时省力。如果差一点先调 PTQ 的粒度、校准方法、截断策略看能不能救回来。如果调完还差 2 个点以上或者模型本身对量化很敏感比如一些轻量级网络、检测模型的小目标分支直接上 QAT。还有一个现实因素QAT 需要训练资源和时间。如果项目周期紧、没有 GPU 资源PTQ 是唯一选择那就得在模型设计阶段就考虑量化友好性。维度PTQQAT是否需要训练否是精度一般更好成本低高适用场景大模型、精度要求不苛刻小模型、精度敏感调参难度中高4.5 QAT 实操中的常见问题QAT 训练时最常见的问题是精度不升反降。原因通常有几个学习率太大、伪量化插入位置不对、BN 统计量没处理好。我遇到过一次QAT 后精度比 PTQ 还差排查发现是 BN 的 running mean 和 variance 在量化后失配了冻结 BN 统计量后就正常了。另一个坑是 QAT 训练完导出模型时伪量化节点没正确替换成量化算子导致推理时还是浮点计算速度没提升。导出后一定要检查模型结构确认量化算子真的生效了。5. 量化位宽与混合精度的实战权衡位宽选择是量化里另一个关键决策。int8 是主流但 int4、int16 也有各自的应用场景。位宽越低压缩率和加速越明显但精度损失越大。5.1 int8 为什么是当前的主流int8 在精度和效率之间取得了很好的平衡。相比 float32模型体积压缩 4 倍内存带宽需求降低 4 倍在支持 int8 加速的硬件上推理速度能提升 2 到 4 倍。精度损失在大多数模型上可以控制在 1 个点以内。硬件支持也是 int8 普及的重要原因。主流推理芯片、移动端 NPU、服务端 GPU 都对 int8 有专门优化。相比之下int4 的硬件支持还不完善很多平台跑不起来或者加速比不明显。5.2 int4 与更低比特的探索int4 能把模型再压缩一半对大模型部署很有吸引力。但 int4 的精度损失明显通常需要配合分组量化、混合精度等技术。目前 int4 主要用在权重上激活值还是保持 int8 或更高。更低比特比如 int2、二值化学术研究里有工业落地很少因为精度损失太大除非模型本身极度冗余。我的建议是除非有极端的存储或带宽限制否则不要轻易上 int4 以下。5.3 混合精度量化的设计思路混合精度是指不同层用不同位宽。敏感层用高位宽鲁棒层用低位宽。比如第一层和最后一层通常对精度敏感保持 int8 甚至 float中间层可以尝试 int4。设计混合精度方案需要逐层做敏感度分析把某一层单独量化看精度掉多少掉得多的就是敏感层。这个分析比较耗时但能显著提升整体压缩率。实操中可以用自动化工具跑一遍敏感度扫描生成每层的敏感度排序再据此分配位宽。5.4 位宽选择的决策依据位宽选择没有标准答案取决于你的约束条件。如果存储和带宽是瓶颈优先考虑更低位宽如果精度是硬指标保守用 int8。我的经验是先明确业务对精度、延迟、体积的硬性要求再倒推位宽方案而不是反过来。6. 量化落地的工程细节与避坑清单原理讲完了最后说说工程落地时那些文档里不会写的细节。这些是我在实际项目里踩出来的经验能帮你少走弯路。6.1 量化友好型模型设计如果项目还在模型设计阶段提前考虑量化能省很多事。几个原则避免使用量化不友好的算子比如某些自定义激活函数控制权重的动态范围避免极端离群值BN 层的位置和参数要合理因为它影响激活值分布。6.2 推理引擎的算子支持差异不同推理引擎对量化算子的支持不一样。有的引擎某些算子只支持 per-tensor有的对非对称量化支持不好。选引擎之前先确认它对你模型里用到的算子支持到什么程度否则量化完发现一堆算子回退到浮点加速效果大打折扣。6.3 量化模型的验证方法量化后不能只看整体精度还要看逐层误差、输出分布变化、极端样本的表现。我通常会做三件事整体精度对比、逐层输出余弦相似度、bad case 分析。逐层相似度能快速定位问题层bad case 分析能发现量化在特定场景下的失效。6.4 常见问题速查表问题现象可能原因排查方向精度大幅下降校准集不具代表性换校准数据某层误差特别大该层分布有离群值换 KL 散度或截断速度没提升算子回退浮点检查算子支持QAT 不升反降学习率过大或 BN 失配调小学习率、冻结 BN输出全为常数scale 计算错误检查量化参数6.5 从 PTQ 到 QAT 的平滑过渡如果 PTQ 效果不理想准备转 QAT不要从头训。在 PTQ 得到的量化参数基础上初始化 QAT 的伪量化节点能让训练更快收敛。另外QAT 训练时可以先用较小的量化位宽做课程学习逐步收紧效果比一步到位好。我个人在实际操作中的体会是量化这件事没有银弹PTQ 和 QAT 各有各的适用场景关键是把浮点表示、量化误差来源、粒度选择这几个底层逻辑搞清楚遇到问题才能有的放矢地排查。很多时候精度掉点不是量化方法的问题而是校准数据、算子支持、模型结构这些外围因素导致的。把排查链路走顺了大部分问题都能定位到具体原因。