我在跳棋中进行强化学习。
在网络击败自己的每场比赛之后,我计算比赛中每个单独位置的损失,调用backward()和step()。我开始相信这不是应该如何使用 SGD,我应该分批喂它,一次说一整场游戏。
严格来说,代码:
- 我是否只是通过将集体输入包装在一维张量中来做到这一点?
- 如果我给 SGD 一个输入集合,它会从集合中采样、计算平均损失并使用该梯度吗?
- SGD 是否真的打算以这种方式用于大型“集合”/批量?
我在跳棋中进行强化学习。
在网络击败自己的每场比赛之后,我计算比赛中每个单独位置的损失,调用backward()和step()。我开始相信这不是应该如何使用 SGD,我应该分批喂它,一次说一整场游戏。
严格来说,代码:
LT;DR:执行 SGD(随机梯度下降)几乎就像执行 GD(梯度下降),但计算成本更低,并且可能是在大数据集上执行 GD 的唯一方法。通过对每个样本(每场比赛)进行评估,您正在失去 SGD 的优势,并且可以说您正在“滚动自己”版本的计算成本低的 GD。
在评估模型的误差/损失函数后,无论您使用什么标准,您都将尝试在网络所在的点获取损失函数的梯度。简单来说,让我们假设您使用交叉熵作为损失的标准(如果您正在评估整个概率结果矩阵,这很有意义)。假设损失函数是,其中是交叉熵,NN评估,输入数据和标签:
这就是nn.CrossEntropyLoss(或任何其他标准)。我们想找到最小值, 为此我们可以使用但我们无法得到通用公式因为我们没有实际的公式. 然而,我们可以找到的(向量)值在我们所在的位置(其中位置是我们神经网络中每个参数/权重的值)。我们可以找对于一种特定的配置这将是网络中所有权重的矩阵。
这过于简单了 - 实际找到矩阵我们需要执行反向传播。pytorch我们通过对E.backward(). 即这是做什么backward(),它得到矩阵.
现在我们有一些非常昂贵的矩阵乘法可供使用到. 如果数据集很大(是完整的数据集)。进入新元。
SGD 与 GD 的唯一区别是 SGD 不会使用整个在上面的计算中。相反,SGD只会从并将其用作估计. 经常有人说,SGD 只取一个样本但实现会有所不同(例如 ASGD)。
最后step()会申请将学习率乘以网络中的所有权重/参数。
由于您要逐个采样并执行backward()每个step()采样,因此您实际上并没有在执行 SGD(尽管调用了该函数)。在您的情况下,SGD 优化器每次只有一个样本可供选择,因此您统一尝试数据集中的所有样本(与随机相反)。(这种一致性会减少模型的方差,这在其他方面可能是危险的,尽管在这里不是很相关)
因此是的:要使用 SGD,您确实需要输入批次,它会“平均”(求和)采样行的梯度. 由于某些样本可能会在某些方向上产生相反的梯度值,因此它将对梯度进行平均/求和/累加(求和所有分量);并希望所采集的样本将在梯度的主要方向上达成一致,并增加该方向的幅度。
PS 从每个选择的样本中累积梯度的事实pytorch也是我们需要zero_grad()在训练循环中调用的原因之一。您希望在 SGD 步骤中累积梯度,但不跨步骤累积(SO question on that)。
要实际生成批次,您可以使用torch.randperm()我个人喜欢关于 SO 的这个答案,randperm但我将在此处添加一个摘要:
有矩阵上面作为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(), ...
您需要在每个时期执行哪些操作(请参阅链接的答案以及更多内容 - 不想完整复制该代码,因为我不值得拥有该优点)。
棘手的一点可能是您需要从两者生成批次和,并保持它们之间的顺序。使用生成的索引randperm允许这样做。