保留第一次失败及其上下文
本指南从一次成功的启动之后开始:PyTorch 能看到设备,但你的应用在某个 batch 或运算符上失败。如果连一个很小的计算都无法运行,就回到最初的诊断。否则,请保留第一个错误、迭代编号以及最后完成的步骤。第一次失败之后接连出现的消息,描述的可能是它的后果,而不是多个彼此独立的原因。
记录代码版本、Python 和 PyTorch 版本、后端、数值类型以及输入形状。对于数据,优先使用内部标识符和维度,而不是完整复制内容。找出问题批次与其他批次的差异:长度、缺失的目标、最后一个不完整的批次、数据增强或很少使用的分支。该档案让你无需重跑整个训练即可复现该案例。
在异步执行下定位出错的启动
在 CUDA 上,操作会被排队,可能在 Python 函数返回后才完成。因此,在拷贝到 CPU 或读取标量时抛出的错误,可能来自之前的计算。PyTorch 文档解释了这种异步执行。堆栈跟踪所指的行是一个需要检查的观测点,并不总是根本原因。
若要在 NVIDIA/CUDA 上进行简短的复现,请单独使用 CUDA_LAUNCH_BLOCKING=1 进行启动。该选项使调用同步,能让错误更接近其源头。它用于诊断,而非计时。你也可以在主要步骤之间临时插入同步,以缩小可疑区间。之后请移除这些插桩:它们会改变常规的调度顺序。
以下命令仅作教学用途,并未实际执行。它假定使用 POSIX 终端且存在 train.py 脚本。该赋值仅对本次启动生效;请根据你的 shell 调整语法。不要将这个 NVIDIA 变量推广到 ROCm 技术栈。
CUDA_LAUNCH_BLOCKING=1 python train.py读懂一类错误,不要急于下结论
报错信息缩小了搜索范围,但它不能替代可复现的案例。域外索引、张量位于错误设备以及无法完成的分配,各自需要不同的检查。请保持对无效数据、算子契约和二进制环境之间的区分。同时更改批次、精度和库会抹掉这种区分。
在设备上执行断言后,不要尝试在同一进程中继续相同的训练。NVIDIA 指出 cudaErrorAssert 会使现有分配失效,必须结束并重启进程。在 notebook 中,这意味着在修正后的复现之前重启内核。不过,重启既不能修正错误的目标,也不能修正无效的索引。
左右滚动表格即可查看所有列。| 观察到的线索 | 首要检查 | 应避免的结论 |
|---|---|---|
| device-side assert | 索引、目标与算子条件 | GPU 一定有故障 |
| 显存不足 | 形状、张量生命周期、进程内存 | 任何 CUDA 错误都是 VRAM 不足 |
| 算子或内核不可用 | 版本、扩展、后端与 dtype | 盲目重装所有东西 |
| 设备不一致 | 模型和每个输入的放置位置 | 在不了解来源的情况下添加拷贝 |
实例演示:四分类问题中出现了类别 4
设想一个教学用的分类器,其输出有四列。它的类别索引为 0 到 3。一个包含数值 4 的标注文件可能表明采用了 1 到 4 的编码,或存在一个意料之外的第五个类别。仅仅增大输出尺寸会掩盖一项约束,却无法解决标注含义的问题。
下面提出的检查在将目标转移到 CPU 之前进行。它并未实际执行。它演示了 CrossEntropyLoss 对 long 类型类别索引的契约,并明确选择了 ignore_index=-100。它不涵盖由概率分布构成的目标。在此场景中,[0, 2, 4] 应被拒绝;该预期结果是由规则推导得出的,并非作为实测结果呈现。
然后修正数据准备中的映射,并检查它与类别名称之间的双射关系。在不确定所有来源是否采用同一约定之前,不要到处减 1。将出错案例添加到一个随项目一起保留的小型验证集中。
import torch
classes = 4
ignore_index = -100
target = torch.tensor([0, 2, 4], dtype=torch.long)
if target.ndim != 1 or target.dtype != torch.long:
raise ValueError("Cibles : vecteur d’indices attendu")
valid = target[target != ignore_index]
if valid.numel() == 0:
raise ValueError("Aucune cible exploitable dans ce batch")
if bool(((valid < 0) | (valid >= classes)).any()):
raise ValueError("Indice de classe hors domaine")在不删除触发条件的前提下缩小程序
首先用相同的变换重放单个输入或单个批次。移除远程跟踪、结果写入以及与故障无关的分支。保留疑似出错的 dtype、形状和算子。如果错误依赖某个特定的长度或内存布局,随意取一个小张量可能无法再复现它。
每次只比较一处修改:关闭可选扩展、参考算子、常规精度,或在 CPU 上存在时用 CPU 执行同一运算。CPU 上成功只是线索,并不等于 CUDA 验证通过。对于自定义函数,还要记录关于 strides、连续性和大小的假设。寻找一个修复前失败、修复后成功的示例,并用输出检查来验证,而不只是没有抛出异常。
在初始范围内验证修复正确性
可接受的修复必须通过最小用例、相邻用例以及原始流程中具有代表性的一部分。尤其要重放最后一个批次、一个短输入、一个长输入以及映射的边界值。确认被拒绝的元素可被识别,且处理的输入数量仍符合预期。静默忽略异常可能把一次可见的崩溃变成不完整的结果。
关闭诊断模式,从全新进程重新开始,并用正常配置确认行为。保留故障原因、所应用的修改以及非回归检查。如果你此前中断了训练,请从错误发生前经过验证的一致检查点重新开始;仅凭故障期间写入的文件并不足以保证其可恢复。
知道何时寻求更有针对性的分析
如果同一个最小用例在输入有效时仍失败,请准备一份精确的请求:运算、形状、类型、后端、版本以及第一条相关信息。移除个人标识符和不必要的路径。二进制扩展可能需要自己的兼容性矩阵;PyTorch 的一般支持并不会自动验证该扩展。
在 ROCm 上,PyTorch 仍保留 torch.cuda 接口和 cuda 设备名称。请检查 torch.version.hip 以在应用 NVIDIA 流程之前识别这套技术栈。信息、工具和诊断选项可能有所不同。本文所述任何检查都不能证明准备好的环境与 Kernodeck 的某个方案兼容;请在选择 GPU 之前用这些标准来明确你的需求。