训练时间过长时 Adam 优化器的奇怪行为

数据挖掘 感知器 火炬
2021-09-27 02:59:39

我正在尝试在 64 个随机生成的数据点上训练单个感知器(1000 个输入单元,1 个输出,无隐藏层)。我正在使用使用 Adam 优化器的 Pytorch:

import torch
from torch.autograd import Variable

torch.manual_seed(545345)
N, D_in, D_out = 64, 1000, 1

x = Variable(torch.randn(N, D_in))
y = Variable(torch.randn(N, D_out))

model = torch.nn.Linear(D_in, D_out)
loss_fn = torch.nn.MSELoss(size_average=False)

optimizer = torch.optim.Adam(model.parameters())
for t in xrange(5000):
  y_pred = model(x)
  loss = loss_fn(y_pred, y)

  print(t, loss.data[0])

  optimizer.zero_grad()
  loss.backward()
  optimizer.step()

最初,损失迅速减少,正如预期的那样:

(0, 91.74887084960938)
(1, 76.85824584960938)
(2, 63.434078216552734)
(3, 51.46927261352539)
(4, 40.942893981933594)
(5, 31.819372177124023)

大约 300 次迭代,误差接近于零:

(300, 2.1734419819452455e-12)
(301, 1.90354676465887e-12)
(302, 2.3347573874232808e-12)

这持续了几千次迭代。但是,训练时间过长,误差又开始增加:

(4997, 0.002102422062307596)
(4998, 0.0020302983466535807)
(4999, 0.0017039275262504816)

为什么会这样?

2个回答

收敛结束时的这种小不稳定性是 Adam(和 RMSProp)的一个特征,因为它如何估计最近步骤的平均梯度幅度并除以它们。

Adam 所做的一件事是保持最近梯度和梯度平方的滚动几何平均值。梯度的平方用于划分当前梯度(的另一个滚动平均值)以决定当前步长。但是,当您的梯度变得并保持非常接近于零时,这将使梯度的平方变得如此之低,以至于它们要么具有较大的舍入误差,要么实际上为零,这可能会引入不稳定性(例如,在一维从10-1010-5由于其他参数的变化),并且步长将开始跳跃,然后再次稳定下来。

这实际上使 Adam 对于您的问题比更基本的梯度下降更不稳定和更糟糕,假设您希望在计算允许您的问题时在数值上尽可能接近零损失。

在深度学习问题的实践中,您不会接近收敛(并且对于一些正则化技术,例如提前停止,您无论如何都不想这样做),因此通常不是实际关注的问题类型亚当是专为。

在比较不同的优化器时,您实际上可以看到 RMSProp 发生了这种情况(RMSProp 是黑线 - 在达到目标时观察最后的步骤):

在此处输入图像描述

你可以通过降低学习率让 Adam 更稳定,更接近真正的收敛。例如

optimizer = torch.optim.Adam(model.parameters(), lr=1e-5)

优化需要更长的时间。使用lr=1e-5您需要训练 20,000 多次迭代才能看到不稳定性,并且不稳定性不那么剧烈,值徘徊10-7.

原因与另一个答案中提到的完全一样,其中一个很好的建议是使用较小的学习率来避免围绕小梯度的这个问题。

我可以想到几种方法:

  1. 您可以使用上限/下限裁剪梯度,但这并不能保证收敛,并且可能会因陷入某些局部最小值而导致训练冻结,并且永远无法摆脱。

  2. 以更高的批量大小、更多的 epoch 和衰减的学习率进行训练。现在我没有任何实际证据表明增加批量大小会导致更好的梯度,但根据我在面临与您类似的问题时所观察到的情况,这样做几乎总是有帮助的。

我确信还有其他方法(如循环学习率等)试图根据统计数据找到最佳学习率。