
1. 模型量化到底在解决什么问题做过模型部署的人都有一个共同体会训练阶段把精度刷到小数点后两位部署时却要在延迟、显存、功耗之间反复妥协。量化就是这场妥协里最有效的一张牌。它的核心思路并不复杂——把原本用32位浮点表示的权重和激活值压缩成8位整数甚至更低让模型在保持可用精度的前提下跑得更快、占得更少。我最早接触量化是在一个视觉检测项目上当时模型在服务器上跑得好好的一搬到边缘设备就卡得没法用。显存不够、推理延迟翻倍客户催得紧最后靠INT8量化把模型体积压到原来的四分之一延迟降了将近一半精度只掉了不到一个百分点。从那以后量化就成了我部署流程里的标配环节。这篇文章围绕INT8矩阵乘、校准、QAT和LLM量化这几个核心话题展开适合已经做过基础模型部署、想进一步优化推理性能的工程师。如果你还在纠结模型能不能跑起来建议先把部署链路跑通再来看量化如果你已经在跑但觉得不够快那这里的内容应该能帮到你。2. 量化基础与INT8矩阵乘的核心逻辑2.1 从浮点到定点量化的数学本质量化的本质是一个仿射映射。把一个浮点数x映射到整数q公式是q round(x / scale zero_point)反量化就是x_hat (q - zero_point) * scale这里的scale是缩放因子zero_point是零点偏移。为什么需要zero_point因为浮点数的0在量化后不一定对应整数0特别是对于有符号的激活值比如经过ReLU之前的特征图需要用一个偏移量来保证浮点0能被精确表示。这个细节在做对称量化和非对称量化时区别很大。对称量化把zero_point固定为0scale直接取绝对值最大值除以127。好处是矩阵乘的时候不用额外处理偏移坏处是对非对称分布的数据精度损失大。非对称量化则根据实际的最小值和最大值来算scale和zero_point精度更好但计算量稍大。我一般这样选权重量化用对称量化因为权重分布通常接近零均值激活量化用非对称量化因为ReLU之后的激活值全是非负的分布明显偏斜。2.2 INT8矩阵乘为什么能加速很多人以为INT8加速是因为数据变小了内存带宽省了。这只是一部分原因。更关键的是现代CPU和GPU都有专门的INT8乘加指令比如x86的VNNI指令集、ARM的dot product指令、NVIDIA GPU的DP4A和Tensor Core INT8模式。这些指令可以在一个时钟周期内完成多个INT8乘加运算吞吐量远超FP32。以VNNI为例一条VPDPBUSD指令能同时完成四个INT8乘法和累加而FP32的FMA指令一次只能做一个乘加。理论上INT8的峰值算力是FP32的四倍。实际推理中受限于内存访问和调度开销通常能拿到2到3倍的加速。但这里有个坑INT8矩阵乘的累加器通常是INT32。为什么不用INT8累加因为两个INT8相乘最大是127乘127约等于16129累加几次就溢出了。INT32能保证在合理的累加深度下不溢出。所以你在写自定义kernel的时候累加器一定要用INT32最后再乘scale反量化回浮点。2.3 权重量化与激活量化的差异权重是训练完就固定的量化过程可以离线做校准数据跑一遍就能确定scale和zero_point。激活值不一样它依赖输入数据每次推理都可能不同。所以激活量化要么用校准集统计出固定的scale要么在推理时动态计算。静态激活量化速度快因为scale是固定的可以和权重一样提前算好。动态激活量化精度高但每次推理都要统计当前batch的最大最小值有额外开销。实践中大部分场景用静态量化就够了只有对精度极其敏感的场景才上动态量化。注意激活量化的scale对精度影响比权重大得多。权重分布相对稳定激活分布受输入影响波动大。校准集选得不好激活量化能把精度拉垮。3. 校准量化精度的胜负手3.1 校准到底在做什么校准就是用一批有代表性的数据跑一遍模型统计每一层激活值的分布然后根据这个分布确定量化的scale和zero_point。听起来简单但校准集的选择和校准算法的选择直接决定量化后的精度。我见过太多人随便拿几十张图做校准结果量化后精度崩了回头怪量化方法不行。实际上问题出在校准集上。校准集必须覆盖模型在实际场景中可能遇到的数据分布数量不用多但代表性要强。一般几百到几千个样本就够了关键是分布要匹配。3.2 常用校准算法对比校准方法原理优点缺点适用场景MinMax取激活值的全局最小最大值实现简单无超参对离群值敏感分布均匀的激活Moving Average MinMax滑动平均统计最小最大值比MinMax稳定需要调窗口大小分布有波动的场景KL散度最小化量化前后分布差异精度好计算量大对精度要求高的场景Percentile取百分位截断抗离群值百分位需调有离群值的激活MSE最小化量化误差平方和平衡性好需要搜索通用场景KL散度校准是TensorRT默认用的方法效果比较稳。它的思路是把量化前后的激活分布都当成直方图然后找一个阈值使得两个分布的KL散度最小。这个阈值就是量化的截断点超过阈值的值会被截断。Percentile校准适合有离群值的场景。比如某些Transformer的激活值会出现极端大值用MinMax会把scale拉得很大导致大部分值量化后精度不够。取99.9%分位数截断牺牲少量离群值的精度换取整体分布的量化精度提升。3.3 校准实操中的关键细节校准数据的预处理必须和推理时完全一致。我踩过一次坑校准的时候用了归一化推理的时候忘了加结果量化模型精度直接掉了一半。这种低级错误听起来可笑但在实际项目里真的会发生。校准的batch size也有讲究。太小了统计不稳定太大了内存吃不消。一般用推理时的batch size就行。如果推理时batch size是1校准也用1但要多跑几个batch取平均。还有一点校准要在目标硬件上做。不同硬件的浮点运算精度有差异虽然理论上影响很小但我在ARM和x86上做过对比同样的校准集量化后的scale确实有细微差别。追求极致精度的话校准和推理用同一套硬件。实操心得校准集从训练集里抽但不要只抽某一类。我一般按类别分层抽样保证每个类别的样本数量均衡。如果训练集有数据增强校准集用原始数据就行不需要增强。4. QAT把量化误差纳入训练过程4.1 QAT与PTQ的本质区别PTQ是训练完再量化QAT是训练时就模拟量化。PTQ快几十分钟就能搞定但精度损失不可控。QAT慢要重新训练几个epoch但精度能逼近原始浮点模型。QAT的核心是在前向传播时插入伪量化节点模拟量化的舍入和截断误差但反向传播时用直通估计器把梯度直接传过去。这样模型在训练过程中就能适应量化带来的误差学到的权重对量化更友好。直通估计器的原理很简单量化的舍入操作本身不可导所以反向传播时直接把梯度原样传过量化节点假装量化不存在。这显然是个近似但实际效果很好。4.2 QAT的训练策略QAT不是从头训练而是在预训练模型的基础上微调。学习率要调小一般是原始学习率的十分之一到百分之一。训练epoch也不用多几个epoch通常就够了。我一般分两个阶段第一阶段只量化权重激活保持浮点让模型先适应权重量化第二阶段权重和激活都量化完成全量化训练。这样比一步到位更稳定。伪量化节点的位置也有讲究。权重在卷积或全连接之前量化激活在之后量化。对于残差连接两个分支的量化scale要一致否则相加的时候会引入额外误差。4.3 QAT的代价与收益QAT最大的代价是时间。一个中等规模的模型QAT跑下来可能要几个小时甚至一天。但收益也很明显PTQ掉点严重的模型QAT能把精度拉回来大部分。我在一个检测模型上做过对比PTQ量化后mAP掉了3.2个点QAT之后只掉了0.4个点。对于精度敏感的场景这个差距是值得花时间做QAT的。但QAT不是万能的。如果模型本身设计就不适合量化比如有大量小数值的权重QAT也救不回来。这种情况下要考虑量化感知的模型设计比如在训练时就加正则化让权重分布更集中。5. LLM量化大模型时代的特殊挑战5.1 LLM量化的难点在哪LLM量化和传统CNN量化有本质区别。CNN的激活值分布相对稳定LLM不一样它的激活值存在明显的离群值某些通道的数值比其他通道大几十倍甚至上百倍。这些离群值如果直接量化会把scale拉得极大导致其他正常值量化后全是零。另一个难点是LLM的权重分布也很特殊。注意力层的权重和FFN层的权重分布差异很大用同一套量化策略效果不好。还有一个现实问题LLM太大了校准数据跑一遍就要很久。7B的模型跑一遍校准集在单卡上可能要几十分钟。70B的模型就更不用说了。5.2 LLM量化的主流方案目前LLM量化主要有几条路线GPTQ基于二阶信息的逐层量化方法。它对每一层的权重做量化用Hessian矩阵的逆来指导量化误差的补偿。GPTQ能把LLM量化到4bit甚至3bit精度损失可控。缺点是量化过程需要校准数据而且计算量不小。AWQ激活感知的权重量化。它的核心观察是LLM的权重里只有少量通道对激活值影响大保护这些通道的权重精度其他通道可以量化得更狠。AWQ不需要反向传播量化速度快。SmoothQuant把激活的量化难度迁移到权重上。它通过一个数学等价变换把激活的离群值平滑掉让激活更容易量化。SmoothQuant通常和INT8配合使用能把LLM量化到W8A8。GGUFllama.cpp用的量化格式支持从2bit到8bit的多种量化级别。它的特点是混合量化不同层可以用不同的bit数。GGUF的Q4_K_M和Q5_K_M是目前社区里比较推荐的量化级别。方案量化位宽是否需要校准精度保持推理框架支持GPTQ3/4/8bit需要好vLLM, ExLlamaAWQ4bit需要好vLLM, TensorRT-LLMSmoothQuant8bit需要很好TensorRT-LLMGGUF2-8bit不需要中等llama.cpp5.3 LLM量化的实操建议如果你只是想在自己的机器上跑LLMGGUF是最省心的选择。下载现成的量化模型用llama.cpp直接跑不需要自己量化。Q4_K_M在精度和速度之间平衡得比较好显存不够就上Q3_K_S显存充裕就上Q5_K_M或Q6_K。如果你要部署到生产环境AWQ和GPTQ更合适。vLLM对这两种格式的支持都很好吞吐量比GGUF高不少。SmoothQuant适合对精度要求极高的场景但部署复杂度也最高。注意LLM量化后一定要做评测。不要只看loss要看实际任务的表现。我见过量化后loss几乎没变但生成质量明显下降的情况。评测集要覆盖你的实际使用场景。6. 量化实操中的常见问题与排查6.1 精度掉点严重怎么排查精度掉点是最常见的问题。排查思路是从后往前找先看是哪一层的量化误差最大再看是权重量化的问题还是激活量化的问题。具体操作逐层对比量化前后的输出找到误差最大的层。如果是权重量化的问题尝试换对称/非对称量化或者对权重做逐通道量化。如果是激活量化的问题换校准算法或者对激活做逐通道量化。逐通道量化是个很有效的技巧。对权重的每个输出通道单独计算scale而不是整个张量共用一个scale。这样能更好地适应不同通道的分布差异。代价是scale的数量变多了但现代推理框架都支持。6.2 推理速度没提升甚至变慢量化后速度没提升通常有几个原因第一硬件不支持INT8加速。老旧的CPU没有VNNI指令INT8矩阵乘是用浮点模拟的反而更慢。部署前先确认目标硬件支持哪些指令集。第二量化后的算子没有被推理框架正确调度。有些框架对某些算子的INT8实现不完善会fallback到浮点。用profiler看一下实际执行的算子类型。第三内存带宽不是瓶颈。如果模型很小计算量也不大量化省下的带宽对整体延迟影响有限。这种情况下量化收益不明显。6.3 常见问题速查表问题现象可能原因排查方法解决方案精度掉点严重校准集不匹配对比校准集和测试集分布重新选校准集精度掉点严重激活离群值统计激活值分布换Percentile或KL校准精度掉点严重权重量化误差大逐层对比输出逐通道量化速度没提升硬件不支持INT8查CPU/GPU指令集换硬件或降级方案速度没提升算子fallback用profiler看算子换推理框架速度变慢反量化开销大看反量化算子耗时融合反量化到前一个算子显存没减少中间激活没量化看显存占用分布开启激活量化6.4 几个容易忽略的细节量化后的模型保存格式要注意。有些框架保存的是量化后的整数权重和scale有些保存的是反量化后的浮点权重。前者体积小后者兼容性好。部署时要确认推理框架支持哪种格式。多线程推理时量化算子的线程安全性要确认。我遇到过某个框架的INT8卷积在多线程下结果不对的情况单线程正常多线程就出错。这种问题很难排查建议用官方推荐的线程配置。量化模型的版本管理也很重要。量化后的模型和原始模型要建立对应关系记录量化配置、校准集信息、评测结果。不然过几个月回头看根本不知道这个量化模型是怎么来的。7. 量化工具链选型与实战建议7.1 主流工具链对比工具支持框架量化方式目标硬件上手难度TensorRTONNXPTQ/QATNVIDIA GPU中等ONNX RuntimeONNXPTQ/QATCPU/GPU低OpenVINOONNX/PytorchPTQIntel CPU/GPU低TFLiteTensorFlowPTQ/QAT移动端/边缘低PyTorchPyTorchPTQ/QAT通用中等llama.cppGGUFPTQCPU/GPU低选工具链的第一原则是看目标硬件。NVIDIA GPU上TensorRT是首选它的INT8 kernel优化最好。Intel CPU上OpenVINO最合适。移动端用TFLite。通用场景ONNX Runtime最省心。第二原则是看模型来源。PyTorch训练的模型用PyTorch自带的量化工具最方便导出ONNX再用其他工具也行但可能遇到算子不支持的问题。7.2 我的量化工作流我现在的量化流程大概是这样的第一步在PyTorch里做PTQ实验快速验证量化可行性。用PyTorch的量化工具跑一遍看精度掉多少。如果掉点可接受继续如果掉点严重考虑QAT或者调整量化配置。第二步导出ONNX用ONNX Runtime或TensorRT做部署量化。这一步要注意算子兼容性有些PyTorch算子导出ONNX后不支持量化需要替换或自定义。第三步在目标硬件上做校准和评测。校准集从实际业务数据里抽评测集覆盖主要场景。第四步如果精度不达标回到PyTorch做QAT然后重新导出部署。这个流程的好处是快速迭代。PTQ实验成本低能快速排除不可行的方案。QAT成本高只在必要时做。7.3 量化不是终点量化只是推理优化的一个环节。实际部署中量化要和算子融合、内存布局优化、并行调度等手段配合使用。单独做量化收益可能有限组合拳打下来效果才明显。另外量化后的模型不是一劳永逸的。业务数据分布会漂移量化时的校准集可能过一段时间就不适用了。建议建立定期评测机制监控量化模型在实际业务中的表现。我在实际项目里的体会是量化能解决大部分推理性能问题但不能解决所有问题。如果模型本身设计就有问题比如层数太深、通道数太多量化也救不了。这种情况下要从模型结构入手做剪枝或知识蒸馏。量化是工具箱里的一把好手但不是唯一的一把。