为什么我的训练损失会定期出现高峰?

时间:2017-12-15 01:33:29

标签: deep-learning keras

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

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

Training loss

x轴上的单位不是训练时期,而是数十个训练步骤。每1390个训练步骤恰好出现一次峰值,这正是训练数据集中一次完整传递的训练步数。

在训练数据集的每次完整传递之后总是出现尖峰这一事实使我怀疑问题不在于模型本身,而在于训练过程中的数据。

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

我完全因两个原因感到困惑:

  1. 每次通过前都会对训练数据集进行洗牌。
  2. 正如您在this Jupyter notebook中所看到的,我正在使用生成器的ad-hoc数据增强功能,因此理论上数据集在任何传递中都应该是相同的:所有增强都是随机的。
  3. 这就是我在损失函数中不理解这种模式的原因。通过在每次传递之前使用ad-hoc数据扩充和混洗数据集,我基本上都在破坏数据集中的任何结构:在训练期间,给定图像的相同变换几乎不可能发生两次,但是尖峰发生在每次完整传递数据集之后,就像发条一样。

    我做了一些测试预测,看看模型是否真正学到了什么,它就是!随着时间的推移,预测会变得越来越好,但当然模型的学习速度非常慢,因为这些峰值似乎每1390步都会使梯度变得混乱。

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

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

    https://github.com/pierluigiferrari/ssd_keras

3 个答案:

答案 0 :(得分:4)

我自己弄清楚了:

<强> TL; DR:

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

长篇解释:

就我而言,问题毕竟是Keras特有的。

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

事实证明,Keras将损失除以小批量大小。这里需要理解的重要一点是,损失函数本身并不是批量大小的平均值,而是平均值发生在训练过程中的其他位置。

为什么这很重要?

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

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

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

答案 1 :(得分:1)

我将添加梯度剪切,因为这样可以防止梯度尖峰在训练过程中弄乱参数。

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

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

答案 2 :(得分:0)

对于在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)