PyTorch, похоже, не оптимизирует правильно - PullRequest
1 голос
/ 27 марта 2019

Я разместил этот вопрос на сайте Data Science StackExchange, поскольку StackOverflow не поддерживает LaTeX.Ссылка здесь, потому что этот сайт, вероятно, более уместен.

Вопрос с правильно отрендеренным LaTeX здесь: https://datascience.stackexchange.com/questions/48062/pytorch-does-not-seem-to-be-optimizing-correctly

Идея состоит в том, что я рассматриваю суммы синусоидальных волн с разными фазами.Волны дискретизируются с некоторой частотой дискретизации s в интервале [0, 2pi].Мне нужно выбирать фазы таким образом, чтобы сумма волн в любой точке выборки была минимальной.

Ниже приведен код 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 ]

2 голосов
/ 27 марта 2019

Вы должны переместить вычисления 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 голос
/ 27 марта 2019

Вас укусила и PyTorch, и математика. Во-первых, вам нужно

  1. Обнулить градиент, установив theta.grad = None перед каждым шагом backward. В противном случае градиенты накапливаются вместо перезаписи предыдущих
  2. Вам нужно пересчитать 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())

, который из-за математики все равно не будет работать так, как вы ожидаете.

Легко видеть, что минимум вашей функции потерь - это когда theta также равномерно распределено по [0, 2pi). Проблема в том, что вы инициализируете свои параметры как torch.zeros, что приводит к тому, что все эти значения равны (это полярная противоположность equispaced!). Поскольку ваша функция потерь симметрична относительно перестановок theta, вычисленные градиенты равны, и алгоритм градиентного спуска никогда не сможет «дифференцировать их». В более математических терминах вам не повезло инициализировать ваш алгоритм точно в седловой точке, поэтому он не может продолжаться. Если вы добавите какой-либо шум, он будет сходиться. Например, с

theta = torch.zeros(n, 1) + 0.001 * torch.randn(n, 1)
theta.requires_grad_(True)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...