资讯动态

模型量化数学原理深度解析:从仿射映射到误差传播

发布时间:2026/10/14 6:14:05 来源:尧图企业网站定制
1. 从一个反直觉的现象说起为什么量化后的模型反而“更聪明”了很多人第一次接触模型量化脑子里冒出的第一个念头是把高精度的浮点数砍成低比特整数这不是在“降智”吗精度掉了模型还能用我当初也是这么想的直到有一次把一个视觉分类模型从FP32压到INT8推理速度翻了将近三倍准确率只掉了不到0.3个百分点甚至在某个类别上还涨了一点。那一刻我才意识到量化这件事远没有“砍精度换速度”这么简单它背后有一套相当漂亮的数学逻辑在支撑。这篇文章想做的事情是把模型量化的数学原理从头到尾捋一遍。不是那种“调个库、传个参数就完事”的教程而是真正搞清楚为什么把连续的浮点数映射到离散的整数格点上信息的损失可以被控制得这么小量化误差到底从哪里来对称量化和非对称量化的数学差异在哪校准Calibration阶段到底在算什么这些问题搞明白了你在实际项目里做量化时就不会只是“试参数”而是知道每一步在干什么、为什么这么干。适合谁看如果你已经用过一些量化工具但对背后的数学一知半解这篇就是写给你的。如果你还没接触过量化但有一定的线性代数和概率基础也能跟下来。我会尽量用生活化的类比把抽象概念讲清楚同时保留必要的数学推导让你不仅“会用”还“懂为什么”。2. 量化的本质一个仿射映射的数学拆解2.1 从浮点到整数仿射变换的基本形式量化的核心操作用一句话概括就是建立一个从浮点数到整数的仿射映射。所谓仿射映射就是“乘以一个缩放因子再加上一个偏移量”这种形式的变换。用数学语言写出来对于非对称量化q round(x / s z)其中 x 是原始浮点值s 是缩放因子scalez 是零点zero-pointq 是量化后的整数值。反量化的时候x_hat s * (q - z)这里的 x_hat 是重建后的浮点值它和原始值 x 之间的差就是量化误差。对称量化是非对称量化的一个特例它强制零点 z 0映射变成q round(x / s) x_hat s * q为什么要有对称和非对称两种这取决于数据的分布。如果一个层的权重分布大致关于零对称比如经过BatchNorm之后的权重对称量化就够了而且计算更简单。但如果数据分布明显偏向一侧比如ReLU之后的激活值全是非负的非对称量化能更充分地利用整数的表示范围。2.2 缩放因子怎么算从数据范围到整数范围缩放因子 s 的计算本质上是一个“范围映射”问题。假设浮点数据的动态范围是 [x_min, x_max]量化后的整数范围是 [q_min, q_max]比如INT8就是[-128, 127]那么对于非对称量化s (x_max - x_min) / (q_max - q_min) z round(q_min - x_min / s)对于对称量化s max(|x_min|, |x_max|) / q_max这里有一个很关键的细节对称量化用 max(|x_min|, |x_max|) 而不是 (x_max - x_min)/2是因为对称量化要求正负范围一致所以取绝对值最大的那个作为边界。这会导致一个问题如果数据分布严重不对称对称量化会浪费一半的整数表示空间。举个例子如果某层激活值范围是 [0, 6]对称量化会取 s 6/127那么所有负整数区间[-128, -1]全部浪费掉了有效精度直接减半。注意选择对称还是非对称不是拍脑袋决定的。权重量化通常用对称量化因为权重分布一般关于零对称而且对称量化可以让卷积运算中的零点乘法项消失简化计算。激活量化通常用非对称量化因为激活值经过ReLU之后是非负的非对称能更充分利用位宽。2.3 量化误差的数学表达它到底损失了什么量化误差 e x - x_hat在round操作的作用下可以写成e x - s * round(x / s z) s * z ...简化一下对于对称量化误差就是e x - s * round(x / s)令 u x / s那么 e s * (u - round(u))。由于 round(u) 和 u 之间的差最大不超过0.5所以|e| s / 2这个结论非常重要量化误差的绝对值上界就是缩放因子的一半。换句话说缩放因子越小量化误差越小。而缩放因子 s range / (q_max - q_min)所以数据范围越小、整数位宽越大量化误差就越小。但这里有个陷阱如果你为了减小 s 而强行缩小数据范围比如做截断那些被截断掉的值会产生更大的误差。这就是为什么量化里有一个专门的步骤叫“校准”目的就是找到一个最优的截断范围在“缩小s”和“减少截断损失”之间取得平衡。3. 校准的数学如何在截断与精度之间找平衡点3.1 为什么不能直接用全局最大最小值最朴素的量化方法是统计整个张量的最大最小值直接算 s 和 z。但这样做有个问题深度网络中的激活值分布通常是“长尾”的绝大多数值集中在一个较小的范围内但偶尔会出现一些极大的离群值outlier。如果你用全局最大值来算 s那么 s 会被这些离群值撑得很大导致正常范围内的值量化误差剧增。我做过一个实验某个层的激活值99%都落在[-2, 2]之间但最大值是15。如果用全局最大值s 15/127 ≈ 0.118那么正常范围内的量化误差上界是0.059。但如果把范围截断到[-3, 3]s 3/127 ≈ 0.0236误差上界降到0.0118缩小了5倍。代价是那1%超过3的值会被截断产生较大的截断误差。但由于它们只占1%总体误差反而更小。3.2 KL散度校准让量化后的分布尽量接近原始分布既然截断有好处那怎么找到最优的截断阈值一个经典的方案是最小化量化前后分布的KL散度。具体做法是统计激活值的直方图得到原始分布 P。对于每一个候选截断阈值 t把超过 t 的值截断然后量化再反量化得到量化后的分布 Q_t。计算 P 和 Q_t 的KL散度 D_KL(P || Q_t)。选择使KL散度最小的那个 t。KL散度的定义是D_KL(P || Q) sum_i P(i) * log(P(i) / Q(i))它衡量的是用分布 Q 来近似分布 P 时信息损失有多大。KL散度越小说明量化后的分布和原始分布越接近量化带来的信息损失越小。这个方法的直觉是我们不关心单个值的误差而是关心整体分布的保真度。即使某些离群值被截断了只要整体分布形状没怎么变量化后的模型表现就不会差太多。3.3 百分位截断一个更简单但有效的替代方案KL散度校准虽然理论优雅但计算量不小而且对直方图的bin数量敏感。在实际项目中我更多用的是百分位截断直接取99.9%或99.99%分位数作为截断阈值。比如把所有激活值排序取第99.9%大的那个值作为 x_max。这样做的好处是简单、快速、可解释。缺点是百分位这个超参数需要调不同层、不同模型的最优百分位可能不一样。我的经验是对于大多数视觉模型99.9%是个不错的起点对于NLP模型中的注意力层可能需要更保守一些用99.99%甚至更高因为注意力分数的分布往往更分散。实操心得在校准阶段不要只用一批数据。我通常会用100-200个batch的校准数据统计每个层的激活值分布然后取平均或中位数作为最终的截断阈值。用单batch校准容易过拟合到那批数据的分布导致量化后在新数据上表现不稳定。4. 量化粒度从Per-Tensor到Per-Channel的数学差异4.1 Per-Tensor量化一个缩放因子管所有Per-Tensor量化是最粗粒度的方案整个张量共用同一个缩放因子 s 和零点 z。数学上最简单计算也最快因为反量化的时候只需要一次乘法。但它的缺点也很明显如果张量内不同通道的数据分布差异很大Per-Tensor量化会顾此失彼。比如某个卷积层的权重第一个通道的范围是[-0.1, 0.1]第二个通道的范围是[-2, 2]用同一个 s 的话要么第一个通道的精度被浪费要么第二个通道的精度不够。4.2 Per-Channel量化每个通道独立的缩放因子Per-Channel量化给每个通道通常是卷积核的输出通道分配独立的 s 和 z。这样每个通道都能根据自己的数据范围来优化量化精度。数学上对于卷积权重 W形状是 [C_out, C_in, K, K]Per-Channel量化会对每个 C_out 通道计算一组 (s_c, z_c)s_c max(|W[c, :, :, :]|) / q_max反量化的时候W_hat[c, :, :, :] s_c * (Q[c, :, :, :] - z_c)Per-Channel量化的精度明显优于Per-Tensor尤其是在权重分布通道间差异大的情况下。代价是存储量增加每个通道要多存一个 s 和一个 z。对于INT8量化权重本身占1字节加上 s 和 z 的开销总体存储增加大约 2/通道元素数通常可以忽略不计。4.3 Per-Group量化在粒度和开销之间取折中Per-Group量化是Per-Channel和Per-Tensor的折中把通道分成若干组每组共用一组 (s, z)。比如每128个通道一组。这样既比Per-Tensor精度高又比Per-Channel开销小。在大型语言模型的量化中Per-Group量化非常常见。因为语言模型的权重矩阵往往很大Per-Channel量化的存储开销会变得显著而Per-Group能在精度和开销之间取得不错的平衡。量化粒度缩放因子数量精度存储开销适用场景Per-Tensor1低最小对精度要求不高的场景Per-ChannelC_out高中等卷积网络权重Per-GroupC_out / G中高较小大语言模型权重注意激活值量化通常只能用Per-Tensor因为激活值是在推理时动态产生的你没法提前知道每个通道的范围。有些方案会做动态量化在推理时实时统计范围但这会引入额外的计算开销。5. 量化误差的传播为什么深层网络对量化更敏感5.1 误差累积的数学分析量化误差不是孤立存在的它会随着数据在网络中的传播而累积和放大。假设第 l 层的输入有一个误差 δ_l经过该层的线性变换 W_l 后输出误差变成δ_{l1} W_l * δ_l ε_l其中 ε_l 是该层量化引入的新误差。展开这个递推关系δ_L (prod_{il}^{L-1} W_i) * δ_l sum_{jl}^{L-1} (prod_{ij1}^{L-1} W_i) * ε_j第一项是初始误差经过所有层传播后的结果第二项是各层量化误差的累积。如果权重矩阵的谱范数最大奇异值大于1误差会被放大如果小于1误差会被衰减。这就是为什么深层网络对量化更敏感层数越多误差累积的机会越多。而且如果某些层的权重谱范数较大误差会被显著放大。5.2 为什么BatchNorm对量化友好BatchNorm层在训练时会归一化激活值使得每层的输入分布保持稳定。从量化的角度看BatchNorm有一个很重要的副作用它把激活值的动态范围压缩到了一个相对固定的区间通常是[-3, 3]左右因为归一化后的值大致服从标准正态分布。这意味着量化BatchNorm之后的激活值缩放因子 s 可以设得比较小量化误差自然就小。而且由于分布稳定校准阶段统计出来的截断阈值在推理时也能保持有效。在实际量化中通常会先把BatchNorm的参数融合到前面的卷积层里这样推理时就少了一个算子同时卷积权重的分布也会因为融合了BatchNorm的缩放而变得更适合量化。5.3 残差连接量化误差的“高速公路”残差连接Residual Connection对量化误差的传播有特殊影响。残差连接的形式是y F(x) x如果 F(x) 的量化误差是 ε那么输出误差就是 ε因为 x 本身可能也有误差但残差连接至少不会放大 F 的误差。这看起来是好事但实际上残差连接会让误差“跳过”一些层直接传播到更深处。更关键的是残差连接要求 F(x) 和 x 的量化尺度要匹配。如果 F(x) 和 x 的分布差异很大量化时可能需要不同的缩放因子但相加操作又要求它们在同一尺度下进行。这就需要在量化方案设计时特别小心通常会对残差连接的两个分支使用相同的量化参数。6. 从数学到工程量化实操中的几个关键决策6.1 权重量化和激活量化为什么要分开处理权重是静态的在推理前就已知所以可以用更精细的量化方案比如Per-Channel而且可以离线做复杂的校准和优化。激活值是动态的每次推理都可能不同所以只能用Per-Tensor而且校准只能在有限的校准数据集上做。这个差异导致了一个常见的工程实践权重量化用INT8甚至INT4激活量化用INT8但两者的缩放因子和零点分开管理。在推理时卷积运算变成y s_w * s_a * conv(Q_w, Q_a) ...其中 s_w 和 s_a 分别是权重和激活的缩放因子。这个乘法可以在后处理阶段合并不会显著增加计算量。6.2 量化感知训练让模型“适应”量化误差如果训练后量化PTQ的精度损失太大可以考虑量化感知训练QAT。QAT的核心思想是在训练过程中模拟量化操作让模型在训练时就“感受到”量化误差从而调整权重来补偿。数学上QAT在前向传播时插入伪量化节点x_q s * round(clamp(x / s, q_min, q_max))反向传播时由于round函数的梯度几乎处处为零需要用直通估计器Straight-Through Estimator, STEdL/dx dL/dx_q也就是说梯度直接穿过round操作不做任何修改。这看起来很不严谨但实际效果出奇地好。直觉解释是虽然round的梯度不对但它至少给出了一个方向让模型知道往哪个方向调整权重可以减少量化误差。6.3 混合精度量化不同层用不同位宽不是所有层都同等重要。有些层对量化非常敏感比如第一层和最后一层有些层则很鲁棒比如中间的卷积层。混合精度量化就是给敏感层分配更高的位宽比如INT16给鲁棒层分配更低的位宽比如INT4。决定哪些层用高位宽通常有两种方法一是基于敏感度分析逐层量化并测量精度损失二是基于权重分布如果某层的权重分布很宽或者有很多离群值就给它更高的位宽。我在实际项目中的做法是先用PTQ跑一遍看每层的量化误差和最终精度损失然后对误差最大的几层提升位宽重新量化。通常只需要提升2-3层就能把精度损失从1%降到0.1%以内。实操心得混合精度量化不要一开始就上先用统一位宽跑通找到瓶颈层之后再针对性调整。否则你会在参数组合的海洋里迷失方向。7. 量化数学的边界哪些情况下量化会失效7.1 当数据分布极度不均匀时如果某层的权重或激活值分布极度不均匀比如99%的值集中在0附近但1%的值跨度极大那么无论怎么选截断阈值都会面临两难截断太多那1%的信息丢失严重截断太少99%的值量化精度不够。这种情况下Per-Tensor量化基本无解只能靠Per-Channel或Per-Group来缓解。如果连Per-Channel都不行那可能需要考虑混合精度给这层单独用更高的位宽。7.2 当模型对数值精度极度敏感时有些模型或任务对数值精度非常敏感比如某些生成模型中的softmax层或者某些强化学习中的值函数。这些地方的微小误差会被放大导致输出完全不可用。对于这种情况我的建议是不要强行量化这些层。保留它们为FP32或FP16只量化那些鲁棒的层。混合精度量化框架通常都支持“排除某些层”的功能善用它。7.3 当校准数据分布与推理数据分布不一致时校准阶段统计的激活值范围是基于校准数据集的。如果推理时的数据分布和校准数据差异很大那么校准阶段算出来的截断阈值可能完全不适用导致量化误差剧增。这个问题在领域偏移Domain Shift场景下特别常见。比如用自然图像校准的模型部署到医学图像上激活值分布可能完全不同。解决办法是尽量用和目标域接近的数据做校准或者使用动态量化在推理时实时统计范围。8. 我个人在量化实践中的几点体会做了这么多量化项目有几个体会是反复被验证的。第一量化不是“免费的午餐”但它是一顿“性价比极高的午餐”。你几乎不可能做到量化后精度零损失但你可以把损失控制在可接受的范围内同时获得2-4倍的推理加速和4倍的模型压缩。这个 trade-off 在绝大多数部署场景下都是值得的。第二数学原理不是装饰品它直接指导工程决策。理解了量化误差的上界是 s/2你就知道为什么要做校准理解了KL散度的含义你就知道为什么截断比不截断好理解了误差传播的递推公式你就知道为什么深层网络要更小心。这些不是理论家的游戏而是工程师的指南针。第三校准数据的质量和数量往往比量化算法本身更重要。我见过太多人花大量时间调量化参数却只用了几十张图片做校准。实际上用200-500个batch的校准数据比任何花哨的算法都管用。第四不要追求极致的低位宽。INT8已经能带来足够的收益INT4虽然更激进但精度损失和工程复杂度都会显著上升。除非你有极强的部署约束否则INT8是大多数场景下的甜点。最后分享一个小技巧在做量化之前先分析一下每层的权重和激活值分布。如果发现某些层的分布特别奇怪比如双峰、长尾、极度偏斜提前标记出来这些层大概率是量化的难点。有针对性地处理这几层比全局调参效率高得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑