Meu código:
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()
Erro:
RuntimeError: Tentando retroceder no gráfico uma segunda vez (ou acessar diretamente tensores salvos após eles já terem sido liberados). Valores intermediários salvos do gráfico são liberados quando você chama .backward() ou autograd.grad(). Especifique retain_graph=True se você precisar retroceder no gráfico uma segunda vez ou se precisar acessar tensores salvos após chamar backward.
Por que ele estaria tentando retroceder pelo mesmo gráfico uma segunda vez? Estou acessando diretamente os tensores salvos depois que eles foram liberados?
Você criou muitos nós folha (variáveis que requerem gradiente), incluindo:
que cria um nó folha (com
torch.zeros(image_width, image_height, requires_grad=True)
) e aplica alguns cálculos para que você obtenha um gráfico de cálculo. Mas então você reutiliza o resultado a cada iteração. Você não o recomputa a cada iteração, então você está tentando voltar no mesmo gráfico várias vezes. As únicas coisas que você deve terrequire_grad = True
são parâmetros que você otimiza.Você está tentando fazer o gradiente fluir para
ellipsoid_axes
o cálculo da máscara, mas o cálculo da máscara não é diferenciável emaxes
(ele retorna 0-1 de qualquer forma). Você deve fazer a máscara "suave" usando algum tipo de sigmoide em vez disso.Isto é mais um comentário, pois este código ainda causará o mesmo erro. Evite for-loops como este com PyTorch, pois eles são lentos. Em vez disso, você pode escrever: