caffe报错(1)
当使用caffe训练网络,loss出现Nan,说明你的loss已经发散了。
loss不收敛,可能的原因
1. 检查label是否从0开始
2. 输入数据和输出数据有误
通常我们都会保证输入的数据是否正确(这个要是不能保证那么后续也就没必要继续了…)。一般输入不正确的话可以立马观察出来。
但有两种情况可能并不是那么容易检测到:
- 数据比较多,99%的数据是对的,但有1%的数据不正常,或者损坏,在训练过程中这些数据往往会造成nan或者inf,这时候需要仔细挑选自己的数据,关于如何挑选数据(https://oldpan.me/archives/how-to-use-tricks-to-train-network)。
- 训练过程中跳出了错误的数据,这是需要在IDE或者通过其他途径对运行中的程序进行分析。
这时我们要注意的是在训练过程中的输入和输出是否正确,可以在自己代码中添加检测函数。
3. 学习率过大
base_lr这个参数在自己训练新网络时,可以从0.1开始尝试,如果loss不下降的意思,那就降低,除以10,用0.01尝试,一般来说0.01会收敛,不行的话就用0.001。
原因很简单,学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。如下图,过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)。
另外,这种情况很容易在网络层数比较深的时候出现,借用gluon的一段话:
4. 损失函数可能不正确
损失函数也是有可能导致输出nan,尤其是在我们自己设计损失函数的时候。
损失函数应该考虑到是否可以正常地backward。
其次对输入的Tensor是否进行了类型转化,保证计算中保持同一类型。
最后考虑在除数中加入微小的常数保证计算稳定性。
5.batchNorm可能捣鬼
如果你的网络中batchNorm层很多,而且充当比较重要的角色,那么可以适当地检查一下Tensor在输入Batchnorm层后有没有可能变为nan,如果恰好发生这种情况,batchNorm层中的移动均值(running_mean)和移动方差(running_var)也很有可能都是nan,而且这种情况很有可能发生在预测阶段。
这种情况通过发生在训练集和验证集是两个截然不同的分布的时候,这是在训练集中学习到的均值和方法在验证集中是没有作用反而会捣乱。或者在一个神经网络中存在两种结构不同的阵营:典型的是Unet,当在自定义Unet的时候,编码网络和解码网络如果是两个结构存在较大差异的网络,那么在编码阶段学习到的分布在解码阶段就会出现问题。
举个真实的例子:Unet + resnet34 表现正常,但是使用Unet + resnext50 则造成损失爆炸(将解码阶段的batchnorm层失效后表现正常)。
当然上述现象出现的原因大部分在当我们使用model.eval()(Pytorch)之后发生。如果你在预测阶段也将模型model设置为model.train(True),那么问题可能就不会出现。
6.采用stride大于kernel size的池化层
在卷积层的卷积步伐大于卷积核大小的时候,有可能产生nan:
比如:
layer {
name: "faulty_pooling"
type: "Pooling"
bottom: "x"
top: "y"
pooling_param {
pool: AVE
stride: 5
kernel: 3
}
}
7.你的Shuffle设置有没有乱动
Suffle即洗牌的意思,如果我们在数据加载阶段将Shuffle参数设置在True,那么在神经网络的读取数据的时候,将会打乱顺序去读取,也就是不按照数据的排列顺序去读取。
一般我们是在训练阶段开启shuffle而在预测阶段关闭shuffle,训练阶段在进行每个epoch的时候开启shuffle可以使数据充分地被随机过一遍(类似于我们烤鱼,我们选择是频繁将鱼上下翻面(shuffle)或者只翻一次面,每次烤很长时间),这样训练的鲁棒性比不shuffle稍高一些。
但是假如我们使用了batch_norm层,并且数据的分布极不规律(使用shuflle和不使用shuffle读取的数据顺序的信息分布完全不同),那么在训练阶段训练好的模型(使用shuffle),在预测阶段使用的时候(不使用shuffle),由于数据分布的不同,也是有可能导致batch_norm层出现nan,从而导致不正常的损失函数出现。
8. 网络设计不合理
如果做很复杂的分类任务,却只用了很浅的网络,可能会导致训练难以收敛。
解决办法
- 原始数据处理:
减均值、除方差或者加入normalization(例如BN、L2 norm等) - 更换参数初始化方法:
对于CNN,一般用xavier或者msra的初始化方法 - 学习率:
(1)减小整体学习率α。学习率比较大的时候,参数可能over shoot了,结果就是找不到极小值点。减小学习率可以让参数朝着极值点前进。
(2)改变层的学习率。每个层都可以设置学习率,可以尝试减小后面层的学习率试试。 - 网络:
(1)改变网络宽度。有可能是网络后面的层参数更新异常,增加后面层的宽度试试。
(2)增加网络层数。 - batch_size: 减小bacthsize
- 加入gradient clipping