torch.optim针对多维张量返回“ ValueError:无法优化非叶张量”

时间:2020-05-17 12:05:55

标签: optimization pytorch tensor

我正在尝试使用torch.optim.adam优化场景顶点的平移。它是 redner 教程系列的代码片段,在初始设置下可以正常工作。它尝试通过将所有顶点移动相同的值translation来优化场景。这是原始代码:

vertices = []
for obj in base:
    vertices.append(obj.vertices.clone())

def model(translation):
    for obj, v in zip(base, vertices):
        obj.vertices = v + translation
    # Assemble the 3D scene.
    scene = pyredner.Scene(camera = camera, objects = objects)
    # Render the scene.
    img = pyredner.render_albedo(scene)
    return img

# Initial guess
# Set requires_grad=True since we want to optimize them later

translation = torch.tensor([10.0, -10.0, 10.0], device = pyredner.get_device(), requires_grad=True)

init = model(translation)
# Visualize the initial guess

t_optimizer = torch.optim.Adam([translation], lr=0.5)

我试图修改代码,以便为每个顶点计算一个单独的平移。为此,我对上面的代码进行了以下修改,使translation的形状从torch.Size([3])torch.Size([43380, 3])

# translation = torch.tensor([10.0, -10.0, 10.0], device = pyredner.get_device(), requires_grad=True)
translation = base[0].vertices.clone().detach().requires_grad_(True)
translation[:] = 10.0

这引入了ValueError: can't optimize a non-leaf Tensor。您能帮我解决这个问题吗?

PS:很长的文字对不起,我对这个话题还很陌生,我想尽可能全面地说明这个问题。

2 个答案:

答案 0 :(得分:3)

只能优化叶张量。叶张量是在图的开头创建的张量,即图中没有跟踪任何操作来生成它。换句话说,当您使用requires_grad=True将任何操作应用于张量时,它都会跟踪这些操作以稍后进行反向传播。您不能将这些中间结果之一提供给优化器。

一个例子更清楚地表明了这一点:

weight = torch.randn((2, 2), requires_grad=True)
# => tensor([[ 1.5559,  0.4560],
#            [-1.4852, -0.8837]], requires_grad=True)

weight.is_leaf # => True

result = weight * 2
# => tensor([[ 3.1118,  0.9121],
#            [-2.9705, -1.7675]], grad_fn=<MulBackward0>)
# grad_fn defines how to do the back propagation (kept track of the multiplication)

result.is_leaf # => False

此示例中的result不是叶子张量,因此无法优化。同样,在您的情况下,translation不是叶子张量,因为创建后您执行的操作是

translation[:] = 10.0
translation.is_leaf # => False

它有grad_fn=<CopySlices>,因此它不是叶子,您不能将其传递给优化器。为避免这种情况,您必须从中创建一个新的张量,该张量与图形分离。

# Not setting requires_grad, so that the next operation is not tracked
translation = base[0].vertices.clone().detach()
translation[:] = 10.0
# Now setting requires_grad so it is tracked in the graph and can be optimised
translation = translation.requires_grad_(True)

您在这里真正在做的是创建一个新的张量,该张量填充为10.0,其大小与顶点张量相同。使用torch.full_like

可以轻松实现这一目标
translation = torch.full_like(base[0],vertices, 10.0, requires_grad=True)

答案 1 :(得分:2)

什么是叶子变量?

叶变量是位于图形开始处的变量。这意味着Autograd引擎跟踪的任何操作都不会创建变量(这就是为什么将其称为叶子变量)的原因。在优化神经网络的过程中,我们要更新叶变量,例如模型权重,输入等。

为了能够将张量提供给优化器,它们应该遵循上面的leaf变量的定义。

一些例子。

a = torch.rand(10, requires_grad=True)

在这里,a是叶子变量。

a = torch.rand(10, requires_grad=True).double()

在这里,a不是叶变量,因为它是通过将浮点张量转换为双张量的操作创建的。

a = torch.rand(10).requires_grad_().double() 

这等效于先前的公式:a不是叶变量。

a = torch.rand(10).double() 

在这里,a不需要渐变,也没有创建渐变的操作(由Autograd引擎跟踪)。

a = torch.rand(10).doube().requires_grad_() 

在这里,a需要grad并且没有创建它的操作:它是一个叶子变量,可以提供给优化程序。

a = torch.rand(10, requires_grad=True, device="cuda") 

在这里,a需要grad并且没有创建它的操作:它是一个叶子变量,可以提供给优化程序。

我从discussion thread中借用了上面的解释。


因此,在您的情况下,translation[:] = 10.0操作使translation成为非叶变量。可能的解决方案是:

translation = base[0].vertices.clone().detach()
translation[:] = 10.0
translation = translation.requires_grad_(True)

在上一条语句中,您设置了requires_grad,因此现在将对其进行跟踪和优化。