训练过程中Loss突然变为NaN的可能原因与解决

训练过程中Loss突然变为NaN的可能原因与解决

深度学习训练过程中,我们依赖模型当前参数训练得到的loss,根据所选择的优化策略,如Adam、SGD等得到参数步进调整值,对参数进行不断的调整,直到模型达到我们的预期。

但在实际训练过程中,有时候会发现loss变为NaN或Inf的情况,导致训练无法正常进行。出现这种情况的原因主要有以下几点:
1. 梯度爆炸
2. 出现除零、对数函数自变量为负值等数学问题
3. 出现坏样本

1. 梯度爆炸

训练过程中由于学习率等超参数设置的不合理,导致优化过程中没有减小loss,反而因为震荡导致loss逐渐增大,最终超过了float表示范围,出现NaN。这一过程通常伴随渐变与自激的特性,也即在训练到某一轮迭代时对参数调整的步进过大导致模型变差,输出的loss随之增大,此时反向传播的梯度也跟着增大,最终模型的参数越调越差,直至崩溃。
如果我们输出每一轮的loss,就能够发现这一逐渐增大的过程,从而能够基本锁定问题出现在梯度爆炸这一层面上。

解决的方式一般是减小学习率。如果涉及到多个损失函数,可以找出哪一项导致了梯度爆炸,通过减小其权重解决。

2. 不合适的损失函数带来的数学问题

采用一些涉及到除法、对数函数等的损失函数时,要注意除零、对数输入负数等情况,这样同样会导致loss变为NaN。这种情况的出现,一般是在开始训练就出现,不存在loss逐渐增大到爆炸的过程。

一般通过对网络输出值域以及损失函数定义的数学分析可以进行判断,并进行相应地调整。

3. 出现坏样本

坏样本的出现也可能导致loss突然变为NaN。坏样本一般是指不符合任务要求需要被清洗掉的样本,如CV任务中全黑图片等等。坏样本不符合模型训练的要求,因此输入到模型中根本无法得到正常的结果,因此loss会变得巨大以至于成为NaN。
一般情况下,我们需要找出并清理掉坏样本。
如果发现loss在逐步减小但是处理某一批数据时突然变为NaN,一般可以考虑是因为样本的原因。在此基础上,还可以暂时停掉模型的反向传播,也就是无论loss是多少都不进行参数的调整与优化,保持初始参数,再次试验。如果依然是跑到一批数据就出现NaN,就可以排除是模型优化过程中梯度爆炸的问题(模型参数不变,可以排除训练导致的loss爆炸)。

那么如何找出并去掉坏样本呢?我们可以设置训练data loader的batch size为1,逐个样本送入网络,同时设置shuffle为False,也就是不打乱顺序。出现loss为NaN表示遇到了坏样本,此时迭代的批次数,就是该坏样本在数据集的位置,从而定位了坏样本出现位置。排除后重新训练,继续观察loss的情况,直到能正常训练一个epoch,表示所有的坏样本均被去掉,可以开始正常的训练了。

另外也可以在数据集读取样本时保留其原始文件名,同样设置batch size为1,遇到loss变为NaN就输出文件名,更加方便一些。

总的来说,出现loss为NaN的情况,将导致训练无法正常进行,我们需要排查可能的原因并解决。梯度爆炸是最为常见的情况,也最容易想到。而损失函数的数学问题以及坏样本问题,比较难以想到。接触了真实数据而非公开数据集,才更容易了解坏样本对训练的负面影响。只有全面排查,彻底解决loss爆炸的问题,才能够使模型按照我们希望的方向进行优化。

你可能感兴趣的:(那些奇奇怪怪的问题,深度学习,计算机视觉)