【问题标题】:Update step in PyTorch implementation of Newton's method牛顿方法的 PyTorch 实现中的更新步骤
【发布时间】:2019-06-16 09:11:18
【问题描述】:

我试图通过实现牛顿求解 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,我携带了足够多的精度来排除四舍五入错误。那么错误来自哪里?

可能相关:如果for 循环,则上述 sn-p 中断时没有在每一步设置retain_graph=True 标志。如果我在循环中省略它——但在第 3 行保留它——我得到的错误消息是: RuntimeError: 试图第二次向后遍历图形,但缓冲区已被释放。在第一次向后调用时指定retain_graph=True。这似乎证明我误解了什么......

【问题讨论】:

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


    【解决方案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() 时它将重新保持梯度(即它停止累积梯度;不将梯度归零,它默认情况下会开始累积梯度。但是,我们希望在这里避免这种行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-22
      • 1970-01-01
      • 2019-09-08
      • 2012-10-24
      • 2017-09-15
      • 1970-01-01
      • 2013-09-25
      • 2013-10-24
      相关资源
      最近更新 更多