【问题标题】:PyTorch doesn't seem to be optimizing correctlyPyTorch 似乎没有正确优化
【发布时间】:2019-03-27 17:19:39
【问题描述】:

由于 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

【问题讨论】:

    标签: pytorch


    【解决方案1】:

    您必须在循环内移动计算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_()
    

    【讨论】:

      【解决方案2】:

      你被 PyTorch 和数学所困扰。首先,你需要

      1. 通过在每个backward 步骤之前设置theta.grad = None 将渐变归零。否则梯度累积而不是覆盖之前的梯度
      2. 您需要在每一步重新计算T。 PyTorch 不是符号,与 TensorFlow 不同,T = t + theta 表示“T 等于当前 t 和当前 theta 的总和”而不是“T 等于 ttheta 的总和,无论它们的值可能是什么未来的任何时间”。

      通过这些修复,您将获得以下代码:

      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 的排列是对称的,因此计算出的梯度是相等的,梯度下降算法永远无法“区分它们”。用更数学的术语来说,您很不幸地在鞍点上准确地初始化您的算法,因此它无法继续。如果添加任何噪声,它将收敛。例如与

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

      【讨论】:

        猜你喜欢
        • 2010-09-08
        • 2016-12-26
        • 1970-01-01
        • 1970-01-01
        • 2021-04-02
        • 1970-01-01
        • 2019-10-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多