为何输入缩放后tf.keras.CategoricalCrossentropy与softmax_cross_entropy_with_logits结果差异显著?
为什么CategoricalCrossentropy输入经softmax后与softmax_cross_entropy_with_logits结果差异巨大?
我原本以为,当CategoricalCrossentropy(CCE)的输入经过softmax处理后,它和tf.nn.softmax_cross_entropy_with_logits的计算结果应该完全一致。但实际测试发现,当输入X的数值较小时,两者结果接近;但把X数值放大10倍后,结果出现了巨大差异。
测试代码如下:
import numpy as np import tensorflow as tf # 测试用例1:X数值较小 X = np.array([[3.0, 1.0, 1.0], [-1.0, 2.0, 5.0]]) Y = np.array([[1, 0.0, 0.0], [0.0, 1.0, 0.0]]) cce = tf.keras.losses.CategoricalCrossentropy(from_logits=False, reduction=tf.keras.losses.Reduction.NONE) cce_res = cce(y_true = Y, y_pred=tf.math.softmax(X)) sce_res = tf.nn.softmax_cross_entropy_with_logits(logits=X, labels=Y) cost1 = tf.reduce_mean(cce_res) cost2 = tf.reduce_mean(sce_res) print(cost1) print(cost2) # 输出: # tf.Tensor(1.645245261490345, shape=(), dtype=float64) # tf.Tensor(1.6452452648724416, shape=(), dtype=float64) # 测试用例2:X数值放大10倍 X = np.array([[3.0, 1.0, 1.0], [-1.0, 2.0, 5.0]]) * 10 Y = np.array([[1, 0.0, 0.0], [0.0, 1.0, 0.0]]) cce = tf.keras.losses.CategoricalCrossentropy(from_logits=False, reduction=tf.keras.losses.Reduction.NONE) cce_res = cce(y_true = Y, y_pred=tf.math.softmax(X)) sce_res = tf.nn.softmax_cross_entropy_with_logits(logits=X, labels=Y) cost1 = tf.reduce_mean(cce_res) cost2 = tf.reduce_mean(sce_res) print(cost1) print(cost2) # 输出: # tf.Tensor(8.059047748974614, shape=(), dtype=float64) # tf.Tensor(15.0000000020612, shape=(), dtype=float64)
核心原因分析
差异来源于两个关键点:
- 数值稳定的计算方式:
tf.nn.softmax_cross_entropy_with_logits内部通过log-sum-exp等数学技巧直接对logits计算,跳过显式softmax步骤,避免了logits数值过大时,softmax输出趋近于0或1带来的精度损失。比如第二个测试用例中,第二个样本的logits是[-10,20,50],该函数能精准计算出交叉熵为30,平均后得到15。 - 输入裁剪机制:
tf.keras.losses.CategoricalCrossentropy默认带有epsilon=1e-7参数,会将输入的y_pred裁剪到[epsilon, 1-epsilon]范围,防止计算log(y_pred)时出现无意义的log(0)。当logits数值极大时,softmax输出的极小值会被替换为1e-7,此时log(1e-7)≈-16.118,取反后得到的交叉熵约为16.118,和第一个样本的近似0值平均后得到8.059,这就是两者结果差异的直接原因。
解决方式
如果想让两者结果一致,有两种可行方法:
- 使用
CategoricalCrossentropy时设置from_logits=True,此时它内部会调用和softmax_cross_entropy_with_logits一致的数值稳定计算逻辑,无需手动做softmax。 - 手动设置
epsilon=0(不推荐,可能导致数值不稳定),同时确保计算环境的浮点数精度足够,但这种方式依然存在logits过大时的数值溢出风险。
内容的提问来源于stack exchange,提问作者user1269298
相关产品推荐
相关产品推荐

