炼丹手册——NaN值问题

对于会写BUG的炼丹师在神经网络的训练阶段,相信经常遇到loss值特别大或者出现NaN的情况。近期我自己把YOLOv1-v4手撸了一把,果不其然我是个善于写BUG的炼丹师,给自己挖了各种细节坑,然后又经历了一点点排查填坑的过程。废话到此为止,我们来说说loss出现特别大或者NaN的问题及其解决方法,内容包括自己踩的坑以及网上别的小伙伴的总结:

权重初始化造成loss异常

神经网络在训练过程中基于梯度下降法优化参数,在训练之前需要给每个参数一个初始值,不合适的初始值有可能造成loss异常,参数初始化的方式有如下几种:

  1. 常量初始化zeros_initializer、ones_initializer、constant_initializer等,一般不用这种方式初始化权重,因为如果初始权重为0,无法有效计算;如果初始权重为1或常量,会有较大的变化;
  2. 随机初始化random_uniform_initializer、random_normal_initializer、Xavier initialization、He initialization

如果是初始化问题造成的损失异常可以尝试在某个预训练模型的基础上做finetune,或者更换初始化方式,推荐使用Xavier initialization或者He initialization其中一种。

学习率大造成loss异常

学习率太大有可能引起梯度值过大产生梯度爆炸从而出现NaN值,学习率的大小通常和神经网络的深度有一定关系,由于网络越深在反向传播时链式法则的求导会不断放大梯度所以建议学习率越小,针对这个问题可以采用以下几个解决方法:

  1. 调节学习率,把lr以10倍下降的方式结合自身网络深度进行测试;
  2. 调节网络结构,梯度值异常增长会随着网络深度的而加剧,减少网络深度或者采用Highway等方式设计网络;
  3. 加入梯度裁剪,每当梯度达到一定的阈值,就把它拉回一个小的数值;

数据本身造成loss异常

  1. 数据集存在一些脏数据,比如输入中就含有NaN/INF,每当学习的过程中碰到这个错误的输入,就会变成NaN,这时候我们需要编写小程序过滤下数据集确保没有损坏的图片(现实场景的脏数据还是挺多的,建议话更多时间在处理数据上)。
  2. 数据存在较大的值在反向传播过程中有可能回传的梯度较大,一般建议对数据做归一化/标准化操作以及添加Batch Norm层;

数据处理造成loss异常

  1. 参数初始化错误,例如模型输出的类别与实际的类别数不匹配等;
  2. 数据预处理有误,这个是我自己code的时候误操作出现的,一般检测网络有一个把标签换算成真值的过程,如果转换的真值是相对于原图做的归一化,那么网络预测的结果也需要转换成原图归一化大小,具体点说YOLOv2-5系列的网络预测的xy是相对于每个Grid的偏移,预测的wh是相对于anchor的偏移,在计算预测值和真值之间的损失时需要将它们还原在同一个空间(原图空间或特征图空间)。
  3. 存在非法计算,例如出现分母是0或者log(0)的情况就会出现NaN,所以一般在设计时会加上一个很小的常数(tf.keras.backend.epsilon等于1e-7),比如:
    计算
    分母增加一个ε避免除以0的情况;

    计算
    需要取对数将
    转换成
    ,将数据压缩在一个范围避免出现log(0)的情况:
    ,tf.clip_by_value这个函数是将第一个参数,限制在第二、三个参数指定的范围之内。

    计算交叉熵同样有类似的问题需要增加一个余量:
    ;

你可能感兴趣的:(网络,神经网络,人工智能,深度学习,机器学习)