【问题标题】:Why does my training loss have regular spikes?为什么我的训练损失有规律的峰值?
【发布时间】:2018-05-29 04:57:38
【问题描述】:

我正在训练这个问题底部链接的 Keras 对象检测模型,尽管我相信我的问题与 Keras 和我正在尝试训练的特定模型 (SSD) 无关,而是与在训练期间将数据传递给模型的方式。

这是我的问题(见下图): 我的训练损失总体上正在减少,但它显示出明显的规律性峰值:

x 轴上的单位不是训练 epoch,而是几十个训练步骤。峰值每 1390 个训练步骤精确出现一次,这正是我的训练数据集一次完整通过的训练步骤数。

每次完全通过训练数据集后总是会出现尖峰,这让我怀疑问题不在于模型本身,而在于训练期间输入的数据。

我在训练期间使用batch generator provided in the repository 生成批次。我检查了生成器的源代码,它确实在每次通过之前使用sklearn.utils.shuffle 对训练数据集进行了洗牌。

我很困惑有两个原因:

  1. 训练数据集在每次通过前都会被洗牌。
  2. 正如您在 this Jupyter notebook 中看到的那样,我正在使用生成器的 ad-hoc 数据增强功能,因此理论上任何传递的数据集都不应相同:所有增强都是随机的。

我做了一些测试预测,看看模型是否真的在学习任何东西,而且确实如此!随着时间的推移,预测会变得更好,但当然模型的学习速度非常慢,因为这些尖峰似乎每 1390 步就会扰乱梯度。

非常感谢任何关于这可能是什么的提示!我使用与上面链接的完全相同的 Jupyter 笔记本进行培训,我更改的唯一变量是从 32 到 16 的批量大小。除此之外,链接的笔记本包含我正在遵循的确切培训过程。

这是包含模型的存储库的链接:

https://github.com/pierluigiferrari/ssd_keras

【问题讨论】:

  • 这不是minimal reproducible example,我认为这个问题可以很好地控制饮食,这将增加得到答案的可能性:)
  • @djk47463 我同意这不是一个紧凑的示例,但是如果您有一个复杂的对象检测模型并且问题可能出在模型的任何部分,您如何创建一个紧凑的示例?无论如何,我自己已经解决了,毕竟这是一个特定于 Keras 的问题。也许这在某些时候对某人有用。

标签: deep-learning keras


【解决方案1】:

对于使用 PyTorch 工作的任何人,解决此特定问题的简单解决方案是在 DataLoader 中指定删除最后一批:

train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=False, 
                                          pin_memory=(torch.cuda.is_available()), 
                                          num_workers=num_workers, drop_last=True)

【讨论】:

  • 当然,这可以完成工作,但它也只是治标不治本的拐杖。关键是损失幅度根本不应该取决于小批量大小,因为这没有任何意义。如果你做对了,那么就没有必要放弃最后一个小批量或担心其他任何事情。
【解决方案2】:

我自己想通了:

TL;DR:

确保您的损失幅度与您的小批量大小无关。

长解释:

在我的情况下,问题毕竟是 Keras 特有的。

也许这个问题的解决方案会在某个时候对某人有用。

事实证明,Keras 将损失除以小批量大小。这里要理解的重要一点是,对批量大小进行平均的不是损失函数本身,而是在训练过程中的其他地方进行平均。

为什么这很重要?

我正在训练的模型 SSD 使用了一个相当复杂的多任务损失函数,它自己进行平均(不是按批次大小,而是按批次中地面实况边界框的数量)。现在,如果损失函数已经将损失除以与批量大小相关的某个数字,然后 Keras 再次除以批量大小,那么损失值的大小突然之间开始取决于批量大小(准确地说,它与批量大小成反比)。

现在通常数据集中的样本数量不是您选择的批量大小的整数倍,因此一个时期的最后一个小批量(这里我隐式地将一个时期定义为对数据集的一次完整传递)将最终包含的样本少于批量大小。如果它取决于批量大小,这就是破坏损失幅度的原因,进而破坏了梯度的幅度。由于我使用的是具有动量的优化器,因此混乱的梯度也会继续影响后续训练步骤的梯度。

一旦我通过将损失乘以批量大小来调整损失函数(从而恢复 Keras 的后续除以批量大小),一切都很好:损失不再出现峰值。

【讨论】:

  • 在这种情况下,通常只删除最后一批会更安全。即使损失与批次大小无关,非常小的批次也更有可能包含非代表性数据,因此会弄乱梯度。当使用小批量梯度下降时,损失 landscape 不是固定的,而是随着每批而变化。如果批量太小,这可能会导致损失的典型波动。只有当批量大小足够大以代表整个数据集时,损失才会稳定。因此,在 epoch 结束时的小(er)批次可能会产生类似的负面影响。
  • @a_guest 我不确定我是否同意,原因有三个。 1) 任何通常使用的小批量大小相对于整个数据集都非常小,以至于损失流形在小批量之间波动很大。小批量不需要非常代表整个数据集。 2) 任何使用动量或类似机制的优化器无论如何都不太依赖于单个批次。只有许多批次的平均值很重要。 3) 从经验的角度来看,使用 mini batch size 1 或非常小的 batch size 的训练在实践中效果很好。
  • 详细说明第一个论点:无论最后一批有 32 个还是 7 个样本,两者都相对于您的 50k 样本(或 500k,或 5百万)。
  • 我正在努力解决一个非常相似的问题,但我的数据大小是批量大小的整数倍,所以这个解释对我不成立。我无法解释每个时期结束时训练损失的急剧上升和急剧下降。
【解决方案3】:

我会添加梯度裁剪,因为这可以防止梯度中的尖峰在训练期间弄乱参数。

梯度剪裁是一种防止在非常深的网络(通常是循环神经网络)中梯度爆炸的技术。

大多数程序都允许您向基于 GD 的优化器添加渐变剪裁参数。

【讨论】:

  • 渐变剪裁可能会完成工作,但我认为在这种情况下这不是一个好主意,因为它会治疗症状而不是原因(损失不应该爆炸首先)。此外,已经提供了解决方案:在大多数情况下,如我上面的情况,问题将是损失幅度取决于批量大小,这是不应该的。
猜你喜欢
  • 2020-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-08-26
  • 2020-10-24
  • 2021-07-24
  • 1970-01-01
  • 2021-06-04
  • 2021-04-06
相关资源
最近更新 更多