ARTICLE DETAIL

资讯详情

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

INT8量化实战:从矩阵乘、校准到QAT与LLM量化落地

INT8量化实战:从矩阵乘、校准到QAT与LLM量化落地 模型上线之后真正让人头疼的往往不是网络结构本身而是精度掉了一点、延迟却卡在瓶颈这种不上不下的状态。FP32 跑得动但太慢FP16 快是快可有些老硬件根本不认于是 INT8 成了绕不开的一站。这篇就围绕INT8 矩阵乘、校准、QAT 与 LLM 量化这条主线把量化从为什么能压到怎么落地讲透。适合已经能把模型跑起来、准备做推理优化的同学也适合被显存和延迟卡过脖子、想搞清楚量化到底动了哪些手脚的工程师。我会尽量把原理、参数、踩坑点都摊开讲读完你应该能自己判断这个模型该不该量化、用哪种量化、校准集怎么选、掉点之后往哪查。1. 量化到底在压什么从浮点到定点的账本1.1 一个生活化类比把米尺换成厘米尺浮点数就像一把可以无限细分的软尺你想量多细都行代价是每次读数都要现算定点数则像一把刻度固定的硬尺刻度之间只能取整但读起来飞快。量化的本质就是把这把软尺换成硬尺并且约定好刻度代表多少真实长度。数学上最常用的线性量化affine quantization长这样real_value ≈ scale × (quantized_value - zero_point)其中scale是浮点缩放因子zero_point是整数零点偏移。反过来说把一个 FP32 值x量化成 INT8q round(x / scale) zero_pointround之后还要 clamp 到[-128, 127]有符号或[0, 255]无符号。这一步 clamp 就是精度损失的来源——超出范围的值会被削平也就是饱和saturation。1.2 对称量化与非对称量化差的不只是一个零点对称量化强制zero_point 0量化范围关于 0 对称通常取[-127, 127]。非对称量化允许零点偏移范围可以是[-128, 127]任意映射。类型零点典型范围适用场景计算开销对称0[-127, 127]权重、激活分布近似对称低可省去零点修正非对称可调[-128, 127]激活全为正如 ReLU 后略高需处理零点为什么权重常用对称量化因为权重通常以 0 为中心近似对称分布强制零点为 0 能省掉一大堆补偿计算硬件实现更简单。而激活经过 ReLU 之后全是非负的如果还用对称量化一半的整数范围就浪费了这时候非对称量化更划算。1.3 per-tensor、per-channel、per-group粒度越细越准也越贵per-tensor整个张量共用一个 scale最省但离群值会拖累整体精度。per-channel每个输出通道一个 scale卷积和线性层常用精度明显提升。per-group把通道再分组每组一个 scaleLLM 量化里非常常见比如 group_size128。粒度选择的经验是权重量化尽量用 per-channel 或 per-group激活量化因为运行时动态统计成本高多数场景还是 per-tensor。这个取舍后面讲 LLM 量化时还会反复提到。2. INT8 矩阵乘为什么能快硬件视角的拆解2.1 从 FP32 MAC 到 INT8 MAC省在哪矩阵乘的核心是乘加运算MAC。FP32 的一个 MAC 需要完整的浮点乘法器和加法器功耗和面积都大。INT8 的 MAC 可以用定点乘法器实现位宽只有 8 位同样面积的芯片能塞进更多运算单元。以常见的推理加速器为例INT8 的峰值算力通常是 FP32 的 4 倍甚至更多原因有三乘法器位宽从 32 位降到 8 位面积和功耗大幅下降数据搬运量减少 4 倍内存带宽压力骤降累加可以用 INT32 完成避免频繁的浮点归一化。注意INT8 的加速收益高度依赖硬件是否原生支持。如果硬件没有 INT8 指令框架会退化成反量化成 FP32 再算那不但不快反而更慢。上线前一定要确认目标硬件的指令集支持情况。2.2 量化矩阵乘的完整计算链路假设权重W和激活X都量化成 INT8各自有 scale 和 zero_pointW ≈ s_w × (W_q - z_w) X ≈ s_x × (X_q - z_x)那么Y W · X ≈ s_w × s_x × (W_q - z_w) · (X_q - z_x)展开后(W_q - z_w) · (X_q - z_x) W_q · X_q - z_w · ΣX_q - z_x · ΣW_q z_w · z_x · N其中N是累加维度长度。可以看到真正的 INT8 矩阵乘只需要算W_q · X_q这一项其余都是对结果的修正项。如果是对称量化z_w z_x 0修正项全部消失只剩一个干净的 INT8 矩阵乘加一个统一的 scale 缩放。这就是为什么工程上偏爱对称量化的深层原因——它让硬件路径最短。2.3 累加溢出INT8 乘 INT8 为什么用 INT32 累加两个 INT8 相乘结果范围是[-128×127, 127×127]约[-16256, 16129]已经超出 INT16 的安全范围。如果累加维度是 1024最坏情况累加值会到千万级别必须用 INT32 才能不溢出。实际实现里累加器通常是 INT32算完再乘上s_w × s_x反量化回浮点。这个INT8 乘、INT32 累加、最后统一缩放的模式是几乎所有 INT8 推理内核的标准套路。3. 校准量化精度的第一道生死线3.1 校准在做什么为什么不能省训练后量化PTQ不需要重新训练但它需要知道激活值的动态范围才能算出合理的 scale。这个统计激活范围的过程就是校准。校准的核心问题是用什么样的数据、统计什么样的范围。范围估小了大量激活被 clamp精度崩范围估大了有效量化位数被浪费精度也掉。这是一个两头不讨好的平衡。3.2 校准集怎么选不是随便丢几百张图就行校准集的选择有几个实操原则数量通常 100 到 500 个样本足够太多收益递减太少统计不稳。分布必须覆盖真实推理时可能遇到的输入分布。如果线上有长尾场景校准集里也要有。预处理校准集的预处理必须和推理时完全一致包括归一化、resize、通道顺序。这一点极其容易出错。我踩过的一个坑校准集用了训练集的中心裁剪但推理时用的是全图 resize结果激活分布对不上量化后 mAP 掉了 6 个点。排查了半天才发现是预处理不一致。3.3 几种主流校准算法对比算法原理优点缺点Min-Max取激活的全局最小最大值简单、覆盖全对离群值极敏感Moving Average Min-Max滑动平均历史 min/max更平滑仍需处理离群值KL 散度最小化量化前后分布差异精度好计算慢Percentile取分位数截断抗离群值分位数需调MSE最小化量化误差平方和平衡性好需搜索KL 散度校准是 TensorRT 的经典方案它通过搜索一个截断阈值让量化后的分布尽量逼近原始分布。Percentile 校准则更直接比如取 99.99% 分位把极端离群值直接砍掉。提示如果发现量化后某些层误差特别大先看这一层的激活是不是有极端离群值。很多情况下单独给这一层换校准算法或调分位数比全局换算法更有效。3.4 逐层敏感度分析找出不能量化的那几层不是所有层都适合量化。第一层卷积、最后的分类头、以及某些带特殊激活的层往往对量化特别敏感。做法是逐层做敏感度分析每次只把一层保持 FP32其余量化看精度变化。精度恢复明显的层就加入量化白名单跳过。这个流程在 PyTorch 里可以用torch.ao.quantization的 observer 配合自定义配置实现在 TensorRT 里则通过 layer-wise 的精度对比工具完成。4. QAT当 PTQ 掉点太多时的救命方案4.1 QAT 的核心思想把量化误差演给训练看PTQ 的问题是量化误差在训练结束后才引入模型没机会适应。QATQuantization-Aware Training在训练前向传播里插入伪量化节点fake quantize模拟量化的舍入和截断让模型在训练中就感受到量化误差从而调整权重去补偿。伪量化的公式就是前面那个q round(clamp(x / scale, -128, 127)) x_fake (q - zero_point) × scale注意前向是量化后的值反向传播时因为round不可导用的是 STEStraight-Through Estimator直接把梯度原样传过去。4.2 QAT 的典型流程与关键参数一个标准的 QAT 流程加载预训练 FP32 模型插入伪量化节点配置哪些层量化、用什么粒度用较小学习率微调若干 epoch冻结量化参数scale、zero_point导出量化模型。关键参数上学习率通常设为原始训练的 1/100 到 1/10epoch 数不用多几个 epoch 往往就够。学习率太大反而会把预训练权重带偏。4.3 QAT 与 PTQ 的取舍什么时候值得多花这些功夫维度PTQQAT训练成本无需要微调精度一般更好落地速度快慢适用场景大模型、冗余度高小模型、精度敏感经验判断如果 PTQ 掉点在 1 个点以内直接用 PTQ掉 1 到 3 个点先试敏感度分析跳过几层掉超过 3 个点或者模型本身很小比如移动端检测网络那就上 QAT。4.4 QAT 实操中的几个隐蔽坑BN 层融合QAT 前一定要把 BN 融进卷积否则量化节点和 BN 的顺序会出问题。伪量化节点的位置放在激活函数前还是后结果差别很大要按框架推荐来。导出后的验证QAT 训练时用的是伪量化导出的是真量化两者数值可能有细微差异必须用真实量化模型再跑一遍验证集。5. LLM 量化当模型大到装不下的时候5.1 LLM 量化的特殊难点LLM 量化和 CNN 量化完全不是一个难度级别。核心难点在于激活离群值LLM 的激活里存在极少数数值特别大的通道outlier这些离群值会把 per-tensor 的 scale 撑得很大导致其他正常值量化后精度全丢。权重分布不均不同通道的权重范围差异大per-tensor 量化效果差。显存瓶颈LLM 参数量巨大量化首先是为了省显存其次才是提速。5.2 权重量化GPTQ、AWQ 与 GGUF 的路线差异方案核心思路特点GPTQ逐层做二阶误差补偿精度好量化需校准数据AWQ保护重要权重通道推理快对激活离群值友好GGUF多种量化等级可选部署方便适合端侧GPTQ 的思路是逐层量化每量化一列权重就用剩余未量化的权重去补偿误差本质是一个带 Hessian 信息的贪心算法。AWQ 则观察到不是所有权重都同等重要通过激活幅度识别出重要通道对这些通道保留更高精度。5.3 激活量化的硬骨头SmoothQuant 与离群值处理激活量化比权重量化难得多。SmoothQuant 的核心 trick 是把激活的量化难度迁移一部分到权重上对激活除以一个平滑因子s对权重乘以s因为数学上Y (X/s) · (W·s)等价但激活的离群值被压平了权重的量化难度增加有限。这个思路很巧妙本质是用权重的余量去换激活的平滑。实际调参时s的选择依赖对激活和权重幅度的统计通常按通道计算。5.4 KV Cache 量化被低估的显存大户长上下文场景下KV Cache 的显存占用可能超过模型权重本身。KV Cache 量化通常用 INT8 甚至 INT4但要注意Key 的分布比 Value 更集中量化更友好Value 的离群值更多需要更细的粒度量化 KV Cache 对生成质量的影响在长序列上会被放大。注意KV Cache 量化一定要在长上下文场景下验证短序列测不出问题长序列上可能直接崩。6. 量化落地的完整检查清单与踩坑复盘6.1 上线前的验证流程量化模型不能只看能不能跑要有一套完整验证数值一致性量化模型和 FP32 模型在同一输入下的输出差异逐层对比精度指标在完整验证集上跑任务指标不只看 loss性能实测在目标硬件上测延迟和吞吐别信理论算力边界输入极端输入、全零输入、超大输入都要测看会不会溢出或异常。6.2 常见掉点原因速查表现象可能原因排查方向整体掉点均匀校准集分布不对检查预处理、样本覆盖个别层误差大该层有离群值敏感度分析、换校准算法输出全错scale 计算溢出检查是否有 NaN/Inf速度没提升硬件不支持 INT8确认指令集、算子融合长序列崩KV Cache 量化过激放宽 KV 量化精度6.3 我踩过的几个真实坑第一个坑是校准集预处理不一致前面提过掉了 6 个点。第二个坑是导出 ONNX 时忘了把伪量化节点正确转换结果导出的模型还是 FP32白忙一场。第三个坑最隐蔽某个版本的推理框架对 per-channel 量化的支持有 bugper-tensor 正常per-channel 结果全乱换版本才解决。这些坑的共同点是量化的问题往往不在量化算法本身而在数据流和工具链的衔接处。所以每次量化上线我都会把数据预处理一致性和导出后数值验证当成必查项。6.4 一个可复用的量化决策流程最后给一个我常用的决策路径先跑 PTQ看掉点掉点可接受直接上线省事掉点偏大做逐层敏感度分析跳过敏感层还不行上 QAT 微调LLM 场景权重优先用 GPTQ/AWQ激活用 SmoothQuantKV Cache 按需量化。这套流程不保证每次都最优但能覆盖绝大多数场景避免一上来就上最重的方案。量化这件事说到底是在精度、速度、显存三者之间找平衡点没有银弹。我个人的体会是先把校准和验证这两件事做扎实比盲目追求更激进的量化等级有用得多。很多时候一个干净的校准集加上逐层敏感度分析就能把 PTQ 的精度拉回到可用范围根本用不着上 QAT。
返回列表