【问题标题】:How do I implement a masked softmax cross-entropy loss function in Keras?如何在 Keras 中实现屏蔽的 softmax 交叉熵损失函数?
【发布时间】:2019-10-13 03:38:42
【问题描述】:

我正在尝试在 Keras 中实现 softmax 交叉熵损失。损失应该只考虑标签为 1 或 0 的样本,而忽略标签为 -1 的样本(即缺少标签)。我找到了一个 binary_crossentropy 函数,但我无法为它实现 softmax 版本。

这是binary_crossentropy

def binary_crossentropy(y_true, y_pred):
    return K.mean(K.binary_crossentropy(tf.multiply(y_pred, tf.cast(tf.not_equal(y_true, -1), tf.float32)),
                                    tf.multiply(y_true, tf.cast(tf.not_equal(y_true, -1), tf.float32))), axis=-1)

我尝试用K.categorical_crossentropy 更改K.binary_crossentropy() 函数,但这只会在计算损失时给我“nan”。

如何在 Keras(Tensorflow 后端)上实现这一点?


编辑

this answer中,作者建议使用sparse_crossentropy,但是我在编译模型时遇到了错误:

使用sparse_categorical_crossentropyboolean_mask

def sparse_crossentropy_masked(y_true, y_pred):
    y_true_masked = tf.boolean_mask(y_true, tf.not_equal(y_true, -1))
    y_pred_masked = tf.boolean_mask(y_pred, tf.not_equal(y_true, -1))
    return K.mean(K.sparse_categorical_crossentropy(y_true_masked, y_pred_masked))

测试用例

y_true = tf.constant(np.array([0.,1.,2., -1]))
y_pred = tf.constant(np.array([[1.,0.,0.], [0.,1.,0.], [0.,0.,1.], [0.,0.,1.]]))
loss_op = sparse_crossentropy_masked(y, y_hat)

y_true_1 = tf.constant(np.array([0.,1.,2.]))
y_pred_1 = tf.constant(np.array([[1.,0.,0.], [0.,1.,0.], [0.,0.,1.]]))
loss_1_op = sparse_crossentropy_masked(y_true_1, y_pred_1)

with tf.Session() as sess:
    loss, loss_1 = sess.run([loss_op, loss_1_op])
    assert loss == loss_1



model.compile(loss=sparse_crossentropy_masked)
### TypeError: int returned non-int (type NoneType

【问题讨论】:

  • 是否有特别的理由让这些样本在网络中输入?为什么不简单地在你的训练集中摆脱它?
  • @ThibaultBacqueyrisses 每个样本都与 1 个或多个输出相关联。因此,如果我将它们从训练中删除,我会丢失许多标签的信息。
  • @MarcosSantana,请考虑 mujjiga 的回答和您提到的错误更新您的问题

标签: tensorflow machine-learning keras deep-learning loss-function


【解决方案1】:

使用sparse_categorical_crossentropyboolean_mask

def sparse_crossentropy_masked(y_true, y_pred):
    y_true_masked = tf.boolean_mask(y_true, tf.not_equal(y_true, -1))
    y_pred_masked = tf.boolean_mask(y_pred, tf.not_equal(y_true, -1))
    return K.mean(K.sparse_categorical_crossentropy(y_true_masked, y_pred_masked))

测试用例

y_true = tf.constant(np.array([0.,1.,2., -1]))
y_pred = tf.constant(np.array([[1.,0.,0.], [0.,1.,0.], [0.,0.,1.], [0.,0.,1.]]))
loss_op = sparse_crossentropy_masked(y, y_hat)

y_true_1 = tf.constant(np.array([0.,1.,2.]))
y_pred_1 = tf.constant(np.array([[1.,0.,0.], [0.,1.,0.], [0.,0.,1.]]))
loss_1_op = sparse_crossentropy_masked(y_true_1, y_pred_1)

with tf.Session() as sess:
    loss, loss_1 = sess.run([loss_op, loss_1_op])
    assert loss == loss_1

更新

sparse_categorical_crossentropy 似乎有一个错误,请参阅类似的问题here。所以我们只剩下使用categorical_crossentropy 了,但现在应该将ground truth 转换为one-hot-encoding。我们将使用 -1 表示不考虑的标签(如果您感到困惑,请在下面的代码中打印y

工作示例:

def categorical_crossentropy_masked(y_true, y_pred):
    y_true_masked = tf.boolean_mask(y_true, tf.reduce_any(tf.not_equal(y_true, -1), 1))
    y_pred_masked = tf.boolean_mask(y_pred, tf.reduce_any(tf.not_equal(y_true, -1), 1))
    return K.mean(K.categorical_crossentropy(y_true_masked, y_pred_masked))

inputs = Input(shape=(3,))
outputs = Dense(32, activation='relu')(inputs) 
outputs = Dense(16, activation='relu')(outputs) 
outputs = Dense(3, activation='softmax')(outputs)
model = Model(inputs, outputs)
model.compile(optimizer='adam', loss=[categorical_crossentropy_masked])

x = np.random.randn(100,3)
y = np.random.randint(0,3, size=(100))

y = tf.keras.utils.to_categorical(y)
# make some targets to -1 
y[np.random.randint(0,100, size=(15))] = np.ones((15,y.shape[-1]))*-1.

model.fit(x, y)

【讨论】:

  • 感谢@mujjiga,但是当我使用该函数编译模型时出现以下错误:TypeError: int returned non-int (type NoneType)
猜你喜欢
  • 2019-08-08
  • 2023-03-29
  • 2018-08-07
  • 2021-11-21
  • 2017-03-14
  • 2017-06-26
  • 1970-01-01
  • 2017-12-26
  • 2021-05-29
相关资源
最近更新 更多