Pytorch 中的弱优化器

Weak optimizers in Pytorch

考虑一个简单的直线拟合 a * x + b = x,其中 ab 是优化参数,x 是由

给出的观测向量
import torch
X = torch.randn(1000,1,1)

可以立即看出对于任何 x 的精确解是 a=1b=0 并且可以很容易地找到它:

import numpy as np
np.polyfit(X.numpy().flatten(), X.numpy().flatten(), 1)

我现在正在尝试通过 PyTorch 中的梯度下降找到这个解决方案,其中均方误差用作优化标准。

import matplotlib.pyplot as plt
import numpy as np

import torch
import torch.nn as nn
from torch.optim import Adam, SGD, Adagrad, ASGD 

X = torch.randn(1000,1,1) # Sample data

class SimpleNet(nn.Module): # Trivial neural network containing two weights
    def __init__(self):
        super(SimpleNet, self).__init__()
        self.f1 = nn.Linear(1,1)

    def forward(self, x):
        x = self.f1(x)
        return x

# Testing default setting of 3 basic optimizers

K = 500
net = SimpleNet() 
optimizer = Adam(params=net.parameters())
Adam_losses = []
optimizer.zero_grad()   # zero the gradient buffers
for k in range(K):
    for b in range(1): # single batch
        loss = torch.mean((net.forward(X[b,:,:]) - X[b,:, :])**2)
        loss.backward()
        optimizer.step()
        Adam_losses.append(float(loss.detach()))

net = SimpleNet()
optimizer = SGD(params=net.parameters(), lr=0.0001)
SGD_losses = []
optimizer.zero_grad()   # zero the gradient buffers
for k in range(K):
    for b in range(1): # single batch
        loss = torch.mean((net.forward(X[b,:,:]) - X[b,:, :])**2)
        loss.backward()
        optimizer.step()
        SGD_losses.append(float(loss.detach()))

net = SimpleNet()     
optimizer = Adagrad(params=net.parameters())
Adagrad_losses = []
optimizer.zero_grad()   # zero the gradient buffers
for k in range(K):
    for b in range(1): # single batch
        loss = torch.mean((net.forward(X[b,:,:]) - X[b,:, :])**2)
        loss.backward()
        optimizer.step()
        Adagrad_losses.append(float(loss.detach()))

loss evolution方面的训练进度可以表示为

令我惊讶的是默认设置下算法的收敛速度非常慢。因此我有两个问题:

1) 是否可以纯粹通过一些Pytorch优化器来实现任意小错误(损失)?由于损失函数是凸函数,因此绝对有可能,但是,我无法弄清楚如何使用 PyTorch 实现这一点。请注意,上述 3 个优化器无法做到这一点 - 查看 20000 次迭代的对数比例损失进度:

2) 我想知道优化器如何在复杂的示例中工作良好,而即使在这个极其简单的示例中它们也不能很好地工作。或者(这是第二个问题)我错过了他们上面的申请有什么问题吗?

你调用zero_grad的地方是错误的。在每个时期,梯度被添加到前一个并反向传播。这使得损失在接近时振荡,但之前的梯度再次将其从解决方案中抛出。

下面的代码将轻松执行任务:

import torch

X = torch.randn(1000,1,1)

net = SimpleNet()
optimizer = Adam(params=net.parameters())
for epoch in range(EPOCHS):
    optimizer.zero_grad()  # zero the gradient buffers
    loss = torch.mean((net.forward(X) - X) ** 2)
    if loss < 1e-8:
        print(epoch, loss)
        break
    loss.backward()
    optimizer.step()

1) Is it possible to achieve an arbitrary small error (loss) purely by means of some Pytorch optimizer?

是的,在大约 1500 次迭代后达到上述精度,您可以降低到机器(在本例中为浮动)精度

2) I am wondering how the optimizers can work well in complex examples, when they does not work well even in this extremely simple example.

目前,我们没有比一阶方法更好(至少广泛传播)的网络优化方法。使用这些是因为对于高阶方法,计算梯度比 Hessians 快得多。复杂的非凸函数可能有很多最小值,这有点完成我们交给它的任务,本身不需要全局最小值(尽管它们可能在某些条件下,参见 this paper)。