我的代码:
import torch
import random
image_width, image_height = 128, 128
def apply_ellipse_mask(img, pos, axes):
r = torch.arange(image_height)[:, None]
c = torch.arange(image_width)[None, :]
val_array = ((c - pos[0]) ** 2) / axes[0] ** 2 + ((r - pos[1]) ** 2) / axes[1] ** 2
mask = torch.where((0.9 < val_array) & (val_array < 1), torch.tensor(1.0), torch.tensor(0.0))
return img * (1.0 - mask) + mask
random.seed(0xced)
sphere_radius = image_height / 3
sphere_position = torch.tensor([image_width / 2, image_height / 2 ,0], requires_grad=True)
ref_image = apply_ellipse_mask(torch.zeros(image_width, image_height, requires_grad=True), sphere_position, [sphere_radius, sphere_radius, sphere_radius])
ellipsoid_pos = torch.tensor([sphere_position[0], sphere_position[1], 0], requires_grad=True)
ellipsoid_axes = torch.tensor([image_width / 3 + (random.random() - 0.5) * image_width / 5, image_height / 3 + (random.random() - 0.5) * image_height / 5, image_height / 2], requires_grad=True)
optimizer = torch.optim.Adam([ellipsoid_axes], lr=0.1)
criterion = torch.nn.MSELoss()
for _ in range(100):
optimizer.zero_grad()
current_image = torch.zeros(image_width, image_height, requires_grad=True)
current_image = apply_ellipse_mask(current_image, ellipsoid_pos, ellipsoid_axes)
loss = criterion(current_image, ref_image)
loss.backward()
print(_, loss)
optimizer.step()
错误:
RuntimeError:尝试第二次向后遍历图表(或在已释放已保存的张量后直接访问它们)。调用 .backward() 或 autograd.grad() 时,将释放已保存的图表中间值。如果您需要第二次向后遍历图表,或者在调用 Backward 后需要访问已保存的张量,请指定 retain_graph=True。
为什么它会尝试第二次向后遍历同一张图?我是否在释放已保存的张量后直接访问它们?
您已经创建了许多叶节点(需要梯度的变量),其中包括:
它创建一个叶节点(带有
torch.zeros(image_width, image_height, requires_grad=True)
)并应用一些计算,这样你就得到了一个计算图。但是每次迭代你都会重复使用结果。你不会每次迭代都重新计算它,所以你试图多次向后移动同一个图。唯一应该有的是require_grad = True
你优化的参数。您尝试
ellipsoid_axes
通过计算掩码来流动梯度,但掩码的计算不可微分axes
(它无论如何都会返回 0-1)。您应该使用某种 S 型函数使掩码“变软”。这更像是一条注释,因为此代码仍会导致相同的错误。避免使用 PyTorch 这样的 for 循环,因为它们很慢。相反,你可以这样写: