DynamicCache(offloading=True) returns wrong logits after first cache reuse on hybrid Gated-DeltaNet + attention models (e.g. Qwen3.8-27B)

这个报错通常出现在使用 DynamicCache(offloading=True) 加载混合 Gated-DeltaNet + attention 架构模型(如 Qwen3.8-27B)时,第二次 forward 复用缓存后 logits 出现远超 bf16 舍入误差的数值偏差,优先排查 offlo

快速结论:这个报错通常出现在使用 DynamicCache(offloading=True) 加载混合 Gated-DeltaNet + attention 架构模型(如 Qwen3.8-27B)时,第二次 forward 复用缓存后 logits 出现远超 bf16 舍入误差的数值偏差,优先排查 offload/prefetch 路径中的 CUDA stream 同步与内存分配问题。

适用环境:Transformers 5.17.0(及 main 8073e6d)、Linux(Modal container)、Python 3.12、torch 2.14.0 / 2.13、CUDA、单卡 NVIDIA RTX PRO 6000 Blackwell (96GB) 或 H100、bfloat16、attn_implementation="sdpa"、模型 Qwen/Qwen3.8-27B(混合架构,64 层中 48 层 linear_attention、16 层 full_attention)。

最快修复方案:暂无确认的一步修复方案。Issue 中最终定位到 offload/prefetch 路径存在两个 stream 问题(prefetch 未等待 offload 的 non_blocking GPU->CPU 拷贝、预取张量缺少 record_stream),但评论中未见发布可用的官方补丁或版本号,建议关注上游修复进展。

注意事项:该问题并非混合模型独有,dense Qwen/Qwen3-8B 以相同方式发散;它是竞态条件,多次运行数值会变化。仅在 offloading=True 时出现,offloading=False 下两次运行 bit-identical。在官方修复前,可优先尝试关闭缓存 offloading(offloading=False)以规避,但这会占用更多显存,尚未在 Issue 中作为正式方案验证。

问题场景

用户在 Linux 容器环境(Modal container)中使用 Transformers 加载 Qwen/Qwen3.8-27B 这类混合架构模型(config.layer_types 显示 64 层中有 48 层为 linear_attention(Gated DeltaNet 风格循环层),16 层为 full_attention),dtype 为 bfloat16,attn_implementation="sdpa",并启用 DynamicCache(config=model.config, offloading=True) 进行缓存卸载以节省显存。在第一次 forward(prefill)后复用 past_key_values 进行后续 decode 步骤时,logits 开始出现错误。

报错原文

0 max_abs_diff 0.0 argmax_match True
1 max_abs_diff 2.1171875 argmax_match False
2 max_abs_diff 7.625 argmax_match False
3 max_abs_diff 17.4375 argmax_match False

评论中补充的复现数据(main 分支,H100,torch 2.13,3 次运行):

run  step0  step1  step2  step3
1    0.0    2.02   5.91   21.02
2    0.0    6.20   10.45  19.06
3    0.0    6.20   12.31  17.13

原因分析

Issue 讨论最终定位到 offload/prefetch 路径存在两个 CUDA stream 相关问题(评论者原文指出):

  • Cache.prefetch 在 prefetch_stream 上拷贝到 GPU 时,没有等待 offload 在默认 stream 上发出的 non_blocking GPU->CPU 拷贝完成。
  • 预取的 keys/values 在 prefetch_stream 上分配,却在默认 stream 上被消费,且未调用 record_stream,导致其内存在 torch.cat 读取之前可能已被下一次 prefetch 复用。

因此这是竞态条件,导每次运行数值不同。此外,该问题并非混合模型独有:linear-attention 的 conv/recurrent states 从不离开 GPU,只有 16 个 full-attention 的 DynamicLayer 被卸载,dense Qwen/Qwen3-8B 也会同样发散。第一步 prefill 完全一致(max abs diff 0.0),说明单次 forward 正确,问题只在缓存复用、真正需要将 offload 层状态移回时出现。

环境排查

  • 确认 Transformers 版本:5.17.0 及 main 分支(8073e6d)均报告可复现。
  • 确认 torch 版本:2.14.0 / 2.13。
  • 确认 CUDA 与单卡 GPU 型号(RTX PRO 6000 Blackwell 96GB、H100)。
  • 确认 Python 版本(3.12)。
  • 确认模型 dtype(bfloat16)与 attn_implementation(sdpa)。
  • 确认是否启用 DynamicCache(offloading=True),并用相同脚本对比 offloading=False。
  • 确认模型是否为混合架构或 dense(该问题在两类模型上均可出现)。

解决步骤

  1. 使用 Issue 中提供的复现脚本,分别以 offloading=False 与 offloading=True 运行 4 步 greedy decode,记录每步 max_abs_diff 与 argmax_match。
  2. 若确认 step 0 完全一致、step 1 起发散且多次运行数值变化,即可判定为 offload/prefetch 路径的竞态问题,而非普通 bf16 舍入噪声。
  3. 关注上游修复(Cache.prefetch 与 offload 的 stream 同步及 record_stream 使用)。评论中 @Cyrilvallez 被点名处理该缓存问题,但截至 Issue 关闭未见发布确认补丁。
  4. 作为规避手段,可优先尝试关闭缓存 offloading(offloading=False),确认数值是否恢复一致;该规避在 Issue 中未作为正式修复验证,且会增加显存占用。

验证方法

用复现脚本对比 offloading=False 与 offloading=True:若修复生效,所有步骤的 max_abs_diff 应回到 bf16 舍入量级(约 1e-2 或更低)且 argmax_match 为 True;同时多次运行应保持 bit-identical 或仅有可忽略的舍入差异,不再出现 run-to-run 变化。

参考来源

huggingface/transformers #48947

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29016

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注