nan (数值数据类型的一类值)

代码错误

终于找到了代码里的小bug
计算预测H值的函数带入了conv_raw_prob = conv[:, :, :, :, 3:]
改成pred_prob = pred[:, :, :, :, 3:]成功解决问题

pred_prob = tf.sigmoid(conv_raw_prob)

def compute1(pred_prob, classes):
    a = pred_prob.numpy()
    tensor_shape = a.shape
    b = a*classes
    c = np.reshape(np.sum(b, -1),[4,tensor_shape[1],tensor_shape[1],3,1])
    return tf.convert_to_tensor(c, dtype=tf.float32)

tf.sigmoid函数

应用sigmoid函数可以将输出压缩至0~1的范围
在这里插入图片描述

简介

NaN(NotaNumber,非数)是计算机科学中数值数据类型的一类值,表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。

返回NaN的运算

返回NaN的运算有如下三种:

  • 至少有一个参数是NaN的运算
  • 不定式
    下列除法运算:0/0、∞/∞、∞/−∞、−∞/∞、−∞/−∞
    下列乘法运算:0×∞、0×−∞
    下列加法运算:∞ + (−∞)、(−∞) + ∞
    下列减法运算:∞ - ∞、(−∞) - (−∞)
  • 产生复数结果的实数运算。例如:
    对负数进行开偶次方的运算
    对负数进行对数运算
    对正弦或余弦到达域以外的数进行反正弦或反余弦运算 [1]

tensorflow训练模型出现nan

问题分析

使用TensorFlow、kersa 中实现不同的神经网络,出现Nan值的情况,一般来说有两种情况:
在loss中计算后得到了Nan值
在更新网络权 的时候出现了Nan值

在loss中得到Nan值

检查代码中是否存在 取对数运算,因为对数的真数部分不能为0。有0在取对数会报错,即使那个数经过了sigmoid处理也不行。tf.nn.sigmoid函数,在输出的参数非常大,或者非常小的情况下,会给出边界值1或者0。

在这里插入图片描述
所以我们要给对数运算的真数部分限定一个范围,否则会出现数值下溢的问题,可以使用tf.clip_by_value(input,min_value,max_value)函数来限定真数的下限。

tf.log(cvr)
# 改为下面
tf.log(tf.clip_by_value(cvr, 1e-10, 0.9999))

在更新网络权 的时候出现Nan值

在训练的过程中出现 预测值 为nan,可能数据有问题。要是训练过程中偶尔出现,你就需要 确保大致知道每一层的输出的一个范围,这样才能彻底的解决Nan值的出现,这个太难了。

在训练的过程中出现损失函数值为nan,这种情况一般是由于学习率太大,需要减小学习率;或者是在训练一段时间后出现nan,这种情况可能是由于梯度爆炸导致的,可以对梯度进行裁剪,将梯度的最大值限定在某个常数。

tensorflow NAN常见原因和解决方法

NAN原因

所有NAN都是这个原因:正向计算时节点数值越界,或反向传播时gradient数值越界;

无论正反向,数值越界基本只有三种操作会导致:

a) 节点/W/gradient数值逐渐变大直至越界

b) 有除零操作,包括0/0。

c) 输入数据存在异常,过大/过小的输入,导致瞬间NAN

现象

两者的现象可能一样,绝大多数情况下,NAN都发生在训练一段时间后,报loss NAN,如果你打印了gradient也可能先报gradient NAN。

危险信号

输入没有归一化、tf.sqrt(), relu()+cosine(), 全relu()激活, 过大的learning rate

如果你的NN网络中包含上述三种情况的任何一个,则极有可能触发NAN

解决方法

首先检查你的输入

如果你的输入feature没有归一化,离线归一化到0~1;如果已经归一化,但是不确定是否有个别异常数据,使用tf.clip_by_value(feature, 0.0, 1.0) 对数据进行异常截断。

不推荐: 前向网络内部使用 tf.clip_by_value

网上常见这种方法。这种方法不太好;前向计算时对节点使用会导致梯度消失。反向传播时对gradient使用过于严苛。可以使用tf.clip_by_globalnorm(gradients, clip_norm=1.0) 让收敛更加稳定。该函数在梯度小时不进行操作,在梯度过大时进行全局归一化而不是直接对个别graident截断。对NAN改善有辅助作用。但如果当前代码存在0/2/3/4问题,则不能寄希望于该方法解决已有的NAN问题。

检查正向计算运算边界

现象:loss值或predict score值逐渐变大或有大幅波动,直至NAN

场景:如果你的前向计算都是relu()激活,缺少类似cosine/tanh/sigmoid/layer_norm这种带有归一化能力的激活或norm操作,则很可能随着W方差的增大,出现个别节点数值过大的问题。

解决方法:

a) 减少learning_rate。尝试把lr减少10倍、50倍、100倍。百试不爽,该类问题绝大部分可以得到解决(注意lr也别太小,否则收敛过慢浪费时间,要在速度和稳定性之间平衡)

b) loss增加L2正则,避免随着迭代W越来越大,直至线性运算结果越界;一般L2后不会再NAN,但要注意对效果的影响,如果效果显著变差则不推荐使用。

c) 检查你的variable初始化值域,确保w初始值足够小,常用初始化|w|<<1; 推荐使用 tf.truncated_normal_initializer(stddev=0.02)

d) 若仍然不行,则应考虑在尽量靠前的位置增加tanh/cosine等有收敛边界的激活函数,但这会影响梯度传播,慎用,尽量使用前几种方法。

检查正向运算除0操作

现象:loss值或predict score正常且稳定,甚至逐渐收敛,但突然loss NAN

场景:数据中存在某些值,导致除0。常见于除法操作、归一化操作或cosine计算中。正向除零比较好发现,也比较好改,但应注意正向改好后反向是不是还有问题。

解决方法:

a) ctrl+F 检查所有tf.div和/关键字,确保分母不会为0. 对于分母>=0的情景,可以使用+0.001这种方式保证非0

b) 应警惕在上一步修改后,反向梯度是否仍有除0场景。如cosine中,若分母为0,则反向梯度也是NAN,只改正向是不够的

检查反向除0操作

a) 所有的开方操作,如开平方 y=tf.sqrt(a) 如果a为0或接近与0,会导致gradient(y,a)=NAN

b) cosine操作,y=cosine(a, b),如果||a||或||b||接近0(由于有6次方操作,a[*]均<0.01就会导致float越界),gradient会NAN;这种情况在a=relu©, b=relu(d)的时候会非常严重,因为relu()有一定概率输出全零值。

解决方法:

a) 搜索所有tf.sqrt,尽量删除。如loss=均方差(y,y_),千万不要在均方差外面再套一个开方。否则当loss接近与0时会梯度NAN

b) 对于relu()+cosine()。删除relu()操作,cosine前不要加激活函数,cosine本来就是非线性变换,不需要两个紧邻的非线性变换。线性计算得到的a,b, 基本不会有L2接近0的情形。

猜你喜欢

转载自blog.csdn.net/Winds_Up/article/details/114936022
NaN