
Different results for PPDocLayoutV3 on CPU and CUDA
快速结论:该问题是由于 PyTorch 在 CPU 和 CUDA 设备上对浮点数舍入行为存在未定义差异,导致 _cached_generate_anchors 函数生成的二值掩码(valid_mask)在边界像素上出现不一致。优先排查是否在特定尺寸(如 50×50)下触发,并可尝试使用 torch.linspace 替代 torch.arange 后归一化的构造方式作为修复方案。
问题场景
用户在运行 PPDocLayoutV3 模型(通过 Hugging Face Transformers 库)时,发现模型在 CPU 和 CUDA 设备上输出结果不一致,具体表现为 _cached_generate_anchors 函数生成的 anchor 掩码存在差异。该函数在 transformers 仓库中被重复定义了至少 7 次,适用于多种 DETR 风格的模型实现。
报错原文
ERROR: Different masks on CPU and GPU
(这是用户自行编写的检测脚本输出的错误信息,用于判断掩码是否一致)
原因分析
问题根源在于 PyTorch 在 CPU 和 CUDA 上对浮点数除法的舍入行为不一致,这是一个已知的 PyTorch 底层行为差异(参考 pytorch/pytorch #186618)。在 _cached_generate_anchors 函数中,通过 torch.arange(..., device=device).float() 生成坐标网格后,执行 grid_xy[..., 0] /= width 和 grid_xy[..., 1] /= height 归一化时,边界像素(如索引 0 和 49)的浮点计算在 CPU 和 CUDA 上可能产生不同的截断或舍入,进而导致 valid_mask = ((anchors > eps) * (anchors < 1 - eps)) 在边界条件判断上产生差异。
对于 50×50 的特征图,观察到了 97 个不匹配的像素,集中在边界区域。该行为在多次运行中稳定复现,表明并非随机噪声。当前尚不清楚哪种设备的行为是设计意图——有意见认为 CPU 掩码因对称性更可能是正确行为,但未经原始模型验证。
⚠️ 注意:仅修复数值一致性(如调整容忍度 eps)而不了解原始模型的预期行为,可能只是将问题转移到其他地方。需要 PP-DocLayoutV3 的原作者或开发者提供在原始硬件上的基准输出(logits、valid_mask 等)以确认正确的实现。
环境排查
- 确认 Python 版本
- 确认 PyTorch 版本(已知存在底层浮点舍入差异,涉及
torch.arange+ 浮点运算在 CPU/CUDA 上的行为) - 确认 CUDA 版本及显卡型号
- 确认 transformers 库版本(PPDocLayoutV3 相关实现)
- 确认是否在特定特征图尺寸(如 50×50)下复现,其他尺寸可能不触发此问题
解决步骤
以下步骤基于 Issue 讨论中提出的替代构造方案,可优先尝试:
- 将原始的
_cached_generate_anchors实现中通过torch.arange生成坐标后除以宽高的方式,替换为直接使用torch.linspace构造归一化坐标网格,避免浮点除法引入的舍入差异。 - 替换后的核心代码示例(来自 Issue 讨论中的解决方案):
import torch
def _cached_generate_anchors(device):
width = height = 50
# 直接使用 linspace 构造归一化坐标,而不是 arange + 除法
anchors = torch.stack(
torch.meshgrid(
torch.linspace(0.5 / height, 1. - 0.5 / height, height, device=device).float(),
torch.linspace(0.5 / width, 1. - 0.5 / width, width, device=device).float(),
indexing="ij",
), -1
).unsqueeze(0)
eps = 1e-2
valid_mask = ((anchors > eps) * (anchors < 1 - eps)).all(-1, keepdim=True)
return valid_mask
注意:该修改已在 Issue 讨论中被验证为能在 CPU 和 CUDA 上生成一致的掩码,且与原始 CPU 行为一致。但该方案仅是数值一致性修复,尚需原始模型开发者确认这是否是 正确 的行为。
_cached_generate_anchors 的重复实现进行统一替换(仓库中至少 7 处重复)。验证方法
运行 Issue 中提供的复现脚本,确认 CPU 和 CUDA 生成的 valid_mask 在 .equal() 比较下不再输出 “ERROR: Different masks on CPU and GPU” 信息。同时建议提取 PPDocLayoutV3 模型的完整推理输出(如 logits、预测框等),对比修改前后的整体差异,确保修复未影响模型的核心预测行为(参考 Issue 中关于需要原始基准输出的讨论)。

![[Bug]: Router context-window checks are silently skipped for all Responses API calls](https://www.chat-gpts.plus/wp-content/uploads/2026/07/33686-73cf43b2-768x403.jpg)
![[Bug]: Nodeenv installation is triggered on startup](https://www.chat-gpts.plus/wp-content/uploads/2026/07/24554-2ca2fb64-768x403.jpg)
