具有多个输入的Tensorflow 2.0自定义损失功能

时间:2019-09-20 06:14:36

标签: tensorflow keras tensorflow2.0

我正在尝试使用以下两个损失函数来优化模型

def loss_1(pred, weights, logits):
    weighted_sparse_ce = kls.SparseCategoricalCrossentropy(from_logits=True)
    policy_loss = weighted_sparse_ce(pred, logits, sample_weight=advantages)

def loss_2(y_pred, y):
    return kls.mean_squared_error(y_pred, y)

但是,因为TensorFlow 2期望损失函数的形式为

def fn(y_pred, y_true):
    ...

我正在为loss_1使用一种变通办法,在将predweights打包成一个张量之前,在调用{{3}的过程中传递给loss_1 },然后将其解压缩到loss_1中。这是不雅和令人讨厌的,因为predweights属于不同的数据类型,因此每次我呼叫model.fit时,都需要进行附加的强制转换,打包,解压缩和取消转换。 / p>

此外,我知道sample_weight的{​​{1}}参数,这有点像model.fit的解决方案。如果不是因为我正在使用两个损失函数,而我只希望将fit应用于其中一个函数,则这可能是一个可行的解决方案。而且,即使这是一个解决方案,也无法推广到其他类型的自定义损失函数。


简而言之,我的问题是:

创建任意数量的损失函数的最佳方法是什么 TensorFlow 2中的参数?

我尝试过的另一件事是传递this question,但这似乎也违反了TensorFlow对损失函数输入的期望。

3 个答案:

答案 0 :(得分:2)

扩展乔恩的回答。如果您仍然想拥有 Keras 模型的好处,您可以扩展模型类并编写自己的自定义 train_step:

from tensorflow.python.keras.engine import data_adapter

# custom loss function that takes two outputs of the model
# as input parameters which would otherwise not be possible
def custom_loss(gt, x, y):
    return tf.reduce_mean(x) + tf.reduce_mean(y)

class CustomModel(keras.Model):
    def compile(self, optimizer, my_loss):
        super().compile(optimizer)
        self.my_loss = my_loss

    def train_step(self, data):
        data = data_adapter.expand_1d(data)
        input_data, gt, sample_weight = data_adapter.unpack_x_y_sample_weight(data)

        with tf.GradientTape() as tape:
            y_pred = self(input_data, training=True)
            loss_value = self.my_loss(gt, y_pred[0], y_pred[1])

        grads = tape.gradient(loss_value, self.trainable_variables)
        self.optimizer.apply_gradients(zip(grads, self.trainable_variables))

        return {"loss_value": loss_value}

...

model = CustomModel(inputs=input_tensor0, outputs=[x, y])
model.compile(optimizer=tf.keras.optimizers.Adam(), my_loss=custom_loss)

答案 1 :(得分:1)

使用TF2中的custom training可以轻松解决此问题。您只需要在GradientTape上下文中计算两分量损失函数,然后使用产生的梯度调用优化器即可。例如,您可以创建一个函数custom_loss来计算给定每个参数的两个损耗:

def custom_loss(model, loss1_args, loss2_args):
  # model: tf.model.Keras
  # loss1_args: arguments to loss_1, as tuple.
  # loss2_args: arguments to loss_2, as tuple.
  with tf.GradientTape() as tape:
    l1_value = loss_1(*loss1_args)
    l2_value = loss_2(*loss2_args)
    loss_value = [l1_value, l2_value]
  return loss_value, tape.gradient(loss_value, model.trainable_variables)

# In training loop:
loss_values, grads = custom_loss(model, loss1_args, loss2_args)
optimizer.apply_gradients(zip(grads, model.trainable_variables))

通过这种方式,每个损失函数都可以采用任意数量的渴望张量,无论它们是模型的输入还是输出。每个损失函数的参数集都不需要像本示例中所示那样相交。

答案 2 :(得分:0)

在tf 1.x中,我们具有tf.nn.weighted_cross_entropy_with_logits函数,该函数可以通过为每个类添加额外的正权重来权衡查全率和查准率。在多标签分类中,它应该是(N,)张量或numpy数组。但是,在tf 2.0中,我还没有找到类似的损失函数,因此我编写了自己的损失函数,并带有额外的参数pos_w_arr

from tensorflow.keras.backend import epsilon

def pos_w_loss(pos_w_arr):
    """
    Define positive weighted loss function
    """
    def fn(y_true, y_pred):
        _epsilon = tf.convert_to_tensor(epsilon(), dtype=y_pred.dtype.base_dtype)
        _y_pred = tf.clip_by_value(y_pred, _epsilon, 1. - _epsilon)
        cost = tf.multiply(tf.multiply(y_true, tf.math.log(
            _y_pred)), pos_w_arr)+tf.multiply((1-y_true), tf.math.log(1-_y_pred))
        return -tf.reduce_mean(cost)
    return fn

不确定使用急切的张量或numpy数组作为输入时,这是什么意思。如果我错了,请纠正我。