我注意到在培训期间经常出现的情况是NAN
被引入。
很多时候,它似乎是由内积/全连接或卷积层爆炸的权重引入的。
这是因为梯度计算正在爆炸吗?还是因为权重初始化(如果是,为什么权重初始化会有这个效果)?还是可能是由输入数据的性质引起的?
这里的首要问题很简单:在训练期间出现 NAN 的最常见原因是什么?其次,有哪些方法可以解决这个问题(以及它们为什么有效)?
我多次遇到这种现象。以下是我的观察:
梯度爆炸
原因:大梯度会使学习过程偏离轨道。
您应该期待什么:查看运行时日志,您应该查看每次迭代的损失值。您会注意到,每次迭代中损失开始显着增长,最终损失将太大而无法用浮点变量表示,它将变为 nan
。
你能做什么:将 base_lr
(在solver.prototxt 中)减少一个数量级(至少)。如果您有多个损失层,您应该检查日志以查看导致梯度爆炸的层并减少该特定层的 loss_weight
(在 train_val.prototxt 中),而不是一般的 base_lr
。
糟糕的学习率策略和参数
原因: caffe 未能计算出有效的学习率,而是得到 'inf'
或 'nan'
,这个无效的学习率乘以所有更新,从而使所有参数无效。
您应该期待什么:查看运行时日志,您应该会看到学习率本身变为 'nan'
,例如:
... sgd_solver.cpp:106] 迭代 0,lr = -nan
您可以做什么:修复您的 'solver.prototxt'
文件中影响学习率的所有参数。
例如,如果您使用 lr_policy: "poly"
并且忘记定义 max_iter
参数,您最终会是 lr = nan
...
有关 caffe 学习率的更多信息,请参阅 this thread。
损失函数错误
原因: 有时损失层中损失的计算会导致出现 nan
。例如,Feeding InfogainLoss
layer with non-normalized values,使用带有错误的自定义损失层等。
您应该期待什么:查看运行时日志您可能不会注意到任何异常:损失逐渐减少,并且突然出现 nan
。
你能做什么:看看你是否可以重现错误,将打印输出添加到损失层并调试错误。
例如:一旦我使用了一个损失,它通过批次中标签出现的频率对惩罚进行了归一化。碰巧的是,如果其中一个训练标签根本没有出现在批次中 - 计算的损失会产生 nan
。在这种情况下,使用足够大的批次(相对于集合中的标签数量)足以避免此错误。
输入错误
原因:您有一个包含 nan
的输入!
您应该期待什么:一旦学习过程“命中”这个错误的输入 - 输出就会变成 nan
。查看运行时日志,您可能不会注意到任何异常:损失逐渐减少,突然出现 nan
。
您能做什么: 重新构建您的输入数据集 (lmdb/leveldn/hdf5...),确保您的训练/验证集中没有错误的图像文件。对于调试,您可以构建一个简单的网络来读取输入层,上面有一个虚拟损失并遍历所有输入:如果其中一个有故障,这个虚拟网络也应该产生 nan
。
步幅大于“池化”层中的内核大小
出于某种原因,选择 stride
>用于池化的 kernel_size
可能会产生 nan
。例如:
layer {
name: "faulty_pooling"
type: "Pooling"
bottom: "x"
top: "y"
pooling_param {
pool: AVE
stride: 5
kernel: 3
}
}
y
中有 nan
的结果。
“BatchNorm”中的不稳定性
据报道,在某些设置下,"BatchNorm"
层可能会由于数值不稳定而输出 nan
。
此 issue 在 bvlc/caffe 中提出,PR #5136 正在尝试修复它。
最近,我意识到 debug_info
标志:在 'solver.prototxt'
中设置 debug_info: true
将使 caffe 打印在训练期间记录更多的调试信息(包括梯度幅度和激活值):这些信息可以 help in spotting gradient blowups and other problems in the training process。
就我而言,原因是未在卷积/反卷积层中设置偏差。
解决方案:将以下内容添加到卷积层参数中。
bias_filler {
type: "constant"
value: 0
}
这个答案不是关于 nan
的原因,而是提出了一种帮助调试它的方法。你可以有这个 python 层:
class checkFiniteLayer(caffe.Layer):
def setup(self, bottom, top):
self.prefix = self.param_str
def reshape(self, bottom, top):
pass
def forward(self, bottom, top):
for i in xrange(len(bottom)):
isbad = np.sum(1-np.isfinite(bottom[i].data[...]))
if isbad>0:
raise Exception("checkFiniteLayer: %s forward pass bottom %d has %.2f%% non-finite elements" %
(self.prefix,i,100*float(isbad)/bottom[i].count))
def backward(self, top, propagate_down, bottom):
for i in xrange(len(top)):
if not propagate_down[i]:
continue
isf = np.sum(1-np.isfinite(top[i].diff[...]))
if isf>0:
raise Exception("checkFiniteLayer: %s backward pass top %d has %.2f%% non-finite elements" %
(self.prefix,i,100*float(isf)/top[i].count))
在您怀疑可能会导致问题的某些点将此层添加到您的 train_val.prototxt
中:
layer {
type: "Python"
name: "check_loss"
bottom: "fc2"
top: "fc2" # "in-place" layer
python_param {
module: "/path/to/python/file/check_finite_layer.py" # must be in $PYTHONPATH
layer: "checkFiniteLayer"
param_str: "prefix-check_loss" # string for printouts
}
}
learning_rate 很高,应该降低 RNN 代码中的准确率是 nan,选择它修复的学习率的低值
我试图构建一个稀疏的自动编码器,并在其中有几个层来诱导稀疏性。在运行我的网络时,我遇到了 NaN。在删除一些层时(在我的情况下,我实际上必须删除 1),我发现 NaN 消失了。所以,我想太多的稀疏性也可能导致 NaN(可能已经调用了一些 0/0 计算!?)
nan
的配置和固定配置的详细信息?什么类型的图层?什么参数?