DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

PyTorch 转 NumPy 报 requires grad,该在什么地方 detach

PyTorch 张量转 NumPy 报 requires grad 时,区分导出与训练损失。说明 detach、CPU 转移、共享内存及 copy,并保留训练计算图。

训练过程中要绘图或计算不参与训练的指标,调用 prediction.numpy() 时出现:

RuntimeError: Can't call numpy() on Tensor that requires grad.
Use tensor.detach().numpy() instead.

这里先要确定转换结果的用途。只给绘图、导出或独立指标使用,可以从计算图中分离一份张量视图;如果这个计算仍是训练损失的一部分,直接加 detach() 会切断梯度,错误虽消失,训练含义却变了。

下面按 PyTorch 2.8 的转换接口说明,示例使用 NumPy 支持的普通稠密浮点张量。稀疏布局、NumPy 不支持的数据类型和复数共轭视图需要另行处理。

导出结果时保留原训练张量

Tensor.numpy() 默认要求张量位于 CPU、不需要梯度,并且数据类型与布局能被 NumPy 接收。detach() 得到一个不再连接当前计算图的张量,但不会自动把 CUDA 数据搬到 CPU。

下面是转换示例,prediction 表示已有的模型输出。导出数组与训练输出使用不同变量:

exported = prediction.detach().cpu().numpy().copy()

这条转换链有三个需要区分的动作:detach() 分离计算图,cpu() 把数据放到 CPU,numpy() 转为数组。最后的 copy() 用来保留独立快照;若只临时只读使用、并明确接受共享内存,可不做这个复制。

不要写成 prediction = prediction.detach().cpu().numpy() 后,又把 prediction 交给原来的训练损失。原 Tensor 应继续走训练路径,导出的数组走绘图或保存路径。

如果执行过 prediction.detach() 却没有接住返回值,随后仍调用原张量的 .numpy(),也会继续报错。detach() 不是在原对象上关闭梯度;也不要用 .data 代替这里明确的分离操作。

CPU 转换可能共享内存

已经位于 CPU 的张量经过 detach().numpy() 后,数组可能与原张量共享存储。detach() 本身也共享原存储,它不是一次数据复制。

下面是局部示例,用于说明为什么保存历史结果时要考虑 .copy():

import torch

value = torch.tensor([1.0, 2.0], requires_grad=True)
view = value.detach().numpy()
snapshot = view.copy()
view[0] = 9.0

这里修改 view 会反映到 value 的存储,snapshot 则保留复制时的数值。训练期间从 NumPy 一侧改写共享数据,还可能干扰梯度计算或正确性检查。仅用于观察时保持只读;需要长期留存、允许后续修改时,采用独立副本。

PyTorch 2.8 支持 numpy(force=True),等价于先分离、移到 CPU 并处理共轭和负位,再转换。它能缩短写法,但不承诺结果总是独立副本,也不让 NumPy 参与自动求导。老版本不一定接受这个参数,因此常规转换链更容易对照各步含义。

损失还要反向传播,就留在 PyTorch 中计算

下面是标量损失与日志值分开的示例。prediction、target 是形状与设备匹配的已有张量,prediction 来自需要训练的模型:

loss = torch.mean((prediction - target) ** 2)
loss.backward()
logged_loss = loss.item()

损失由 PyTorch 运算产生,backward() 能沿原图计算梯度。.item() 仅取得单元素张量的 Python 数值,适合记录日志,不可微分,也不能用于多元素输出的整体导出。

如果先将模型输出转换成 NumPy,使用 NumPy 函数算损失,再用 torch.tensor() 包回结果,新的 Tensor 不会自动找回已经离开的计算图。需要梯度的那一段应使用对应的 PyTorch 运算;没有直接等价算子的业务计算,需另行设计可微实现或正确的自定义求导,不能靠反复包装对象补回梯度。

纯推理阶段可以在 torch.no_grad() 中运行不需要求导的前向计算。它控制该范围内的梯度记录,不能把所有已有张量变成独立副本,也不能代替 CPU 转移。不要把整个训练循环放进 no_grad(),只为让绘图函数接收输出。

验收时分别检查训练与导出两条路径:原损失仍能按预期反向传播,绘图或保存不再报转换错误,修改导出快照也不会改变训练张量。若转换前就报 CPU 与 CUDA 混用,应先核对参与运算的模型与张量设备。