PyTorch似乎无法正确优化

时间:2019-03-27 04:06:52

标签: pytorch

由于StackOverflow不支持LaTeX,因此我已在Data Science StackExchange网站上发布了此问题。在此处链接它是因为该站点可能更合适。

使用正确渲染的LaTeX的问题在这里:https://datascience.stackexchange.com/questions/48062/pytorch-does-not-seem-to-be-optimizing-correctly

我的想法是我正在考虑不同相位的正弦波之和。在间隔[0,2pi]中以一定的采样率s对波进行采样。我需要以这样一种方式选择相位,使任何采样点处的波的总和最小化。

下面是Python代码。优化似乎没有正确计算。

import numpy as np
import torch

def phaseOptimize(n, s = 48000, nsteps = 1000):
    learning_rate = 1e-3

    theta = torch.zeros([n, 1], requires_grad=True)
    l = torch.linspace(0, 2 * np.pi, s)
    t = torch.stack([l] * n)
    T = t + theta

    for jj in range(nsteps):
        loss = T.sin().sum(0).pow(2).sum() / s
        loss.backward()
        theta.data -= learning_rate * theta.grad.data

    print('Optimal theta: \n\n', theta.data)
    print('\n\nMaximum value:', T.sin().sum(0).abs().max().item())

下面是示例输出。

phaseOptimize(5, nsteps=100)


Optimal theta: 

 tensor([[1.2812e-07],
        [1.2812e-07],
        [1.2812e-07],
        [1.2812e-07],
        [1.2812e-07]], requires_grad=True)


Maximum value: 5.0

我认为这与广播有关

T = t + theta

和/或我计算损失函数的方式。

验证优化不正确的一种方法是简单地对数组$ \ theta_1,\ dots,\ theta_n $的随机值求损失函数,例如均匀分布在$ [0,2 \ pi] $中。在这种情况下,最大值几乎总是比phaseOptimize()报告的最大值低得多。实际上,要简单得多是考虑$ n = 2 $的情况,并简单地将$ \ theta_1 = 0 $和$ \ theta_2 = \ pi $求值。在这种情况下,我们得到:

phaseOptimize(2, nsteps=100)

Optimal theta: 

 tensor([[2.8599e-08],
        [2.8599e-08]])


Maximum value: 2.0

另一方面,

theta = torch.FloatTensor([[0], [np.pi]])
l = torch.linspace(0, 2 * np.pi, 48000)
t = torch.stack([l] * 2)
T = t + theta

T.sin().sum(0).abs().max().item()

产生

3.2782554626464844e-07

2 个答案:

答案 0 :(得分:2)

您必须在循环内移动计算T,否则它将始终具有相同的常数值,因此常数损失不变。

另一件事是将theta初始化为索引处的不同值,否则由于问题的对称性,每个索引的梯度都相同。

另一件事是,您需要将梯度设为零,因为backward只是对其进行累加。

这似乎可行:

def phaseOptimize(n, s = 48000, nsteps = 1000):
    learning_rate = 1e-1

    theta = torch.zeros([n, 1], requires_grad=True)
    theta.data[0][0] = 1
    l = torch.linspace(0, 2 * np.pi, s)
    t = torch.stack([l] * n)

    for jj in range(nsteps):
        T = t + theta
        loss = T.sin().sum(0).pow(2).sum() / s
        loss.backward()
        theta.data -= learning_rate * theta.grad.data
        theta.grad.zero_()

答案 1 :(得分:1)

您被PyTorch和数学都咬了。首先,您需要

  1. 通过在每个theta.grad = None步骤之前设置backward来缩小渐变。否则,梯度会累积,而不是覆盖以前的梯度
  2. 您需要在每个步骤中重新计算T。 PyTorch不是象征性的,与TensorFlow不同,T = t + theta表示“ T等于当前t和当前theta的总和”,而不是“ T等于t和{{ 1}},无论将来任何时候它们的价值如何。”

使用这些修复程序,您将获得以下代码:

theta

由于数学问题,它仍然无法按预期工作。

很容易看出损失函数的最小值是def phaseOptimize(n, s = 48000, nsteps = 1000): learning_rate = 1e-3 theta = torch.zeros(n, 1, requires_grad=True) l = torch.linspace(0, 2 * np.pi, s) t = torch.stack([l] * n) T = t + theta for jj in range(nsteps): T = t + theta loss = T.sin().sum(0).pow(2).sum() / s theta.grad = None loss.backward() theta.data -= learning_rate * theta.grad.data T = t + theta print('Optimal theta: \n\n', theta.data) print('\n\nMaximum value:', T.sin().sum(0).abs().max().item()) 也均匀地分布在theta上。问题在于您正在将参数初始化为[0, 2pi),这将导致所有这些值相等(这是等距的两极!)。由于您的损失函数相对于torch.zeros的排列是对称的,因此计算出的梯度是相等的,并且梯度下降算法永远无法“区分它们”。用更多的数学术语来说,您不够幸运地无法准确地在鞍点上初始化算法,因此它无法继续。如果添加任何噪音,它将收敛。例如,

theta