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