2

我试图通过实现牛顿求解x = cos(x)的方法来深入了解 PyTorch 的工作原理。这是一个有效的版本:

x =  Variable(DoubleTensor([1]), requires_grad=True)

for i in range(5):
    y = x - torch.cos(x)
    y.backward()
    x = Variable(x.data - y.data/x.grad.data, requires_grad=True)

print(x.data) # tensor([0.7390851332151607], dtype=torch.float64) (correct)

这段代码对我来说似乎不优雅(效率低下?),因为它在for循环的每个步骤中都重新创建了整个计算图(对吗?)。我试图通过简单地更新每个变量持有的数据而不是重新创建它们来避免这种情况:

x =  Variable(DoubleTensor([1]), requires_grad=True)
y = x - torch.cos(x)
y.backward(retain_graph=True)

for i in range(5):
    x.data = x.data - y.data/x.grad.data
    y.data = x.data - torch.cos(x.data)
    y.backward(retain_graph=True)

print(x.data) # tensor([0.7417889255761136], dtype=torch.float64) (wrong)

似乎,对于DoubleTensors,我携带了足够多的精度来排除舍入误差。那么错误来自哪里?

可能相关:如果循环,上面的代码片段retain_graph=True在每一步都没有设置标志的情况下中断。for如果我在循环中省略它——但在第 3 行保留它——我得到的错误消息是: RuntimeError: Trying backing through the graph a second time, but the buffers have been freed. 第一次向后调用时指定retain_graph=True 。这似乎证明我误解了什么......

4

1 回答 1

2

我认为您的第一个代码版本是最佳的,这意味着它不会在每次运行时创建计算图。

# initial guess
guess = torch.tensor([1], dtype=torch.float64, requires_grad = True) 

# function to optimize
def my_func(x): 
    return x - torch.cos(x)

def newton(func, guess, runs=5): 
    for _ in range(runs): 
        # evaluate our function with current value of `guess`
        value = my_func(guess)
        value.backward()
        # update our `guess` based on the gradient
        guess.data -= (value / guess.grad).data
        # zero out current gradient to hold new gradients in next iteration 
        guess.grad.data.zero_() 
    return guess.data # return our final `guess` after 5 updates

# call starts
result = newton(my_func, guess)

# output of `result`
tensor([0.7391], dtype=torch.float64)

在每次运行中,我们my_func()定义计算图的函数 用当前guess值进行评估。一旦返回结果,我们就计算梯度(通过value.backward()调用)。有了这个梯度,我们现在更新我们guess的梯度并将梯度归零,这样下次调用时它将重新保持梯度value.backward()(即它停止累积梯度;如果不将梯度归零,默认情况下它会开始累积梯度。但是,我们想在这里避免这种行为)。

于 2019-01-23T06:02:57.580 回答