You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Keras二元交叉熵损失函数出现NaN输出的原因咨询

自定义二元交叉熵损失出现NaN的原因解析

我来帮你拆解下为什么你自己写的二元交叉熵损失会迭代几次就变成NaN——这其实是自定义损失函数时非常常见的数值稳定性坑,结合你提到的sigmoid激活和二元交叉熵公式,核心原因主要有这两点:

1. Sigmoid输出的边界值触发log(0),产生负无穷

你用的二元交叉熵公式是 y * log(p) + (1-y) * log(1-p),其中p是sigmoid的输出。但sigmoid的数学输出范围是**(0,1)**,实际工程计算中,当模型的输入logit(sigmoid的输入值)绝对值很大时,比如logit=100,sigmoid的输出会无限趋近于1;logit=-100时,输出无限趋近于0。

这时候计算log(p)或log(1-p)就会变成log(0),而log(0)在数值计算中会被处理成负无穷(-inf)。当你的batch里存在这样的样本时,损失值会混入-inf,反向传播时梯度计算会直接变成NaN,经过几次迭代后,模型参数被NaN污染,后续的损失输出自然全是NaN。

举个例子,你最初的自定义损失可能是类似这样的:

def custom_bce(y_true, y_pred):
    p = tf.sigmoid(y_pred)
    return -tf.reduce_mean(y_true * tf.math.log(p) + (1 - y_true) * tf.math.log(1 - p))

这段代码完全符合公式,但没有做任何数值稳定处理,一旦遇到p趋近于0或1的情况,就会触发NaN。

2. 梯度传播中的NaN污染

当损失计算中出现-inf后,反向传播时对这个值求导的结果会是NaN。模型参数更新时会引入NaN,下一次迭代的前向计算就会基于带NaN的参数,导致所有输出都变成NaN,进入恶性循环。

TensorFlow官方实现的解决思路

TensorFlow的BinaryCrossentropy损失函数做了专门的数值稳定优化:

  • 如果你设置from_logits=True,它会直接基于logit计算损失,而不是先算sigmoid再算log。内部会把sigmoid和log的计算合并成更稳定的公式,完全等价于-log(sigmoid(logits)),但避免了直接计算log(0)的情况。
  • 即使你不设置from_logits=True,官方实现也会给sigmoid的输出加上一个极小的epsilon(比如1e-7),把p的范围限制在[epsilon, 1-epsilon],确保log计算不会出现负无穷。

总结一下,你最初的损失函数出现NaN的核心原因就是缺乏数值稳定处理,直接计算sigmoid边界值的log导致负无穷,进而引发梯度和参数的NaN污染。

内容的提问来源于stack exchange,提问作者user1502981

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:44:11