牛顿方法的PyTorch实现中的更新步骤

时间:2019-01-22 20:33:27

标签: python mathematical-optimization pytorch newtons-method automatic-differentiation

我试图通过实施牛顿方法来解决PemTorch的工作原理,以解决 x = cos(x)。这是一个有效的版本:

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)

似乎DoubleTensor的精度足以排除舍入误差。那么错误从哪里来?

可能与之相关:如果循环retain_graph=True,则上述代码段在没有设置for标志的情况下中断。如果在循环内省略它,但保留在第3行---上,我得到的错误消息是: RuntimeError:尝试第二次向后浏览图形,但缓冲区已被释放。第一次回叫 时,请指定keep_graph = True。这似乎证明我误解了某些东西...

1 个答案:

答案 0 :(得分:1)

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

# 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()时重新持有该渐变(即,它不再累积渐变;而不会清零)渐变,默认情况下会开始累积渐变。但是,我们要在此处避免这种行为)。