文章

Qwen3.8-Flash-Next 的 ngram FP8 量化损失:512 亿权重全量扫描

逐元素比较 Qwen3.8-Flash-Next 的 BF16 与官方 FP8 ngram embedding,从余弦、相对 L2、行级长尾、分片和 head 维度判断量化损失。

Qwen3.8-Flash-Next 的 ngram FP8 量化损失:512 亿权重全量扫描

“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_offsetsngram_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
相对 L22.663625%
SQNR31.49 dB
RMSE0.00020324
MAE0.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:

  1. 记录 ngram/PLE 输出的逐 token 余弦、相对 L2 和最大绝对误差;
  2. 比较 logits KL、top-k 重合率和固定种子输出一致性;
  3. 在真实语料上按 ngram 命中频率加权,而不是让冷门桶与高频桶权重相同;
  4. 最后再看任务指标、吞吐和显存收益。

从这次全量权重扫描看,官方 FP8 ngram 的量化误差稳定、均匀,没有局部灾难性退化;是否值得部署,则必须把这 2.66% 的权重相对 L2 放回真实执行路径里验证。

项目地址:cyijun/qwen38-ngram-fp8-analysis

交互式技术报告:Qwen3.8-Flash-Next ngram FP8 量化损失

本文由作者按照 CC BY 4.0 进行授权