牛顿法 PyTorch 实现中的更新步骤
Update step in PyTorch implementation of Newton's method
我试图通过实施牛顿法求解 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)
似乎在 DoubleTensor
中,我携带了足够的精度数字来排除舍入误差。那么错误从何而来?
可能相关:如果 for
循环,上面的代码片段在没有在每一步设置 retain_graph=True
标志的情况下中断。如果我在循环中省略它——但在第 3 行保留它——我得到的错误消息是:
RuntimeError:试图第二次向后遍历图形,但缓冲区已被释放。在 第一次向后调用时指定 retain_graph=True 。 这似乎证明我误解了某些东西...
我认为您的第一个代码版本是最佳的,这意味着它不会在每个 运行.
上创建计算图
# 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
值进行评估。一旦 returns 结果,我们计算梯度(使用 value.backward()
调用)。有了这个梯度,我们现在更新我们的 guess
并将我们的梯度归零,以便下次我们调用 value.backward()
时它会重新保存梯度(即它停止累积梯度;不归零梯度,默认情况下它会开始累积梯度。但是,我们想在这里避免这种行为)。
我试图通过实施牛顿法求解 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)
似乎在 DoubleTensor
中,我携带了足够的精度数字来排除舍入误差。那么错误从何而来?
可能相关:如果 for
循环,上面的代码片段在没有在每一步设置 retain_graph=True
标志的情况下中断。如果我在循环中省略它——但在第 3 行保留它——我得到的错误消息是:
RuntimeError:试图第二次向后遍历图形,但缓冲区已被释放。在 第一次向后调用时指定 retain_graph=True 。 这似乎证明我误解了某些东西...
我认为您的第一个代码版本是最佳的,这意味着它不会在每个 运行.
上创建计算图# 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
值进行评估。一旦 returns 结果,我们计算梯度(使用 value.backward()
调用)。有了这个梯度,我们现在更新我们的 guess
并将我们的梯度归零,以便下次我们调用 value.backward()
时它会重新保存梯度(即它停止累积梯度;不归零梯度,默认情况下它会开始累积梯度。但是,我们想在这里避免这种行为)。