下面把 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 张量。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐