跳到主要内容
TileLang 系统教程

tilelang-tutorial.huandu.me

第 27 章 低精度、量化与数值正确性

/code/27-low-precision-quantization/

02-quant-dequant/README.md2272 字节
# 示例 02:五种量化方案的往返误差

## 目的

把「量化误差有多大」从定性说法变成五个可比较的数字。同一份 256×1024 的数据
(行幅度服从对数正态,模拟不同通道的量级差)分别做:

| 方案 | scale 的粒度 | 误差来源 |
| --- | --- | --- |
| int8 per-tensor | 整张权重一个 | 均匀量化,`absmax/(2·127)` |
| int8 per-channel | 每个输出行一个 | 同上,但 absmax 小得多 |
| int4 per-group-64 | 每 64 个 K 元素一个 | 均匀量化,`absmax/(2·7)` |
| MXFP8(e4m3, blk 32) | 每 32 个 K 元素一个 E8M0 | fp8 码本的相对间距 `2^-4` |
| MXFP4(e2m1, blk 32) | 每 32 个 K 元素一个 E8M0 | fp4 码本的相对间距 `2^-2` |

## 运行前提

Python 3.9+ 与 numpy。**不需要 GPU / NPU**。

## 运行命令

```bash
python3 quant_dequant.py
python3 quant_dequant.py --rows 512 --cols 2048 --seed 20261001
```

## 期望输出

作者在写作机上的实际输出(2026-10-01,Python 3.11.7 + numpy 1.24.1,默认参数):

```text
数据:256 x 1024,行幅度服从对数正态(sigma=1.2),seed=20261001
数据 absmax = 86.5222,全局 RMS = 3.8745

方案                      相对 L2         逐元素 max err       SQNR (dB)   解析误差上界
int8 per-tensor           4.951e-02       3.406e-01           26.1          相对 absmax: 1/(2*127) = 3.9e-3
int8 per-channel          7.674e-03       3.393e-01           42.3          相对 absmax: 1/(2*127) = 3.9e-3
int4 per-group-64         1.083e-01       6.049e+00           19.3          相对 absmax: 1/(2*7) = 7.1e-2
MXFP8 (e4m3, blk 32)      2.685e-02       3.808e+00           31.4          码本相对间距: 2^-4 = 6.3e-2
MXFP4 (e2m1, blk 32)      1.177e-01       1.586e+01           18.6          码本相对间距: 2^-2 = 2.5e-1
```

## 判据:哪些误差是「正常的」

四条可以直接拿去用的结论:

1. **int8 ≈ bf16**:`1/(2·127) = 3.9e-3` 与 bf16 的 `u = 3.9e-3` 同量级。
   权重量化到 int8 在精度上不比 bf16 差,收益主要来自带宽与容量。
2. **int4 ≈ fp8 e4m3**:`7.1e-2` 对 `6.3e-2`。8 位浮点与 4 位定点能表达差不多的
   相对精度,区别在动态范围(fp8 有指数,int4 没有)。
3. **误差上界是相对 absmax 的**。这份数据 per-tensor 的 absmax 是 86.5,
   而 per-row absmax 的中位数只有 3.44,差 25 倍——所以 per-tensor 的相对 L2
   (4.95e-2)反而比 3.9e-3 大一个数量级。**粒度决定误差,位宽只是其中一个因素。**
4. SQNR 那一列给的是工程直觉:int8 约 26–42 dB,int4 与 MXFP4 约 19 dB。
   如果对拍时看到 20 dB 附近的偏差,先怀疑量化本身,而不是 kernel。

## 注意事项

* 这里的 e4m3/e2m1 舍入用「最近码本值」,平局取较小值,与硬件的 round-to-nearest-even
  不是逐位一致;这会改变个别样本,不改变量级。
* E8M0 scale 用「向上取到 2 的幂」的规则,与锁定版本 `ceil_to_ue8m0` 一致;
  但这里没有实现源码里 `clamp_min(1e-4)` 与 `clamp(1, 254)` 之外的边界处理细节。
* 这份数据的分布(对数正态行幅度)是刻意选的:纯 i.i.d. 正态数据下三种整数粒度的
  差距会小到看不出来,掩盖了「粒度才是主导因素」这个结论。

## 作者实际做到的验证级别

**CPU 逻辑验证**:在 macOS (Apple Silicon)、Python 3.11.7 + numpy 1.24.1 上实际运行过,
退出码 0。未在任何加速器上运行过任何 kernel。

← 浏览本章全部代码