Используйте sparse_categorical_crossentropy
и boolean_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))
TestCase
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
похоже, есть ошибка, см. Аналогичную проблему здесь . Таким образом, нам осталось использовать categorical_crossentropy
вместо этого, но теперь основную истину следует преобразовать в горячее кодирование. Мы будем обозначать метки, которые не рассматриваются, используя -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)