有条件地创建零梯度的自定义 Keras 损失函数

Custom Keras loss function that conditionally creates a zero gradient

我的问题是,如果 y_true 取某些值,我不想调整权重。由于我尝试使用的 RNN 的性质,我不想简单地从训练数据中删除这些示例。

有没有办法用这种行为在 Keras 中编写条件损失函数?

例如:如果 y_true 为负,则应用零梯度,以便模型中的参数不会改变,如果 y_true 为正 loss = losses.mean_squared_error(y_true, y_pred)

由于 y 是分批的,因此您需要 select 那些在自定义损失函数中非零的批次

def myloss(y_true, y_pred):
    idx  = tf.not_equal(y_true, 0)
    y_true = tf.boolean_mask(y_true, idx)
    y_pred = tf.boolean_mask(y_pred, idx)
    return losses.mean_squared_error(y_true, y_pred)

那么可以这样使用:

model = keras.Sequential([Dense(32, input_shape=(2,)), Dense(1)])
model.compile('adam', loss=myloss)

x = np.random.randn(2, 2)
y = np.array([1, 0])
model.fit(x, y)

但是如果批处理中的所有 y_true 都为零,您可能需要损失函数中的额外逻辑,在这种情况下,loss 函数可以修改为:

def myloss2(y_true, y_pred):
    idx  = tf.not_equal(y_true, 0)
    y_true = tf.boolean_mask(y_true, idx)
    y_pred = tf.boolean_mask(y_pred, idx)
    loss = tf.cond(tf.equal(tf.shape(y_pred)[0], 0), lambda: tf.constant(0, dtype=tf.float32), lambda: losses.mean_squared_error(y_true, y_pred))
    return loss

您可以定义自定义损失函数并简单地使用 K.switch 有条件地获得零损失:

from keras import backend as K
from keras import losses

def custom_loss(y_true, y_pred):
    loss = losses.mean_squared_error(y_true, y_pred)
    return K.switch(K.flatten(K.equal(y_true, 0.)), K.zeros_like(loss), loss)

测试:

from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(1, input_shape=(1,)))

model.compile(loss=custom_loss, optimizer='adam')

weights, bias = model.layers[0].get_weights()

x = np.array([1, 2, 3])
y = np.array([0, 0, 0])

model.train_on_batch(x, y)

# check if the parameters has not changed after training on the batch
>>> (weights == model.layers[0].get_weights()[0]).all()
True

>>> (bias == model.layers[0].get_weights()[1]).all()
True