我在 pytorch 中错误地使用了 optim.SGD 吗?

数据挖掘 梯度下降 火炬
2022-02-27 10:37:06

我在跳棋中进行强化学习。

在网络击败自己的每场比赛之后,我计算比赛中每个单独位置的损失,调用backward()和step()。我开始相信这不是应该如何使用 SGD,我应该分批喂它,一次说一整场游戏。

严格来说,代码:

  • 我是否只是通过将集体输入包装在一维张量中来做到这一点?
  • 如果我给 SGD 一个输入集合,它会从集合中采样、计算平均损失并使用该梯度吗?
  • SGD 是否真的打算以这种方式用于大型“集合”/批量?
1个回答

LT;DR:执行 SGD(随机梯度下降)几乎就像执行 GD(梯度下降),但计算成本更低,并且可能是在大数据集上执行 GD 的唯一方法。通过对每个样本(每场比赛)进行评估,您正在失去 SGD 的优势,并且可以说您正在“滚动自己”版本的计算成本低的 GD。

梯度下降总结

在评估模型的误差/损失函数后,无论您使用什么标准,您都将尝试在网络所在的点获取损失函数的梯度。简单来说,让我们假设您使用交叉熵作为损失的标准(如果您正在评估整个概率结果矩阵,这很有意义)。假设损失函数是,其中H是交叉熵,fNN评估,X输入数据和Y标签:

E=H(f(X),y)

这就是nn.CrossEntropyLoss(或任何其他标准)。我们想找到最小值E, 为此我们可以使用E但我们无法得到通用公式E因为我们没有实际的公式f. 然而,我们可以找到的(向量E在我们所在的位置(其中位置是我们神经网络中每个参数/权重的值)。我们可以找E对于一种特定的配置W这将是网络中所有权重的矩阵。

EW=2NXT(XWy)

这过于简单了 - 实际找到矩阵W我们需要执行反向传播。pytorch我们通过E.backward(). 即这是做什么backward(),它得到矩阵W.

现在我们有一些非常昂贵的矩阵乘法可供使用WEW. 如果数据集很大(X是完整的数据集)。进入新元。

随机梯度下降

SGD 与 GD 的唯一区别是 SGD 不会使用整个X在上面的计算中。相反,SGD只会X并将其用作估计EW. 经常有人说,SGD 只取一个样本X但实现会有所不同(例如 ASGD)。

最后step()会申请EW将学习率乘以网络中的所有权重/参数。

你的方法

由于您要逐个采样并执行backward()每个step()采样,因此您实际上并没有在执行 SGD(尽管调用了该函数)。在您的情况下,SGD 优化器每次只有一个样本可供选择,因此您统一尝试数据集中的所有样本(与随机相反)。(这种一致性会减少模型的方差,这在其他方面可能是危险的,尽管在这里不是很相关)

因此是的:要使用 SGD,您确实需要输入批次,它会“平均”(求和)采样行的梯度X. 由于某些样本可能会在某些方向上产生相反的梯度值,因此它将对梯度进行平均/求和/累加(求和所有分量);并希望所采集的样本将在梯度的主要方向上达成一致,并增加该方向的幅度。


PS 从每个选择的样本中累积梯度的事实pytorch也是我们需要zero_grad()在训练循环中调用的原因之一。您希望在 SGD 步骤中累积梯度,但不跨步骤累积(SO question on that)。

代码

要实际生成批次,您可以使用torch.randperm()我个人喜欢关于 SO 的这个答案,randperm但我将在此处添加一个摘要:

有矩阵X上面作为X(在代码中),可以创建一批 256 个样本:

batch_size = 128
n_samples = X.size()[0]
permutation = torch.randperm(n_samples)

for i in range(0, n_samples, batch_size):

    indices = permutation[i:i+batch_size]
    batch_x, batch_y = X[indices], Y[indices]

    # nn(X), criterion(), backward(), step(), zero_grads(), ...

您需要在每个时期执行哪些操作(请参阅链接的答案以及更多内容 - 不想完整复制该代码,因为我不值得拥有该优点)。

棘手的一点可能是您需要从两者生成批次XY,并保持它们之间的顺序。使用生成的索引randperm允许这样做。