代码错误
终于找到了代码里的小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的情形。