手动实现Keras二元交叉熵损失与官方实现结果不一致问题求解
问题原因排查及修复方案
核心错误1:输入语义与参数设置不匹配
你调用官方tf.keras.losses.BinaryCrossentropy时设置了from_logits=True,代表传入损失的是未经过sigmoid激活的原始logits输出,值域为(-∞, +∞);而你自定义的损失函数默认输入为0~1之间的概率值,直接对输入做K.clip、取对数的操作,完全不适用于logits输入:
- 所有小于
K.epsilon()的负logits会被强行截断为极小的概率值,所有大于1的正logits会被截断为接近1的概率值,原始输出语义被完全扭曲,损失计算自然和官方结果差异极大。
核心错误2:判别器损失逻辑完全错位
原始官方判别器损失的计算逻辑是分别计算两部分损失再求和:
- 真实样本输入判别器的输出与全1标签计算BCE(要求判别器把真样本判为真)
- 生成样本输入判别器的输出与全0标签计算BCE(要求判别器把假样本判为假)
而你写的new_d_loss错误地将real_output作为标签、generated_output作为预测值,完全没有对应上述两部分损失的计算逻辑,结构本身就是错的。
核心错误3:生成器损失训练方向反转
原始官方生成器损失的逻辑是:生成样本输入判别器的输出与全1标签计算BCE,要求判别器把假样本判为真,对应公式为-log(y_pred);而你写的new_g_loss计算的是-log(1 - y_pred),刚好和目标相反,会引导生成器生成更容易被判别器识别的假样本,训练完全走偏。
修复后的自定义损失代码(与官方from_logits=True逻辑完全对齐)
import tensorflow.keras.backend as K def custom_bce_from_logits(y_true, y_pred_logits): # 先将logits通过sigmoid转为概率值,和官方from_logits=True逻辑对齐 y_pred = K.sigmoid(y_pred_logits) y_pred = K.clip(y_pred, K.epsilon(), 1 - K.epsilon()) term_0 = (1 - y_true) * K.log(1 - y_pred) term_1 = y_true * K.log(y_pred) return -K.mean(term_0 + term_1) def new_d_loss(real_output, generated_output): # 分别计算真实样本、生成样本的损失再求和,和官方逻辑对齐 real_loss = custom_bce_from_logits(tf.ones_like(real_output), real_output) fake_loss = custom_bce_from_logits(tf.zeros_like(generated_output), generated_output) return real_loss + fake_loss def new_g_loss(generated_output): # 生成样本要求判别器判为真,标签为全1 return custom_bce_from_logits(tf.ones_like(generated_output), generated_output)
验证方法
你可以用随机生成的模拟logits输入,分别调用官方损失和自定义损失计算结果,两者数值误差在1e-6以内即为对齐成功。
内容的提问来源于stack exchange,提问作者Danyal Bustan
相关产品推荐
相关产品推荐

