Keras自定义损失函数出现NaN及reduction_indices类型错误如何解决
问题原因分析
1 最大值API调用错误
Keras后端的K.max接口作用是对单个张量沿指定维度计算最大值,第二个入参为待降维的轴索引,要求传入int32/int64类型。两次调用均不符合该API的参数要求:
- 第一次将浮点型阈值作为第二个参数传入,系统误判为轴参数,触发
TypeError类型错误 - 第二次将两个张量放入列表传入
K.max,因两个张量形状不匹配无法堆叠计算,触发形状不匹配错误
要实现两个张量逐元素取最大值的需求,应该使用K.maximum接口,或者直接用更便捷的K.clip接口做范围截断。
2 阈值书写错误
第二次修改代码时使用的阈值是[[00000001]],缺少小数点,实际等价于整数1,完全无法起到避免log输入为0的作用。
3 损失公式错误(核心问题)
当前书写的损失公式不符合二元交叉熵的标准形式:
标准二元交叉熵公式为 -(y_true * log(y_pred) + (1-y_true) * log(1-y_pred)),现有代码中误将乘号写为减号,会导致损失值计算完全偏离预期,甚至出现梯度爆炸,最终输出全为nan。
4 输出层激活缺失(潜在问题)
要计算log(y_pred)和log(1-y_pred),要求y_pred的取值范围必须在(0,1)区间内。如果输出层没有添加sigmoid激活函数,y_pred可以输出任意负数或者大于1的值,直接计算log也会出现nan。
解决方案
步骤1 修正输出层配置
输出维度为1的Dense层必须添加sigmoid激活函数,确保y_pred输出范围在(0,1)之间:
# 输出层示例 output = Dense(1, activation='sigmoid')(x)
步骤2 实现数值稳定的自定义损失
使用K.clip接口截断y_pred的取值范围,同时修正损失公式,完整代码如下:
def loss(self, y_true, y_pred): epsilon = 1e-7 # 截断y_pred避免log输入为0或1 y_pred_clipped = K.clip(y_pred, epsilon, 1 - epsilon) # 正确的二元交叉熵计算 binary_crossentropy = -(y_true * K.log(y_pred_clipped) + (1 - y_true) * K.log(1 - y_pred_clipped)) return K.mean(binary_crossentropy, axis=-1)
如果坚持使用逐元素取最大值的方式实现截断,可以替换为如下写法,效果与K.clip一致:
y_pred_clipped = K.maximum(K.minimum(y_pred, 1 - epsilon), epsilon)
内容的提问来源于stack exchange,提问作者mossishahi
相关产品推荐
相关产品推荐

