Tensorflow训练时某一个batch开始loss突然变为nan(tfdbg使用案例)

版权声明:本文章为原创,未经许可不得转载 https://blog.csdn.net/weixin_41864878/article/details/89317760

关于Loss突然变成nan的问题,网上大多搜出来都是梯度爆炸导致的,这里我们还是要分情况讨论
首先明确训练过程何时出现的nan
(1)一开始迭代loss就是nan:这种情况就属于梯度爆炸引起的loss值始终为nan
(2)到训练的中后期突然变成nan(训练能正常迭代n步):这不属于梯度爆炸,往往和计算loss时引入的log函数有关,也是本文重点解决问题

梯度爆炸引起的loss值为nan的解决方法

这类方法网上已有很多的说明
(1)减小学习率:1e-4~1e-6为宜,如果比1e-6还要小还是会出现nan,那就要考虑调整网络结构
(2)减小batch size:我个人觉得实在显存足够的情况下不太寄希望于这个参数太多…………
(3)引入正则项:例如在每一层的kernel_regularizer参数下引入l2正则项,这样可以不用考虑梯度裁剪(这一点后面会讲到)
(4)加入BN层:我一般加在激活函数之后,每一层都会加,网上关于BN层加在激活函数之前好还是之后好众说纷纭,还是自己做实验看看哪种效果好再决定
(5)对输入数据做归一化处理
(6)梯度裁剪:梯度裁剪的方式有两种
一种是对于部分梯度进行裁剪(我自己的理解就是由网络结构引入的梯度计算)

optimizer = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
grads = optimizer.compute_gradients(loss)
for i, (g, v) in enumerate(grads):
    if g is not None:
        grads[i] = (tf.clip_by_norm(g, 5), v)  # 阈值这里设为5
train_op = optimizer.apply_gradients(grads)

第二种是对于全局梯度计算完毕之后做裁剪

optimizer = tf.train.AdamOptimizer(learning_rate=0.001, beta1=0.5)
grads, variables = zip(*optimizer.compute_gradients(loss))
grads, global_norm = tf.clip_by_global_norm(grads, 5)
train_op = optimizer.apply_gradients(zip(grads, variables))

这两者的区别我看到别的博客说是计算时间上的差别,第二种会更加费时。由于我没有针对性的做实验所以也不能给出结论。
再来说说梯度裁剪和正则项的引入之间的关系,因为本质上梯度裁剪就是把大于1的梯度通过L2正则项进行处理,使其小于1(公式不想手打了,图片来自https://blog.csdn.net/guolindonggld/article/details/79547284)
在这里插入图片描述所以当在网络结构定义中引入了kernel_regularizer的L2正则化参数后,梯度裁剪就不会带来任何收益(没错我就是尝试梯度裁剪发现没有任何用处之后才发现的)
针对这一点大家可以采用tf.gradients()函数来看加入梯度裁剪之前和之后网络梯度的变化

引入log(0)引起的loss值为nan的解决方法

这个log函数在很多时候都是隐形的,就是包含在tensorflow写好的api中,我们调用了此类函数但是并不知道函数里面其实做了log操作
(1)交叉熵的使用
在计算交叉熵的过程中会引入 y t r u t h l o g ( y p r e d i c t ) y_{truth} * log(y_{predict}) ,因此当softmax输出的预测值如果概率为0,就会导致nan值
改善方法:
重写交叉熵函数:在预测值后加一个很小的正数cross_entropy = y_{truth}*log(y_{pre} + 1e-10),之后做一个reduce_mean就是最后的loss输出
重写log函数:就是要对代码中用到的log函数进行修正tf.log(tf.clip_by_value(tf.sigmoid(y_pre), 1e-8, tf.reduce_max(y_pre)))
(2)tensorflow自带的debug工具
如果怎么调整网络结构都不管用,恭喜您,喜提tfdbg工具包一套

from tensorflow.python import debug as tf_debug
##在session中导入tfdbg,并加入检查nan值的filters
sess = tf.Session()
sess = tf_debug.LocalCLIDebugWrapperSession(sess)
sess.add_tensor_filter("has_inf_or_nan", tf_debug.has_inf_or_nan)

然后正常执行代码,在tfdbg命令窗口输入tfdbg> run -f has_inf_or_nan,程序就会运行到出现nan值的时候允许debug,代价就是这样run的速度大概变慢了5倍
之后就可以根据报错的节点查找nan值出现的地方排查
具体可以参考https://zhuanlan.zhihu.com/p/33264569

更新于2019/6/11
tfdgb用来找nan值真的蛮好用,前提是你的nan会在代码中出现的很早
步骤如下:
正常执行python test.py后,代码会到建立session时出现tfdbg会话窗口
然后输入
在这里插入图片描述
回车后程序会自动退出到正常界面开始运行,然后就是等待出现nan值的时候,会自动跳回tfdbg,并按时间先后顺序显示nan值出现的tensor名,然后就可以开始排查了,问题大概率都是由第一个tensor引起的,其实看tensor名应该大致就知道问题所在了。
我这里第一个tensor名是数据的tensor,所以大致就应该知道是数据出了问题
在这里插入图片描述
这里可以用pt IteratorGetNext:1来查看tensor中的值,pt -a是全部打印,我就不展示了,确实是数据中出现了nan值
在这里插入图片描述
参考:
https://blog.csdn.net/accumulate_zhang/article/details/79890624
https://blog.csdn.net/shwan_ma/article/details/80472996
https://blog.csdn.net/leadai/article/details/79143002
https://zhuanlan.zhihu.com/p/33264569

猜你喜欢

转载自blog.csdn.net/weixin_41864878/article/details/89317760