【问题标题】:Tensorflow 2.0 Custom loss function with multiple inputs具有多个输入的 Tensorflow 2.0 自定义损失函数
【发布时间】:2020-01-21 04:17:24
【问题描述】:

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

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 打包到一个张量中,然后在对model.fit 的调用中传递给loss_1,然后在loss_1 中解压缩它们。这是不优雅和讨厌的,因为predweights 是不同的数据类型,所以每次我调用model.fit 时都需要额外的强制转换、打包、解包和取消转换。

此外,我知道fitsample_weight 参数,这有点像this question 的解决方案。如果不是因为我使用了两个损失函数并且我只想将sample_weight 应用于其中一个,这可能是一个可行的解决方案。此外,即使这是一个解决方案,它是否也不能推广到其他类型的自定义损失函数。


说了这么多,我的问题,简洁地说,是:

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

我尝试过的另一件事是传递 tf.tuple,但这似乎也违反了 TensorFlow 对损失函数输入的要求。

【问题讨论】:

  • 使用闭包怎么样?基本上,你可以定义一个标准的损失函数,我们命名为inside_loss,它只在你的loss_1 中使用(y_truey_pred)。您可以将权重或 logits、任何参数传递给 loss_1。最后,您的loss_1 将返回inside_loss 这个函数。这很像我们自定义 keras 损失函数的方式。 github.com/keras-team/keras/issues/2121
  • @zihaozhihao 这是一个有趣的解决方案,但是当使用急切张量或 NumPy 数组作为输入时,它就不起作用了。
  • 嗯,你是说loss_1的论点吗?如果是这样,我相信这行得通。
  • 对于loss_1 是,不,它不会起作用,因为在创建闭包时闭包捕获的数据不可用。
  • TF 2.0 期望损失函数的形式为def fn(y_true, y_pred),即y_true 是第一个参数。

标签: tensorflow keras tensorflow2.0


【解决方案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】:

    扩展jon的答案。如果您想要仍然具有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)
    

    【讨论】:

    • 我刚尝试了你的代码......但是确实收到错误“ValueError:无法编译模型,因为它没有损失优化。”使用Keras 2.3.0和Tensorflow 2.2.0 span>
    • 通常意味着您无需任何可用于优化的梯度的损耗函数或损耗函数即可。例如。如果您的损失函数只会返回标量。 span>
    【解决方案3】:

    在 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 数组作为输入时它不起作用。如果我错了,请纠正我。

    【讨论】:

    • 这适用于 TF 1.x,其中pos_w_arr 不是一个急切的张量。在 TF 2 中,pos_w_arr 在创建闭包时不可用,因此 pos_w_arr 必须是一个常量。我对pos_w_arr 不同批次的情况感兴趣。
    • 如果可能,可以将pos_w_arr设置为tf.keras.Input
    • 基本上,当您适合您的模型时,x=[x_data,pos_w]x_datapos_w 都是 Input
    • 在我的情况下,pos_w_arr 需要是模型的 输出。那么tf.keras.Input 会是正确的吗?
    猜你喜欢
    • 2019-05-19
    • 1970-01-01
    • 2018-01-09
    • 2019-12-16
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 2019-08-04
    • 2018-10-28
    相关资源
    最近更新 更多