AI图鉴

AI图鉴首页 › AI 资讯 › 浅析模型量化的数学原理 — 本文从数学角度完整拆解模型量化(Quantizati

浅析模型量化的数学原理 — 本文从数学角度完整拆解模型量化(Quantization):它如何把 FP32 浮点权重

来源:掘金 · 发布时间:10-12 · 由 AI图鉴每小时自动聚合

本文从数学角度完整拆解模型量化(Quantization):它如何把 FP32 浮点权重/激活映射成低比特整数,S 和 Z 怎么求,量化误差从何而来、如何严格证明它的界,信噪比怎么推等

本文从数学角度完整拆解模型量化(Quantization):它如何把 FP32 浮点权重/激活映射成低比特整数,S 和 Z 怎么求,量化误差从何而来、如何严格证明它的界,信噪比怎么推,以及为什么 8-bit 甚至 4-bit 量化之后,模型精度损失通常并不严重。 内容框架与 MIT 6.5940 / TinyML(韩松 Song Han)课程的 Quantization 一讲一致。

收录于微信公众号:AngryCode 愤怒的代码

0. TL;DR

- 量化的本质是一条仿射变换:r = S(q − Z)。r 是 float,q 和 Z 都是整数,S 是 float——全公式只有一次浮点乘法。

- S = 数据范围 ÷ 整数范围。数据范围有三条求法:min-max、裁剪+MSE、裁剪+KL 散度。

- 误差来自四舍五入,可严格证明 |ε| ≤ S/2、零均值、方差 S²/12(均匀分布假设下积分可得)。

- 每加 1 bit,量化信噪比提升 6 dB(SQNR = 6.02n dB);int8 的噪声只有满量程的 ~0.4%。

- 误差在求和/传播时正负抵消(标准差按 √N 增长),且激活函数 Lipschitz 连续(ReLU 的 L=1)使误差不被放大,因此整网几乎不掉点。

- 矩阵乘法可以拆成纯整数 GEMM + 一次尺度相乘,这是量化省算力、省带宽、省功耗的数学来源。

1. 为什么要量化

端侧/边缘设备(手机、MCU、NPU)受四个硬约束:

- 内存:FP32 一个权重占 4 字节,int8 只占 1 字节 → 模型体积缩小 4 倍。

- 带宽:内存搬运是端侧推理的主要能耗来源,位宽降低直接成比例省电。

- 算力:整数 MAC 单元面积、功耗、时延都远低于 FP32 MAC。

- 实时性:整数运算 + 更小的内存占用 → 更低延迟。

一句话:量化不是为了"更好",而是为了"能跑"。 而它之所以可行,是因为量化误差在数学上可控——这就是本文要讲清楚的。

2. 出发点:FP32 到底存了什么

IEEE 754 单精度浮点数 = 1 符号位 + 8 指数位 + 23 尾数位:

x = (−1)^s × 2^(e−127) × (1 + f / 2^23)

- 数值范围:约 ±3.4 × 10³⁸

- 相对精度:约 2⁻²³ ≈ 1.2 × 10⁻⁷(约 7 位有效数字)

关键认知:神经网络权重是训练出来的统计量,不是物理常数。 它不需要 7 位有效数字的精度。这为量化提供了巨大的冗余空间。

3. 量化的数学定义:仿射映射

3.1 核心公式

量化的核心就是把实数 r 映射到整数 q:

r = S · (q − Z) (反量化:整数 → 实数)

q = clamp( round(r / S) + Z , q_min, q_max ) (量化:实数 → 整数)

clamp 是什么:限幅函数——把值夹进 [min, max] 区间,太小压到 min、太大压到 max、区间内原样返回:

clamp(x, min, max) = min 若 x < min

= x 若 min ≤ x ≤ max

= max 若 x > max

在量化里它的作用:round(r/S) + Z 可能超出整数合法范围——比如 r 是校准时没见过的 outlier,算出的整数超出 [q_min, q_max](如 int8 算出 300 或 −254)。clamp 保证结果一定落得进 n 比特(int8 就是 [−128, 127]),不会溢出回绕。代价是被夹掉的值产生裁剪误差(无界,见 5.1 节)——这正是校准(KL/MSE 选阈值)要优化的那个误差的来源。clamp 在不同框架里也叫 clip(NumPy/TensorFlow)、饱和运算 saturate(硬件)。

3.2 四个量各自的类型(一个常见误解)

Z 不是 float,是整数(和 q 同一个整型):

所以 r = S(q − Z) 的运算过程是:

(q − Z) → 整数减法

× S → 乘一个 float,得到 float

整个公式里只有一个 float 乘法,其余都是整数运算——这正是量化能提速的关键。

为什么 Z 一定是整数? 看 Z 的求法:

Z = round( q_min − r_min / S )

q_min 是整数,r_min/S 是 float,相减后 round 成整数,所以 Z 天生是整数,且落在 [q_min, q_max] 内,能用和 q 一样的 n 比特存下来。如果 Z 是 float,硬件就得做浮点减法、多存一个 float,量化就失去意义了。

一个具体例子:数据范围 r ∈ [−0.5, 1.0],uint8 量化(q ∈ [0, 255]):

S = (1.0 − (−0.5)) / 255 = 1.5/255 ≈ 0.005882

Z = round(0 − (−0.5)/0.005882) = round(85) = 85

验证 r = 0:S(q − Z) = 0 ⟹ q = Z = 85。实数 0 对应的整数就是 85——Z 是整数。

3.3 为什么叫仿射变换

因为 r = S·q − S·Z 是一条直线:斜率 S,截距 −S·Z。它允许"缩放 + 平移",所以既能表示对称分布(Z=0),也能表示非对称分布(Z≠0,如 ReLU 输出的非负激活)。本文的一切结论都从这条直线推出。

4. 怎么求 S 和 Z

S 的计算核心一句话:S = 数据范围 ÷ 整数范围。整数范围由位宽定死,真正要决定的是"数据范围"怎么取。

n 比特整数的表示范围:

4.1 方法一:min-max 非对称(全覆盖,无裁剪)

S = (r_max − r_min) / (q_max − q_min)

Z = round( q_min − r_min / S )

验证端点对齐:r_min = S·(q_min − Z),r_max = S·(q_max − Z)。

例子:某层 ReLU 激活,范围 [0, 6.0],用 uint8:

S = (6.0 − 0) / (255 − 0) = 6/255 ≈ 0.023529

- 优点:一个值都不裁剪,全盖住。

- 缺点:对 outlier 极其敏感。如果 99.9% 的值落在 [0, 0.5]、只有一个 6.0,S 就被这个 outlier 撑大,绝大多数值的量化误差被无谓放大。

4.2 方法二:min-max 对称(Z=0)——为什么必须加绝对值

对称量化强制 Z = 0,映射退化为 r = S·q:

α = max(|r_min|, |r_max|)

S = α / q_max (有符号时 q_max = 2^(n−1) − 1)

这里 abs 不是可选项,是定义本身。 原因:对称量化要求映射关于 0 对称——整数能取 −127 和 +127,对应的实数就是 −S·127 和 +S·127,所以实数区间必须是 [−α, α],而 α 必须 ≥ 所有数据里"离 0 最远"的值,即最大绝对值。

反例:不加绝对值会怎样。设张量 r_min = −2.0,r_max = 1.0:

- ❌ 不加 abs:max(r_min, r_max) = 1.0,S = 1.0/127。那么 −2.0 对应整数 = −2.0/(1/127) = −254,超出 int8 范围 [−127, 127],被硬夹到 −127,所有小于 −1.0 的负值全部失真。

- ✅ 加 abs:α = max(|−2|, |1|) = 2.0,S = 2.0/127。区间 [−2, 2] → [−127, 127],完美覆盖。

数据范围(不对称): [----r_min=-2.0 ····· 0 ····· r_max=1.0----]

对称量化强制摊开成: [----−α=-2.0 ······· 0 ······· +α=+2.0----]

↑

右边 [1.0, 2.0] 这段是"浪费"的

对称量化为了保持 Z=0 的简洁,代价就是两边必须对称摊开,数据偏一边时另一半范围就浪费了——这也是它和非对称量化的核心区别。

实现细节:有符号 int8 的 q_max 用 127 而不是 128,为了 ±q_max 对称(−128 被闲置)——这是"强制对称"付出的微小代价,换来的 GEMM 大幅简化(见第 7 节)。

4.3 方法三:裁剪 + 校准(针对 outlier)

min-max 的问题在 outlier。更聪明的做法:故意裁掉少量极端值,换取更小的步长。

设裁剪阈值 T(0 < T ≤ max|r|),只量化 [−T, T] 内的值,超过 T 的直接夹到端点:

S = T / q_max (对称)

T 的选取是核心,两条准则:

① MSE 准则:选 T 使总误差的均方最小:

T* = argmin_T E[ (Q(r) − r)² ]

= argmin_T [ 裁剪误差(超出T的部分) + 量化误差(S²/12) ]

T 太小 → 裁剪误差大;T 太大 → S 大、量化误差大。两者此消彼长,最优 T 平衡它们。

② KL 散度准则(NVIDIA TensorRT 的做法):把 FP32 值和量化后的值各做成直方图,选 T 使两个分布的信息损失最小:

T* = argmin_T KL( P_fp32 ‖ P_quant(T) )

直观理解:选一个 T,让"量化前的数值分布"和"量化后的数值分布"长得最像。 对高斯/拉普拉斯分布的权重,最优 T 的经验值约在 ±(2.5 ~ 4)σ——不是 min-max 的全范围,而是收敛在几倍标准差处。

4.4 数据从哪来:权重 vs 激活(一个关键区别)

所以 PTQ 流程里常看到"用 100~1000 张校准图片跑前向、统计每层激活的分布"——那就是在给激活算 S(和 Z)。

4.5 方法小结

5. 量化误差的数学分析

量化后的值记为 Q(r) = S · round(r/S + Z) − S·Z,误差:

ε = Q(r) − r

5.1 性质 1:误差有界——|ε| ≤ S/2 的完整证明

这个上界只在"值落在量化范围内、未被裁剪"时成立。证明只需三步,核心是把误差拆成"步长 S × 四舍五入误差"。

第一步:定义中间变量,把 r 用 t 表示。

令 t = r/S + Z,则反解出 r = S·(t − Z)。t 的含义是"r 在整数刻度上的位置"(还没取整)。量化就是对这个位置四舍五入:

q = round(t) = round(r/S + Z)

第二步:把量化误差改写。

ε = Q(r) − r

= S·(round(t) − Z) − S·(t − Z) ← 代回 Q(r) 和 r

= S·( round(t) − t ) ← S·Z 和 −S·Z 抵消

令 δ = round(t) − t(以刻度为单位的四舍五入误差,无量纲),得到:

ε = S · δ

第三步:证明 |δ| ≤ 1/2。

任取实数 t,它一定落在某个整数区间 [n, n+1] 内(n = ⌊t⌋)。四舍五入到最近的整数,结果要么是 n、要么是 n+1,距离分别是 t−n 和 (n+1)−t。而这两段距离之和:

(t − n) + ((n+1) − t) = 1

两段加起来才 1,所以较短的那段一定 ≤ 1/2。四舍五入恰好选的是最近整数,因此:

|δ| = |round(t) − t| = min(t−n, (n+1)−t) ≤ 1/2

代入第二步:

|ε| = S·|δ| ≤ S/2 ∎

直观理解:四舍五入的本质是"往最近的刻度靠",而刻度间距是 1(在 t 的坐标系里),所以最多走半个刻度。换算回实数坐标系,每个刻度代表 S,所以最多错 S/2。

数值例子:S = 0.1, Z = 0, r = 0.53:

t = 0.53/0.1 = 5.3 → round = 5 → δ = 5 − 5.3 = −0.3

ε = S·δ = 0.1 × (−0.3) = −0.03

|ε| = 0.03 ≤ 0.05 = S/2 ✓

⚠️ 一个必须记住的例外:这个上界只对"范围内的值"成立。若 r 超出 [r_min, r_max],它会被 clamp 硬夹到端点,此时误差可以任意大——裁剪误差无界:

总误差 = 量化误差(≤ S/2) + 裁剪误差(无界)

这正是 4.3 节"KL 散度选阈值"要解决的问题:范围太窄 → 裁剪误差大;太宽 → S 大 → 量化误差大。

5.2 性质 2:误差零均值、方差 S²/12——完整推导

方差 S²/12 是在一个前提假设下算出来的:量化误差 ε 在 [−S/2, S/2] 上均匀分布。

前提:为什么 ε 是均匀的。 设 t = r/S + Z,分数部分 frac = t − ⌊t⌋ ∈ [0, 1)。四舍五入后:

frac ∈ [0, 0.5) → round(t) = ⌊t⌋ → δ = round(t)−t = −frac ∈ (−0.5, 0]

frac ∈ [0.5, 1) → round(t) = ⌊t⌋+1 → δ = 1 − frac ∈ (0, 0.5]

关键:如果 frac 在 [0,1) 上均匀分布(输入数据足够"乱"、步长足够小时这是很好的近似),那么 δ 就在 (−0.5, 0.5] 上均匀分布。再乘上常数 S,得到:

ε = S·δ 在 [−S/2, S/2] 上均匀分布

这就是"误差均匀模型"(Bennett 均匀量化模型)。

期望(零均值):均匀分布的 PDF 是 f(ε) = 1/S(区间长度正好是 S,总概率 1):

E[ε] = ∫ ε·f(ε) dε = (1/S) ∫_{−S/2}^{S/2} ε dε = 0

因为 ε 是奇函数,在关于 0 对称的区间上积分必为 0。

方差(逐步积分):Var(ε) = E[ε²] − (E[ε])²,而 E[ε]=0,所以:

Var(ε) = E[ε²] = (1/S) ∫_{−S/2}^{S/2} ε² dε

= (1/S) · [ ε³/3 ]_{−S/2}^{S/2}

= (1/S) · [ (S/2)³/3 − (−S/2)³/3 ]

= (1/S) · [ S³/24 − (−S³/24) ]

= (1/S) · ( S³/12 )

= S²/12

标准差:

σ_ε = S / √12 ≈ 0.289·S

几何直觉:均匀分布在 [−S/2, S/2] 上的随机变量,RMS 值是 S/√12 ≈ 0.289S——比最大值 S/2 = 0.5S 小不少,因为大部分误差落在 0 附近,落在 ±S/2 极端处的是少数。

数字例子:int8、范围 [−1,1]:S = 1/127 ≈ 0.00787。

σ_ε = 0.00787/√12 ≈ 0.00227 ← 误差的"典型大小"

max = S/2 ≈ 0.00394 ← 误差的"最坏情况"

典型误差只有满量程的 0.23%,最坏也才 0.39%——这就是 int8 精度够用的直观来源。

⚠️ 假设什么时候会失效:

- 数据近乎常数(某层激活值都差不多)→ 误差不是随机的,是确定性偏置,方差公式失真。

- 步长太大(低比特,如 2-bit)→ 分数部分不再近似均匀。

- 数据被裁剪(clipping)→ 裁剪误差混进来,不再是均匀模型。

此时 S²/12 只能当粗略估计,实际误差要靠校准(KL 散度 / MSE)来度量。

5.3 性质 3:信噪比与 SQNR = 6.02n dB

信噪比(SNR)= 信号功率 ÷ 噪声功率:

SNR = P_signal / P_noise

"功率"是什么? 对一串数字 x,功率就是均方值 P = E[x²]。关键性质:对零均值信号,功率 = 方差(Var = E[x²] − E[x]²,均值 0 时第二项为 0)。这正是上面 S²/12 能直接当"噪声功率"用的原因。

dB 是什么? dB(分贝)是一个用对数表示"比值"的单位——它不是某个物理量,而是回答"大了多少倍"的压缩写法:

功率比: dB = 10 · log₁₀( P / P₀ )

幅度比: dB = 20 · log₁₀( A / A₀ )

- 是比值,不是绝对值:必须有两个数才能算 dB(工程里"−3 dBm"等写法是约定了参考值 P₀ = 1 毫瓦,本质仍是比值)。

- 为什么 10 或 20:dB 度量功率比,用 10·log;功率正比于幅度的平方,所以幅度比要用 20·log(10·log(A²/A₀²) = 20·log(A/A₀))。

为什么要用对数? 两个理由:

- 数值范围太大,对数压扁:信噪比可从 1 倍到 10 万倍——10× 是 10 dB、100× 是 20 dB、1000× 是 30 dB、63096× 才 48 dB。对数把跨数量级的比值压成好记的小数字。

- 乘法变加法:信号经 5 级放大、每级 10 倍,总增益 10⁵ 倍;用 dB 只需 10+10+10+10+10 = 50 dB。级联系统增益直接相加——这是 dB 在工程里无处不在的根本原因。

几个值得记住的锚点:

量化的信噪比专门叫 SQNR(量化信噪比),噪声就是量化误差 ε。推导分四步:

步骤 1:信号功率(信号在 [−α, α] 均匀分布,区间长 2α,方差 (b−a)²/12):

P_signal = (2α)² / 12 = α² / 3

步骤 2:噪声功率(量化误差在 [−S/2, S/2] 均匀):

P_noise = S² / 12

步骤 3:步长与位宽的关系(n 比特有 2ⁿ 个刻度,铺满总长 2α):

S = 2α / 2ⁿ

步骤 4:相除:

SQNR = (α²/3) / (S²/12) = 4α² / S²

= 4α² / (4α²/2^(2n)) = 2^(2n)

SQNR(dB) = 10·log₁₀(2^(2n)) = 20n·log₁₀(2) = 6.02·n dB

"6 dB/bit" 的含义:6.02·n dB 是总量公式,6 dB/bit 是它的斜率——同一件事的两种说法,就像"车费 = 6.02×n 元"和"单价 6.02 元/公里"。6.02·n 这个式子本身就是"每 bit 6.02 dB × 共 n 个 bit"。

从推导链验证"为什么每 +1 bit 恰好 +6 dB",看 n → n+1 时发生什么:

① 刻度数翻倍:2ⁿ → 2ⁿ⁺¹

② 步长减半: S = 2α/2ⁿ → S/2

③ 噪声功率降到 1/4:S²/12 → (S/2)²/12

④ 信噪比 ×4,换算成 dB:10·log₁₀(4) = 20·log₁₀(2) = 6.02 dB

每 +1 bit 都重复这条链条、都 +6.02 dB——注意第④步就是上面锚点表里的"+6 dB = 幅度 ×2":加 1 bit 让整数范围恰好翻倍。n 次累加就是 6.02·n。用 n 从小到大看更直白:

(口语里常说"6 dB/bit",6 是 6.02 的约整。)

48 dB 意味着什么:把 dB 反解回倍数——dB 的定义是"倍数取对数",反过来就是"对数当指数":

dB = 10·log₁₀(P/P₀) ← 定义

dB/10 = log₁₀(P/P₀) ← 两边 ÷10

P/P₀ = 10^(dB/10) ← 两边作为 10 的指数,对数消掉

即 10^(dB/10) 就是 10·log₁₀(·) 的逆运算。代入 48:

功率比 = 10^(48/10) ≈ 63096 ← 信号功率是噪声的 6 万多倍

幅度比 = 10^(48/20) ≈ 251 ← 信号幅度是噪声的 251 倍

(幅度版除以 20 是因为幅度平方才是功率;一致性检查:251² ≈ 63000 ✓。口算技巧:每 10 dB 添一个零——48 dB = 40 dB + 8 dB ≈ ×10000 × 6.3。)

即 int8 量化的噪声幅度只有满量程的 1/251 ≈ 0.4%。

一个常见变体:若你见过 6.02n + 1.76 dB,那是满幅正弦波的经典 ADC 公式——正弦波功率 = α²/2(峰值 α),比均匀信号 α²/3 大,多出 10·log₁₀(1.5) ≈ 1.76 dB。区别只在信号形状假设,本质是同一套推导;本文假设均匀信号,用 6.02n dB。

5.4 性质 4:误差相加按 √N 增长,而非 N

若 N 个误差彼此独立:

Var(Σ εᵢ) = N · S²/12 ⟹ std(Σ εᵢ) = √N · S / √12

均值恒为 0。正负误差互相抵消,总误差不会线性爆炸——这是"整张网络几乎不掉点"的统计根基。

6. 部署视角:S/Z 的保存与恢复

6.1 S 和 Z 必须单独保存

S、Z 是量化的"字典"——量化后的权重只是一堆整数,没有 S、Z 就翻译不回真实值,所以必须随模型一起保存/下发(S 一般存 FP32 或 FP16,Z 存成和 q 一样的整数类型)。

但保存开销极小,具体看粒度。以一层卷积权重 [512, 512, 3, 3] ≈ 236 万参数 为例:

对比:INT8 本身把权重从 9.4 MB 压到 2.36 MB。S/Z 元数据通常连 1% 都不到。粒度越细字典越大——这是粒度权衡的另一半。

6.2 「恢复」有两种玩法

玩法 A:反量化后算浮点(weights-only 量化)

存的是 int8 权重 → 推理时先 r = S(q−Z) 还原成 float → 做 float 矩阵乘

激活值保持 float。好处:改动小,LLM 推理(如 llama.cpp 的 GGUF)常用这招。缺点:计算本身还是 float,没吃到整数算力的便宜。

玩法 B:整数 GEMM(integer-only,端侧主流)

权重和激活都保持整数 → 直接做整数矩阵乘 Q_w·Q_x → 最后统一乘一次 S_w·S_x

这里不逐个数恢复——还原动作被"推迟"到整个矩阵乘算完之后,只乘一次标量。既省内存,又省算力。下一节推导它的数学。

7. 从标量到矩阵:整数 GEMM

7.1 矩阵形式的仿射映射:J 的作用

W ≈ S_w·(Q_w − Z_w·J) 就是标量公式 r = S(q−Z) 的矩阵版——对 W 的每个元素套一遍 S(q−Z)(per-tensor,共用一套 S、Z),用矩阵符号一次写出来。

问题在于"矩阵元素减标量"不规范,于是引入全 1 矩阵 J(和 W 同形状):

Z_w · J = 每个元素都是 Z_w 的矩阵

这样"减标量 Z_w"就规范地写成了"减矩阵 Z_w·J":

Q_w − Z_w · J = Q_w 的每个元素都减去 Z_w

注意:Z_w · J 里的 · 是标量乘矩阵,不是矩阵乘法。

数值例子(非对称、Z≠0):激活 x 范围 [−0.5, 1.0],uint8,S_x = 1.5/255 ≈ 0.005882,Z_x = 85:

x = [ −0.5 ] Q_x = [ 0 ]

[ 0.0 ] [ 85 ]

[ 1.0 ] [ 255 ]

Q_x − Z_x·J = [ 0 ] [ 85 ] [ −85 ]

[ 85 ] − [ 85 ] = [ 0 ]

[ 255] [ 85 ] [ 170 ]

S_x·(Q_x − Z_x·J) = 0.005882 · [ −85 ] = [ −0.5 ] ✓

[ 0 ] [ 0.0 ] ✓

[ 170 ] [ 1.0 ] ✓

Q_x 里存的 85 代表实数 0,所以要减掉 85(即 Z_x)"归零",再乘步长。

为什么要写成带 J 的形式? 纯粹是为了下面 GEMM 展开的代数方便——有了 J 才能用分配律把"整数部分"和"零点修正项"干净拆开。J 是代数工具,实际代码里就是"每个元素减 Z",不会真的存一个全 1 矩阵。

7.2 GEMM 展开

权重矩阵 W 与输入 x 量化后:

W ≈ S_w · (Q_w − Z_w · J)

x ≈ S_x · (Q_x − Z_x · J)

矩阵乘法用分配律展开:

W·x ≈ S_w · S_x · [ Q_w·Q_x − Z_w·J·Q_x − Z_x·Q_w·J + Z_w·Z_x·J·J ]

- Q_w·Q_x 是纯整数矩阵乘法——唯一昂贵的一步,而整数 MAC 单元比 FP32 便宜约一个数量级。

- 后三项只是"按行/列求和 + 乘标量",可预计算或廉价完成。

- 若两侧均对称(Z_w = Z_x = 0),后三项全部消失:

W·x ≈ S_w · S_x · (Q_w · Q_x)

只剩"一次整数 GEMM + 一次标量乘法"。这正是对称量化在硬件上最受欢迎的原因。

卷积同理:权重核在空间滑动时被共享,零点修正项可以提前算好,不增加每步的额外开销。

7.3 什么是"纯整数 GEMM":一个点积例子

GEMM(GEneral Matrix Multiply,通用矩阵乘法)就是矩阵乘法,神经网络里全连接层和卷积的核心。每个输出元素是一个点积——"相乘再累加"(Multiply-Accumulate,MAC):

C[i,j] = Σ_k A[i,k] × B[k,j]

"纯整数"指:操作数(Q_w、Q_x)都是整数,中间每一次乘、每一次加都是整数运算,浮点不混在循环里。

具体例子:权重 W = [1.0, 0.5, −0.5],输入 x = [2.0, 1.0, −1.0],真实点积:

W·x = 1.0×2.0 + 0.5×1.0 + (−0.5)×(−1.0) = 3.0

量化成 int8(对称,W 范围 [−1,1] → S_w=1/127,x 范围 [−2,2] → S_x=2/127):

Q_w = [127, 64, −64]

Q_x = [127, 64, −64]

纯整数 GEMM 算的是:

Q_w·Q_x = 127×127 + 64×64 + (−64)×(−64) = 24321 ← 全是整数乘加

最后乘一次标量还原:

W·x ≈ S_w·S_x × (Q_w·Q_x) = (1/127 × 2/127) × 24321 ≈ 3.016 ≈ 3.0 ✓

贵的部分(3 次乘 + 3 次加)全在整数域,浮点只用了 1 次(最后乘 S_w·S_x)。

一个工程细节:整数点积累加会溢出 int8,所以累加器(accumulator)通常用 int32,最后再缩回。

7.4 为什么这很重要

- 硬件便宜:整数乘法器/加法器在芯片上的面积和功耗,比浮点单元小一个数量级。

- 数据搬得少:int8 比 float32 少 4 倍字节,内存带宽省 4 倍。

8. 为什么量化后精度损失不大

把前面的数学拼起来,答案有四层:

8.1 误差统计层:小、均匀、互相抵消

- 误差有界且零均值(性质 1、2):单个误差 ≤ S/2,int8 时只有满量程千分之几。

- 误差互相抵消(性质 4):求和时标准差按 √N 增长而非 N,网络各层的成千上万个误差不会叠加成大误差。

8.2 网络鲁棒层:Lipschitz 连续

Lipschitz 连续给"函数变化有多猛"设上限。函数 f 是 L-Lipschitz 连续的,若存在常数 L ≥ 0,对任意 x, y:

|f(x) − f(y)| ≤ L · |x − y|

L 就是 Lipschitz 常数。一句话直觉:输入变化多少,输出最多变化 L 倍,绝不超过。

对可导函数,Lipschitz 常数就是导数的最大绝对值 L = sup |f'(x)|——即"导数处处有界,函数不会比某条直线更陡"。

以 ReLU 为例感受 L=1 的含义:

输入 x 从 1.0 变成 1.1(扰动 0.1)

ReLU(1.1) − ReLU(1.0) = 0.1 → 变化正好等于扰动(不放大)

输入从 −1.0 变成 −0.9(扰动 0.1)

ReLU(−0.9) − ReLU(−1.0) = 0 − 0 = 0 → 变化为 0(比 L·扰动还小)

量化误差 ε 是加到每层输出上的小扰动。扰动逐层传播会不会爆炸,就看每层是否 Lipschitz:

输出扰动 ≤ L · 输入扰动

- 常用激活函数(ReLU/sigmoid/tanh)都是 Lipschitz 的,误差逐层最多线性累积,不会指数爆炸。

- ReLU(L=1)和 sigmoid(L=0.25)不仅不放大,还常常收缩——误差越传越弱。

- 反例:若激活像 x² 那样导数无界,小误差经过几层就可能被疯狂放大,量化就崩了。神经网络常用的激活函数恰好都避开了这一点——这就是量化误差"传不远"的数学保障。

延伸:权重矩阵本身也可以算 Lipschitz 常数(它的最大奇异值)。完整一层 y = φ(Wx+b) 的 Lipsc

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

DeepSeek

幻方量化推出的AI智能助手和开源大模型

蛙蛙写作

AI小说和内容创作工具

讯飞绘文

AI批量原创,多平台矩阵号管理

Laper

AI 原生剧本创作工具

相关免费羊毛

Cohere

每月 1000 次免费请求,专注 RAG

OpenRouter

免费模型库:19 个模型可白嫖

智谱 AI

GLM-4-Flash 模型调用免费

硅基流动

新用户注册送 14 元代金券

同源资讯

你让AI干的活,它可能只干了十分之一——而你根本看不出来

掘金 · 10-12

[Agent Memory / 强化学习] MemPO源码学习笔记 --- (7)--- 混合机制

掘金 · 10-12

我用AI做了一款「三国吃鸡」,开局孔明灯,落地青龙刀 !

掘金 · 10-12

LLM 驱动的自动化测试系列(10):自愈定位——当启发式打分撞上语义理解的天花板

掘金 · 10-12

手机端 Agent 怎么动起来:Kotlin 与 Python 之间的桥

掘金 · 10-12

GitHub 11万star:AI拆软件神器保姆级实操教程

掘金 · 10-12

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 合作与友链 · 关于本站 · 528 款 AI 工具分类导航 · 每日 AI 热门项目与动态