具有多个输入的 Tensorflow 2.0 自定义损失函数
Tensorflow 2.0 Custom loss function with multiple inputs
我正在尝试使用以下两个损失函数优化模型
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
的变通方法,我将 pred
和 weights
打包到一个张量中,然后在 model.fit
and then unpack them in loss_1
. This is inelegant and nasty because pred
and weights
are of different data types and so this requires an additional cast, pack, un-pack and un-cast each time I call model.fit
.
此外,我知道 fit
的 sample_weight
参数,这有点像 的解决方案。如果不是因为我使用了两个损失函数并且我只想将 sample_weight
应用于其中一个,这可能是一个可行的解决方案。另外,即使这是一个解决方案,它是否不能推广到其他类型的自定义损失函数。
综上所述,我的问题简明扼要地说:
创建具有任意数量的损失函数的最佳方法是什么
TensorFlow 2 中的参数?
我尝试过的另一件事是传递 tf.tuple
但这似乎也违反了 TensorFlow 对损失函数输入的要求。
在 tf 1.x 中,我们有 tf.nn.weighted_cross_entropy_with_logits
函数,它允许我们通过为每个 class 添加额外的正权重来权衡召回率和准确率。在多标签class化中,应该是一个(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
不确定你是什么意思,但是当使用 eager tensor 或 numpy 数组作为输入时它不起作用。如果我错了,请纠正我。
在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))
这样一来,每个损失函数都可以采用任意数量的热切张量,而不管它们是模型的输入还是输出。每个损失函数的参数集不必像本例中所示那样不相交。
扩展 Jon 的回答。如果您仍然希望获得 Keras 模型的好处,您可以扩展模型 class 并编写您自己的自定义 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)
我正在尝试使用以下两个损失函数优化模型
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
的变通方法,我将 pred
和 weights
打包到一个张量中,然后在 model.fit
and then unpack them in loss_1
. This is inelegant and nasty because pred
and weights
are of different data types and so this requires an additional cast, pack, un-pack and un-cast each time I call model.fit
.
此外,我知道 fit
的 sample_weight
参数,这有点像 sample_weight
应用于其中一个,这可能是一个可行的解决方案。另外,即使这是一个解决方案,它是否不能推广到其他类型的自定义损失函数。
综上所述,我的问题简明扼要地说:
创建具有任意数量的损失函数的最佳方法是什么 TensorFlow 2 中的参数?
我尝试过的另一件事是传递 tf.tuple
但这似乎也违反了 TensorFlow 对损失函数输入的要求。
在 tf 1.x 中,我们有 tf.nn.weighted_cross_entropy_with_logits
函数,它允许我们通过为每个 class 添加额外的正权重来权衡召回率和准确率。在多标签class化中,应该是一个(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
不确定你是什么意思,但是当使用 eager tensor 或 numpy 数组作为输入时它不起作用。如果我错了,请纠正我。
在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))
这样一来,每个损失函数都可以采用任意数量的热切张量,而不管它们是模型的输入还是输出。每个损失函数的参数集不必像本例中所示那样不相交。
扩展 Jon 的回答。如果您仍然希望获得 Keras 模型的好处,您可以扩展模型 class 并编写您自己的自定义 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)