Qwen3.8-Flash-Next 的 ngram FP8 量化损失:512 亿权重全量扫描
逐元素比较 Qwen3.8-Flash-Next 的 BF16 与官方 FP8 ngram embedding,从余弦、相对 L2、行级长尾、分片和 head 维度判断量化损失。
“FP8 版本能省一半 ngram 权重空间”很容易确认,但“它到底损失了多少”不能只看文件大小,也不能随便抽一个小矩阵下结论。这次我直接比较 Hugging Face 缓存中的 Qwen/Qwen3.8-Flash-Next BF16 checkpoint 与官方 Qwen/Qwen3.8-Flash-Next-FP8,把 ngram embedding 的 128 个分片全部扫了一遍。
最终覆盖 320,001,446 个有效 embedding 行、51,200,231,360 个权重元素。结论是:FP8 的方向保持很好,相对 L2 约 2.66%,而且误差在分片和 head 之间高度均匀;从权重空间没有看到局部量化崩坏。但这仍不是“端到端效果无损”的证明。
先确认 ngram 实际用了什么量化尺度
模型配置中的普通 FP8 线性层声明了 128×128 block scale,但不能据此推断 ngram embedding 也使用同样规则。检查 safetensors 索引与张量 header 后,可以看到:
- ngram embedding 有 128 个分片;
- 每片形状为
2,500,012 × 160; - BF16 checkpoint 的分片 dtype 为
BF16; - FP8 checkpoint 的分片 dtype 为
F8_E4M3,对应 PyTorch/safetensors 的 E4M3FN; - 整个 ngram embedding 共享一个 BF16
weight_scale ≈ 1.9931793e-4。
因此反量化公式是 Q = decode_E4M3FN(code) × weight_scale。如果机械套用配置里的 block scale,比较从起点就错了。
两个 checkpoint 的 ngram_heads_offsets 和 ngram_heads_vocab_sizes 完全一致。metadata 给出的有效行数比 128 个物理分片的总容量少 90 行,因此扫描时排除了末尾 padding,避免零填充影响极值与行级统计。
余弦与相对 L2 回答不同问题
我同时计算了几类指标:
- 全局余弦:展平所有有效权重后比较方向;
- L2 与相对 L2:
||Q-W||₂以及||Q-W||₂ / ||W||₂; - SQNR:
-20·log10(relative_l2); - RMSE、MAE、最大绝对误差与范数偏差;
- 每个 160 维 embedding 行的独立余弦和相对 L2;
- 逐分片、逐 ngram head 的完整聚合;
- FP8 饱和、NaN、下溢到零和符号翻转。
绝对 L2 会随着参数数量增长。在 512 亿元素上得到 45.99,单独看几乎没有可比性;相对 L2、RMSE 和行级余弦更适合解释量化噪声。
全量结果:方向很稳,误差并非为零
| 指标 | 结果 |
|---|---|
| 全局余弦相似度 | 0.999645413 |
| 余弦距离 | 354.59 ppm |
| 相对 L2 | 2.663625% |
| SQNR | 31.49 dB |
| RMSE | 0.00020324 |
| MAE | 0.00013715 |
| 最大绝对误差 | 0.00323486 |
| 量化后/原始范数比 | 1.00030934 |
余弦接近 1,说明整体方向保持得很好;相对 L2 约 2.66%,说明它仍是可测量的量化噪声,不能称为数值无损。量化后的整体范数只高约 0.031%,没有明显的全局幅值漂移。
存储 scale 与“固定当前 FP8 码值后、令全局 L2 最小”的事后最优单一 scale 只差约 0.066%。这说明结果不是由明显错误的 scale 造成。由 BF16 全局最大绝对值反推的 scale 与 checkpoint scale 也非常接近。
128 个分片没有异常孤岛
最差分片是 shard 065,相对 L2 为 2.664088%;最好分片是 shard 036,相对 L2 为 2.663295%。二者只差 0.000792 个百分点。
16 个 ngram head 的差异更小:相对 L2 极差约 0.000268 个百分点。也就是说,整体误差不是某个分片或某个 head 拉高的,而更像均匀分布在全表中的量化噪声。
这直接影响回退策略。如果只能把一部分权重保留为 BF16,当前证据不支持按完整 shard 或 head 选择;更合理的方向是结合真实访问频率和行级异常定位具体 vocab/hash 桶。
行级长尾也没有出现崩坏
全量 320,001,446 行的余弦均值为 0.999651742,最小值为 0.999289005,没有任何一行低于 0.999。低于 0.9995 的行约占 0.2804%。
每分片等距抽取 4,096 行后,行余弦 P0.1 为 0.999480546;行相对 L2 的中位数为 2.6561%,P99 为 3.0996%,全量最大值为 3.8218%。抽样分位数与全量最小值、均值和阈值计数相互约束,未看到少量坏行被全局余弦掩盖。
FP8 编码本身也很健康:
- NaN 编码为 0;
- 非零值符号翻转为 0;
- 1,054,989 个元素下溢到零,占约 0.00206%;
- 饱和到最大有限 E4M3FN 码值的元素只有 1 个。
下溢数量看起来超过百万,但分母是 512 亿;比例和行级指标都表明它没有形成明显局部损坏。
不依赖 PyTorch,也要验证 FP8 解码
扫描脚本直接 mmap safetensors,用 NumPy 将 BF16 位模式还原为 FP32,并通过 256 项查表解码 E4M3FN,因此不需要 GPU,也不要求安装 PyTorch。
自写查表随后与 ml_dtypes.float8_e4m3fn 做了独立对照:254 个有限编码逐值完全一致,最大差异为 0,两个 NaN 编码的掩码也一致。
数值回卷还检查了:
relative_l2² = 1 + norm_ratio² - 2·norm_ratio·cosine,误差约1.11e-15;- SQNR 由相对 L2 独立重算后完全一致;
- 128 个分片与 16 个 head 的行数、元素数和误差平方和都能回卷到总体。
这些检查不能证明模型质量,却能排除 dtype、scale、padding、分片覆盖和聚合公式错误。
权重误差不能替代端到端验收
当前结果只回答 checkpoint 权重空间的问题。真实推理中,ngram 桶访问频率并不均匀,PLE 后续的卷积、投影和门控也可能抑制或放大噪声。
下一步更有价值的验证是固定输入,同时运行 BF16 与 FP8:
- 记录 ngram/PLE 输出的逐 token 余弦、相对 L2 和最大绝对误差;
- 比较 logits KL、top-k 重合率和固定种子输出一致性;
- 在真实语料上按 ngram 命中频率加权,而不是让冷门桶与高频桶权重相同;
- 最后再看任务指标、吞吐和显存收益。
从这次全量权重扫描看,官方 FP8 ngram 的量化误差稳定、均匀,没有局部灾难性退化;是否值得部署,则必须把这 2.66% 的权重相对 L2 放回真实执行路径里验证。