ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

INT8 量化实战指南:从数值原理到 LLM 部署优化

INT8 量化实战指南:从数值原理到 LLM 部署优化 这年头做模型部署谁还没被显存和延迟逼疯过几次。模型在 fp16 下跑得动一上生产环境就露馅多路并发显存爆掉首 token 延迟压不下来单卡 QPS 提不上去。INT8 量化这时候往往是最立竿见影的一招——模型体积直接缩小一半推理吞吐提升两到三倍精度损失通常控制在可接受范围。这个系列上一篇聊了推理框架的整体架构这篇专门讲量化INT8 矩阵乘到底发生了什么、校准是怎么选 scale 和零点、QAT 和 PTQ 怎么选、LLM 量化又有什么额外的坑。这篇文章适合刚接触量化的部署工程师也适合已经在用工具做过几次量化但效果不理想、想搞清楚问题到底出在哪一步的开发者。文章不会绕弯子直接从最底层的数值计算讲起再把校准、QAT、LLM 量化这些环节一个个拆开。准备好我们从部署侧的算力账单开始。1. 部署侧的算力账单显存、带宽与 INT8 的吸引力1.1 7B 模型的显存和延迟从哪来模型在推理时的开销其实主要分三块权重占显存、权重搬运占带宽、矩阵乘占算力。这三者之间有连带关系——显存占用越大能同时服务的并发数越少权重文件越大每次读取的带宽开销越高如果 GPU 的 INT8 算力闲置不用等于白扔一半硬件能力。举一个直接的数字例子。一个 7B 参数的模型fp16 权重占 14GB单张 24GB 的消费级显卡勉强放得下但显存已经吃掉六成。换成 INT8 之后权重降到 7GB直接省出一半显存也意味着我可以把并发数翻倍、或者在同卡上再塞一个服务。如果是 70B 级别的模型fp16 要 140GBINT8 只要 70GB能不能塞进一张卡完全是两个世界。带宽就更直接了。GPU 要计算矩阵乘得先把权重从显存搬到寄存器或 Tensor Core 里这个搬运速度就是带宽瓶颈。权重越小搬运时间越短每次前向的计算效率就越高。在 LLM 的 decode 阶段权重搬运甚至比计算更耗时因为每生成一个 token 只做一次矩阵乘数据量却很大。这就是为什么很多人说LLM 推理是带宽密集型——量化把权重缩小等于直接解决核心矛盾。算力方面以 A100 为例FP16 算力约 312 TFLOPSINT8 约 624 TOPS差距是两倍。新一代 GPU 上差距还可能更大。Tensor Core 是专门为低精度矩阵乘设计的硬件单元只要数据格式到位INT8 的吞吐就是实打实的翻倍。所以部署侧优化的第一反应永远是量化不是因为量化新潮而是因为它同时打中了显存、带宽、算力三个痛点。1.2 量化是有损压缩不是白拿的收益很多人听到量化就以为模型变小了还能变快这免费午餐太划算了。实际上量化是一个有损压缩过程把连续的浮点数映射到有限的离散整数集合信息必然减少。关键在于减少的信息是不是模型推理真正依赖的信息。我做量化项目时一贯的原则是先评估任务对数值误差的容忍度再决定量化方案。比如图像分类、文本分类这类任务对权重和激活的微小扰动不敏感PTQ训练后量化往往足够而目标检测、回归任务、以及一些对边界框定位精确度要求极高的场景INT8 的误差可能直接体现在指标上。理解量化到底是丢了哪些信息、为什么丢了这些信息没关系比背公式重要得多。这篇文章后面会从数值角度把这层窗户纸捅破。2. INT8 矩阵乘的数值真面目从量化公式到累加器溢出2.1 量化公式一个尺子和一个原点量化的核心思想可以概括成一句话用 256 个整数档位去表示原来浮点的数值范围。这里有数学公式作为基本框架。对称量化最简单q round(r / s)r s * q其中 r 是浮点值q 是整数s 是 scale缩放因子。对称量化假设浮点值的分布正负对称零点就是 0所以公式里没有 zero point。INT8 的取值范围是 [-128, 127]scale 的计算方式是s max(|r_min|, |r_max|) / 127。非对称量化则在公式里多一个 zzero pointq round(r / s z)r s * (q - z)其中 z 用来补偿分布不对称的情况比如 ReLU 之后激活值全是非负的用对称量化会浪费一半的整数档位非对称量化则能把档位全部用起来。2.2 对称 vs 非对称权重和激活的选择权重矩阵的数值分布通常比较接近正态分布——有正有负、中心在 0 附近所以权重多用对称量化。激活值就不一样尤其是经过 ReLU 之后全为正数这时激活用非对称量化更合理档位利用率更高。实际工程中的做法在线下模型里激活值常常用非对称量化在 TensorRT 等框架中权重默认 per-channel 对称量化激活则 per-tensor 或 per-token 用非对称量化。具体怎么排列组合取决于推理框架支持哪些算子组合不支持的组合会在转换时报错或者回退到浮点实现。2.3 矩阵乘的完整流程假设我们要计算Y X WX 是 [M, K] 的激活矩阵W 是 [K, N] 的权重矩阵。量化后的推理流程分四步量化激活X_int8 round(X_fp16 / sx)sx 是激活的 scale。量化权重W_int8 round(W_fp16 / sw)sw 是权重的 scale。矩阵乘Y_int32 X_int8 W_int8乘积在 INT32 累加器中完成。反量化Y_fp16 Y_int32 * (sx * sw)。注意第 3 步的累加器为什么用 INT32因为两个 INT8 相乘最大是 127 * 127 16129单次乘法不超 INT16 范围但矩阵乘是一个累加过程——K 维每做一次乘法就累加一次累加 K 次之后很容易溢出 INT16。比如 K512理论最大累加数百万远超 INT16 的 32767但仍在 INT32 的 21 亿范围内。所以矩阵乘内部必须用 INT32 累加器这是硬件设计时就已经定好的规则。反过来讲如果你的部署框架把 INT8 矩阵乘的累加器截断到 INT16精度一定崩。好在主流硬件Tensor Core、x86 VNNI和框架都正确实现了 INT32 累加这点不需要自己操心但要明白原理——调试精度问题时才能定位到是累加器的问题还是校准的问题。2.4 per-tensor 与 per-channel粒度的取舍scale 可以给整个张量共用一个也可以按维度细分。per-tensor整个张量一个 scale实现最简单但量化误差大因为张量内不同通道的数值范围差异无法体现。per-channel每个输出通道一个 scale准确度高很多。权重量化常用 per-channel因为权重是静态数据离线算好存起来即可推理时不增加额外负担。per-token对激活的每个 token 单独算 scale这是 LLM 量化里常用技巧后文会细讲。选粒度本质上是准确度和硬件支持之间的取舍。卷积和全连接层用 per-channel 几乎无成本但激活的 per-token 量化需要在运行时统计 token 的最大值多一次额外的归约操作。做工程时不要被更细更好迷惑先看框架支持到什么粒度再决定方案。3. 校准不是测数据而是给激活分布找一把合适的尺子3.1 校准到底在解决什么问题权重是静态的离线就能算好 scale但激活是动态的运行时数据不同、分布不同量化 scale 必须提前定下来。解决这个问题的手段叫校准Calibration拿一批有代表性的输入数据在目标模型上做前向推理统计每个会被量化的激活张量的数值分布再根据这个分布算出 scale 和 zero point。校准过程不更新任何权重——模型权重在全程冻结只是测分布。校准结束后得到每个量化节点对应的 scale 和 zero point保存到量化配置里。真正部署时这些参数被加载进推理引擎运行时用它们把浮点激活转成 INT8。3.2 校准数据集怎么选校准数据集的质量直接决定量化精度细节值得仔细抠。数量不用多500~2000 个样本基本够用。不是越多越好太多反而校准耗时翻倍边际收益很小。覆盖要全面分类任务里每个类别都要有样本检测任务要覆盖不同目标大小、不同场景光照。校准数据偏科scale 就被带偏。分布要贴近真实部署数据用于 OCR 识别的模型校准集就应该是排版真实、不同字体混杂的图像而不是干净的白底黑字合成图。不要混入训练集校准集和训练集重叠会导致模型见过这些数据指标虚高生产环境一换真实数据就现原形。有一点容易忽略校准数据要经过和正常推理完全一样的前处理流程。你用 OpenCV 裁剪缩放的图片训练校准时就别省缩放你线上有归一化参数校准时的输入也必须带同样的归一化。很多量化精度翻车案例最后查到原因不是量化本身而是校准数据的前处理跟线上不一致。3.3 MinMax、百分位与熵方法谁的尺子更好除了用什么数据校准另一个核心问题是拿到分布之后怎么定阈值。常用方法有四类校准方法核心思路优点缺点适用场景MinMax直接取 min/max实现简单速度最快对离群值极度敏感分布干净的小型模型Percentile取 99.9%/99.99% 分位点能容忍少数离群值分位数需要人工调正常数据的通用选择MSE枚举候选阈值选量化前后误差最小的误差有明确度量计算量较大受离群值影响控制精度要求高的场景KL 散度在原始分布和量化分布之间找最小 KL 距离的阈值更贴近信息论最优实现复杂校准耗时TensorRT 默认方案实操说明一下MinMax 面对一个数值为 200 的离群激活会把 scale 拉到 200/127正常的小值全部挤到几个整数档位里精度瞬间崩掉。Percentile 能拖住这个问题——取 99.99% 分位数时最极端的 0.01% 被丢弃scale 反而更贴近大多数数据的真实范围。KL 树熵方法实际上是 TensorRT 等框架内部默认的校准算法它不只是看绝对误差还考虑量化后的分布和原始分布的形状相似性在某些任务上更稳定。3.4 校准环节的三个常见坑第一个坑校准模式没切到 eval。如果模型里有 Dropout 或 BatchNorm校准跑的是训练模式Dropout 会随机丢弃神经元BatchNorm 会更新 running_mean激活分布和线上完全不一致。校准前必须model.eval()BatchNorm 在 eval 模式下会用固定的 running_mean分布才稳定。第二个坑校准集只有一两个 batch 就看效果。每批次数据的变化会体现在激活最大值上一两个 batch 的统计非常不稳定。至少跑几百个样本、分多个 batch再统计出来的 scale 才可靠。第三个坑校准后不验证每个量化层自己的误差。很多人只看端到端指标一旦掉点就毫无头绪。正确做法是校准后用校准集做一次量化模型推理比较每个量化层量化前后的输出差异找到误差最大的几个层再针对性地调整那些层的校准方式或改回浮点。这一步贵在定位一次定位省好几天的盲调时间。4. QAT当 PTQ 扛不住的时候再上场的中场补救4.1 PTQ 的能力边界训练后量化PTQ的优势是快——不需要重新训练几十分钟就能跑完校准精度通常掉 0.5~2%。但在有些任务上 PTQ 会明显翻车小模型MobileNet 级别、稀疏模型、超分辨率/生成模型、以及分布极端不平衡的数据量化后可能会出现几个百分点的精度下降。一个典型信号量化模型的每层输出差异SQNR明显偏高且集中在某几层。这种情况下强行调校准数据很难再往下压需要有别的手段。如果 PTQ 试到极限还不够就上 QAT。4.2 伪量化与直通估计器QAT 的核心思想是在训练/微调阶段就模拟推理时的量化误差让模型在反向传播时看到量化的存在从而自动调整权重去适应。具体实现靠一个叫伪量化Fake Quantization的操作forward先量化再反量化模拟部署时的量化误差backward用直通估计器STE让梯度直接穿过 round。数学描述如下x_fake round(x / s) * s这个操作在 forward 时是量化反量化s 是 scale在 backward 时因为 round 不可导STE 直接令dx/ds 1近似梯度原样通过。PyTorch 里的torch.quantization.FakeQuantize封装好了这个逻辑不需要自己手写。一个关键细节伪量化操作的 scale 在训练时是动态统计的根据当前 batch 的激活分布算出来的训练结束后再把 scale 固定成校准得到的静态值替换成真正的量化算子。很多新手在 QAT 训练时没有在意 scale 的更新机制导致训练完转静态量化时分布不一致精度掉得莫名其妙。4.3 QAT 实操流程一个可复用的流程如下先做 PTQ 校准拿到静态 scale 和 zero point评估量化误差确定哪些层误差大。在预训练模型上插入伪量化操作所有要被量化的层前后都要插入。用一个小学习率微调学习率通常取 1e-5~1e-4训练步数是原训练步数的 3%~10%。训练时关闭 BN 的跟踪统计将 BN 层设为 eval 模式或融合进卷积层避免 running_mean 在微调中偏移。验证转换微调完导出 onnx精度验证没问题后转部署引擎。流程说起来简单细节却容易踩坑。比如学习率开太大模型直接飞到另一个最优解反而比 PTQ 更差微调步数太短QAT 的效果还没完全体现就被截断。时间允许的话参数单调地试固定步数 1000 步lr 从 1e-5 往上调看验证集指标变化找到最佳组合。4.4 QAT 和 PTQ 的选择策略一个快速判断逻辑如果任务简单、模型大、量化后精度掉点 1%直接用 PTQ。如果掉点在 1~3%先试调校准数据、换校准方法再试 per-channel/per-token还不行上 QAT。如果掉点 3%直接 QAT同时检查模型架构有没有严重的离群值问题。QAT 也不是万能的——它增加了训练管线复杂度部署时如果模型量化算子不支持训练做的伪量化就白搭。落地前先确认推理框架对 QAT 导出的支持。常见的组合是 PyTorch 做 QAT 训练导出 ONNX 后用 TensorRT 或 ONNX Runtime 部署这个链路相对成熟。5. LLM 量化为什么更难离群值、注意力分布与 KV Cache5.1 LLM 激活分布里的钉子户传统 CNN 模型量化调用的套路放到 LLM 上一路碰壁。原因出在离群值LLM 的激活分布不是正态分布而是大部分值很小、少数几个维度出现极大值的长尾分布。比如某个 token 在某个隐藏维度上数值达到几十甚至上百而同一维度其他 token 只有零点几。这个离群值会把 scale 直接拉夸正常的值全被压到几个整数档位里。有研究统计过LLM 的离群值通常集中在少数几个维度上且这些维度与模型语义高度相关。直接整层用 INT8 量化精度掉得很凶。于是大家想到了各种绕过或压缩离群值影响的方案。5.2 主流方案LLM.int8()、GPTQ、AWQ、SmoothQuant先说LLM.int8()这是最直觉的一个方案检测激活中的极端离群维度把它们拆分出来用 fp16 计算其余的正常维度用 INT8 矩阵乘最后把两部分结果加到一起。好处是无需训练直接部署坏处是在线检测离群值有额外开销且真实加速效果没有纯 INT8 那么理想。GPTQ是训练后量化的标杆级方法核心思路是逐层量化并用二阶信息Hessian 近似补偿误差。它反复更新尚未量化的权重让已经量化层的误差在后续计算中得到一定抵消。GPTQ 常见 4bit 量化精度可以做到基本无损所以现在很多开源社区模型的量化版都是 GPTQ 格式。AWQ走的是另一条路不关心权重数值本身大不大而看哪些通道对激活值影响大。原理是找到重要通道给这些通道的权重做缩放保护从而减小量化误差。AWQ 的优点是不需要反向传播在校准集上跑一次前向就能算出缩放因子速度快效果好。SmoothQuant则把问题从激活难量化转成权重难量化通过数学变换把激活的量化难度转移到权重上让激活和权重都更容易量化。这种方案在 TensorRT-LLM 里有很好的集成适合追求 INT8 全量化速度的场景。四个方案不是互相排斥的——实际上最新框架里经常把它们混着用。比如 AWQ 做权重量化配合 SmoothQuant 的激活迁移再叠一层 per-token 动态量化是当前高吞吐 LLM 推理的常见做法。5.3 KV Cache 量化被很多人忽略的显存大头LLM 部署还有一个显存消耗大户KV Cache。长上下文场景下每个 token 的 K 和 V 向量都要缓存上下文越长KV Cache 占的显存越大甚至超过权重本身。比如 7B 模型 fp16 权重 14GB上下文 32K 时某些配置下 KV Cache 也能到十几 GB。KV Cache 量化就是把缓存的 K/V 矩阵从 fp16 转成 INT8 或 INT4显存直接砍半甚至更多。难点在于 KV 是动态产生的量化必须在写入缓存时完成读取时要反量化。而且不同 layer 的 KV 分布差异很大需要精细的 per-channel 或 per-head 量化策略。TensorRT-LLM、vLLM 等主流框架都提供了 KV Cache 量化开关生产环境建议有条件就开显存收益非常明显。5.4 工具链盘点从 ONNX 到 TensorRT-LLM、vLLM、GGUFLLM 量化的工具链已经比较成熟按部署场景分一下框架量化支持特点典型用途ONNX RuntimeINT8 PTQ/QAT、per-channel跨平台、成熟稳定传统模型和中小模型部署TensorRTINT8 PTQcalibrator延迟最低、GPU 优化彻底生产级 CNN 和部分 LLMTensorRT-LLMINT8、INT4、AWQ、GPTQ、SmoothQuant专为 LLM 设计高吞吐 LLM 在线服务vLLMAWQ、GPTQ、FP8PagedAttention 显存优化高频 LLM API 服务llama.cppGGUF、q2~q8 量化CPU/GPU 跨界支持本地部署、边缘设备GGUF 的量化档位很细从 q2_k 到 q8_0 是一个精度/体积的连续谱。本地跑模型时7B 模型 q4_k 大概 4.5GBq8_0 大概 7.5GB根据硬件显存选档位就可以。档位越高精度越好但体积也越大没有绝对最优只有你的硬件最合适。6. 落地中的几个经验校准集、混合精度与验证6.1 校准集不是越多越好关键是代表性有段时间我做量化疯狂往校准集里堆数据总感觉数据越多越安全。后来发现 2000 张和 5000 张在量化精度上几乎没有区别反而校准时间翻倍。真正影响结果的是数据的代表性——校准集要和真实部署数据的分布一致否则再多样本也是白给。一个不错的做法用一段生产环境的真实日志数据。比如 OCR 服务直接把线上采集的脱敏图片做抽样比任何手工构造的数据都贴近真实情况。数据侧没有捷径但选对来源等于赢在起跑线。6.2 混合精度不是所有层都适合 INT8量化到 INT8 不代表每一层都会被量化。如果某些层的输入分布极端、量化误差放大严重可以策略性地保留为 fp16。这种混合精度做法在工程中非常常见也是解决全 INT8 掉点最直接的手段。定位哪些层不该量化用上一轮量化后的层误差分布来判断误差最高的前 10~20% 的层先回退 fp16再测端到端指标看还有没有掉点空间。注意回退 fp16 会牺牲一部分加速收益所以要挑准那些误差贡献大但计算占比小的层收益损失最小。6.3 验证指标要看端到端效果不要只看单层指标量化调试中最容易陷入的误区是盯着某几个中间层的输出差异看半天得出这个层误差太大的结论后疯狂调那层却忽略了端到端指标。中间层误差大不代表最终输出误差大——后面还有机会通过后续层把误差吸收掉。反过来中间层误差小也有可能因为误差在链路上累积放大。我的习惯做法是两层验证并行先跑端到端业务指标准确率、BLEU、NDCG 等有掉点再逐层定位。端到端指标才是最终裁决者单层指标只是辅助定位工具。6.4 量化模型的迭代和维护量化模型不是一劳永逸的。模型版本更新、训练数据变化、部署场景漂移都可能让之前校准好的 scale 不再合适。项目组应该建立一个量化流水线意识每次模型更新都自动触发校准、验证、部署的流程而不是全靠手工操作。我在实践中会把校准集的版本也纳入模型版本管理——校准集和模型权重一样需要版本控制。量化参数跟着校准集走模型更新后重新校准量化精度变化追踪起来才不会一团乱麻。最后说点个人的实际体会量化这个领域理论很好懂但真正落地时问题永远出在分布上——数值分布不在你的预期内量化精度就跟着跑偏。先把校准和数值原理吃透比追着各种新工具跑半圈有用得多。这套方法论我换了无数个模型、数个框架一直都在用希望也能帮你在部署路上少踩几个坑。
返回列表