#pytorch
#pytorch
Вопрос:
Я разместил этот вопрос на сайте 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: nn', theta.data)
print('nnMaximum 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
Ответ №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, так и math. Во-первых, вам нужно
- Обнуляйте градиент, задавая значение
theta.grad = None
перед каждымbackward
шагом. В противном случае градиенты накапливаются вместо перезаписи предыдущих - Вам нужно пересчитывать
T
на каждом шаге. PyTorch не является символьным, в отличие от TensorFlow иT = t theta
означает «T равно сумме текущегоt
и currenttheta
«, а не «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: nn', theta.data)
print('nnMaximum 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)