PyTorch不';似乎没有正确地进行优化

PyTorch不';似乎没有正确地进行优化,pytorch,Pytorch,由于StackOverflow不支持LaTeX,我已经在Data Science StackExchange网站上发布了这个问题。链接到这里,因为这个网站可能更合适 正确渲染乳胶的问题如下: 我的想法是考虑不同相位的正弦波之和。在间隔[0,2pi]内,以一定的采样率s对波进行采样。我需要以这样一种方式来选择相位,任何采样点的波的总和都是最小的 下面是Python代码。优化的计算似乎不正确 将numpy导入为np 进口火炬 def相位优化(n,s=48000,nsteps=1000): 学习率=1

由于StackOverflow不支持LaTeX,我已经在Data Science StackExchange网站上发布了这个问题。链接到这里,因为这个网站可能更合适

正确渲染乳胶的问题如下:

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

下面是Python代码。优化的计算似乎不正确

将numpy导入为np
进口火炬
def相位优化(n,s=48000,nsteps=1000):
学习率=1e-3
θ=火炬。零([n,1],需要_grad=True)
l=torch.linspace(0,2*np.pi,s)
t=火炬烟囱([l]*n)
T=T+theta
对于范围内的jj(nsteps):
损耗=T.sin().sum(0).pow(2).sum()/s
loss.backward()
θ.data-=学习率*θ.grad.data
打印('最佳θ:\n\n',θ数据)
打印('\n\n最大值:',T.sin().sum(0.abs().max().item())
下面是一个示例输出

phaseOptimize(5,nsteps=100)
最佳θ:
张量([[1.2812e-07],
[1.2812e-07],
[1.2812e-07],
[1.2812e-07],
[1.2812e-07]],需要_grad=True)
最大值:5.0
我想这与中国的广播有关

T=T+theta
和/或我计算损失函数的方式

验证优化是否正确的一种方法是,简单地对数组$\theta\u 1、\dots、\theta\u n$的随机值计算损失函数,例如均匀分布在$[0,2\pi]$。这种情况下的最大值几乎总是远低于
phaseOptimize()
报告的最大值。事实上,更容易考虑的是$N=2美元,并且简单地评估$\ theta a1=0 $和$\ theta a2=\pI$。在这种情况下,我们得到:

phaseOptimize(2,nsteps=100)
最佳θ:
张量([[2.8599e-08],
[2.8599e-08]]
最大值:2.0
另一方面,

theta=torch.FloatTensor([[0],[np.pi]])
l=torch.linspace(0,2*np.pi,48000)
t=火炬烟囱([l]*2)
T=T+theta
T.sin().sum(0.abs().max().item()
产生

3.2782554626464844e-07

您必须将计算
T
移动到循环内部,否则它将始终具有相同的常量值,从而导致常量损失

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

另一件事是,您需要将梯度设置为零,因为
向后
只是将它们累加起来

这似乎有效:

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_()

你被PyTorch和math都咬了。首先,你需要

  • 通过在每个
    向后
    步骤之前设置
    theta.grad=None
    将梯度归零。否则,渐变将累积,而不是覆盖以前的渐变
  • 您需要在每个步骤中重新计算
    T
    。PyTorch不是符号,与TensorFlow不同,
    T=T+theta
    意味着“T等于当前
    T
    和当前
    theta
    之和”,而不是“T等于
    T
    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())
    
    由于数学的原因,它仍然不会像你期望的那样工作

    我们可以很容易地看到,损失函数的最小值是
    θ
    [0,2pi)
    上均匀分布。问题是,您将参数初始化为
    火炬。零
    ,这导致所有这些值相等(这是等间距的极性相反!)。由于损失函数相对于
    θ
    的排列是对称的,因此计算的梯度是相等的,梯度下降算法永远无法“区分它们”.用更数学的术语来说,你很不幸在鞍点上初始化了你的算法,所以它无法继续。如果你添加任何噪声,它就会收敛。例如

    theta = torch.zeros(n, 1) + 0.001 * torch.randn(n, 1)
    theta.requires_grad_(True)