FP4 / NVFP4 从数学角度统一总结
下面把 FP4 / NVFP4 从数学角度统一总结一下。
1. FP4 是什么?
FP4 是 4-bit floating point,常见形式是 E2M1:
1 bit sign
2 bit exponent
1 bit mantissa
也就是:
s e1 e0 m
其中:
s = 符号位
e = 指数位
m = 尾数位
FP4/E2M1 的裸数值集合是:
Q_FP4 = {0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
也就是说,FP4 不是连续数,也不是等间距整数,而是一组 非均匀浮点格点。
正数侧是:
0, 0.5, 1, 1.5, 2, 3, 4, 6
负数侧对称:
-0, -0.5, -1, -1.5, -2, -3, -4, -6
2. FP4 和 INT4 的数学区别
INT4 通常是:
q_int4 ∈ {-7, -6, ..., 0, ..., 6, 7}
它是等间距的。
FP4 是:
q_fp4 ∈ {0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
它是非等间距的。
对比:
INT4:
-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7
FP4:
-6, -4, -3, -2, -1.5, -1, -0.5, 0,
0.5, 1, 1.5, 2, 3, 4, 6
所以本质区别是:
INT4 = 线性整数格点
FP4 = 浮点非均匀格点
3. NVFP4 是什么?
NVFP4 不是单纯的裸 FP4,而是:
NVFP4 = FP4 E2M1 value + block scale + global scale
数学表达是:
x_hat_{b,i} = q_{b,i}^{E2M1} × s_b × g
其中:
x_hat_{b,i} = 反量化后的近似真实值
q_{b,i}^{E2M1} = FP4 裸值
s_b = 第 b 个 block 的 block scale
g = 整个 tensor 的 global scale
通常可以把:
α_b = s_b × g
合并起来理解:
x_hat_{b,i} = q_{b,i} × α_b
所以 NVFP4 的真实数值列表不是固定的,它取决于当前 block 的 scale。
4. NVFP4 的真实数值列表
裸 FP4 的固定列表是:
{0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
但 NVFP4 真实值是:
{0, ±0.5α, ±1α, ±1.5α, ±2α, ±3α, ±4α, ±6α}
其中:
α = s_block × s_global
所以 scale 不同,同一个 FP4 编码代表的真实值也不同。
5. 例子 1:scale = 0.2
假设:
s_block = 0.2
s_global = 1.0
α = 0.2
裸 FP4:
{0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
乘以 0.2 后,真实可表示值变成:
{0, ±0.1, ±0.2, ±0.3, ±0.4, ±0.6, ±0.8, ±1.2}
比如原始值:
x = 0.47
先归一化:
0.47 / 0.2 = 2.35
FP4 裸值里最接近 2.35 的是:
2
所以量化为:
q_fp4 = 2
反量化:
x_hat = 2 × 0.2 = 0.4
误差:
error = 0.4 - 0.47 = -0.07
6. 例子 2:scale = 20
假设另一个 block:
s_block = 20
s_global = 1.0
α = 20
真实可表示值变成:
{0, ±10, ±20, ±30, ±40, ±60, ±80, ±120}
所以同样的裸 FP4 编码 6:
scale = 0.2 时:
6 × 0.2 = 1.2
scale = 20 时:
6 × 20 = 120
这说明:
NVFP4 的真实数值列表由 block scale 决定。
7. scale 如何决定?
最简单的办法是让当前 block 的最大绝对值对齐到 FP4 的最大裸值 6。
假设一个 block:
x = [0.12, 0.28, 0.47, 0.93, -0.15, -0.72]
最大绝对值:
amax = max(|x|) = 0.93
FP4 最大裸值:
qmax = 6
所以最简单的 scale 是:
α = amax / 6 = 0.93 / 6 = 0.155
也就是:
scale ≈ 当前 block 最大绝对值 / 6
8. 例子 3:用 max/6 决定 scale
还是这个 block:
x = [0.12, 0.28, 0.47, 0.93, -0.15, -0.72]
scale:
α = 0.155
归一化:
x / α
= [0.774, 1.806, 3.032, 6.000, -0.968, -4.645]
FP4 裸值列表:
{0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
逐个 round:
| 原始值 x | x / 0.155 | 最近 FP4 q | 反量化 q × 0.155 |
|---|---|---|---|
| 0.12 | 0.774 | 1 | 0.155 |
| 0.28 | 1.806 | 2 | 0.310 |
| 0.47 | 3.032 | 3 | 0.465 |
| 0.93 | 6.000 | 6 | 0.930 |
| -0.15 | -0.968 | -1 | -0.155 |
| -0.72 | -4.645 | -4 | -0.620 |
所以:
x_hat = [0.155, 0.310, 0.465, 0.930, -0.155, -0.620]
误差:
error = x_hat - x
= [0.035, 0.030, -0.005, 0.000, -0.005, 0.100]
可以看到,最大值 0.93 被精确覆盖,因为 scale 是按最大值对齐的。
9. 为什么不能总是简单用 max/6?
因为 outlier 会破坏 scale。
比如:
x = [0.1, 0.2, 0.3, 20.0]
如果用:
scale = 20 / 6 = 3.33
那么小值归一化后:
0.1 / 3.33 ≈ 0.03 → 0
0.2 / 3.33 ≈ 0.06 → 0
0.3 / 3.33 ≈ 0.09 → 0
小值全变成 0。
所以工程上 scale 不一定严格等于 max/6,可能使用:
percentile scale
clipping scale
calibration scale
learned scale
QAT 训练出来的 scale
误差最小化 scale
更一般的目标是选择一个 scale,使重构误差最小:
Loss(s) = Σ_i (x_i - s × Round_FP4(x_i / s))²
也就是说:
scale 的真正目标不是单纯覆盖最大值,
而是让整个 block 的量化重构误差尽可能小。
10. 量化和反量化公式
给定原始值:
x_{b,i}
选择 scale:
α_b = s_b × g
量化:
q_{b,i} = Round_E2M1(x_{b,i} / α_b)
反量化:
x_hat_{b,i} = q_{b,i} × α_b
其中:
q_{b,i} ∈ {0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
所以完整过程是:
原始浮点值
↓ 除以 scale
归一化到 FP4 裸值范围
↓ Round 到最近 FP4 格点
FP4 编码值
↓ 乘回 scale
反量化近似值
11. 最核心总结
FP4:
q_fp4 ∈ {0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6}
它是 4-bit 的非均匀浮点格点。
NVFP4:
x_hat_{b,i} = q_{b,i}^{E2M1} × s_b × g
也就是:
真实值 ≈ FP4裸值 × block scale × global scale
scale 的基本选择:
s_block × s_global ≈ max(|x_block|) / 6
但工程上会用 calibration、clipping、QAT 或误差最小化来改进。
一句话:
FP4 提供一组极少的非均匀浮点格点;NVFP4 通过 block scale 和 global scale,把这些格点缩放到每个 block 的真实数值范围中,从而用 4 bit 近似表达 FP16/BF16/FP32 张量。
更多推荐


所有评论(0)