在之前的另一篇文章中(Keras多输出自定义损失函数与中间层输出),我讨论了我遇到的问题。最终,这个问题通过以下方式解决了:
def MyLoss(true1, true2, out1, out2, out3): loss1 = tf.keras.losses.someloss1(out1, true1) loss2 = tf.keras.losses.someloss2(out2, true2) loss3 = tf.keras.losses.someloss3(out2, out3) loss = loss1 + loss2 + loss3 return lossinput1 = Input(shape=input1_shape)input2 = Input(shape=input2_shape)# 不考虑符号表示,仅关注思想output1 = Submodel1()([input1,input2]) output2 = Submodel2()(output1)output3 = Sumbodel3()(output1)true1 = Input(shape=true1shape)true2 = Input(shape=true2shape)model = Model([input1,input2,true1,true2], [output1,output2,output3])model.add_loss(MyLoss(true1, true2, output1, output2, output3))model.compile(optimizer='adam', loss=None)model.fit(x=[input1 ,input2 ,true1,true2], y=None, epochs=n_epochs)
在那个问题中,我使用的所有损失函数都是keras的损失函数(即tf.keras.losss.someloss
),但现在我想添加更多的损失函数,并且我想将自定义损失函数与keras的损失函数结合使用。也就是说,现在我有这样的方案:
为了添加这两个损失函数,这些是SSIM损失函数,我尝试了以下方法:
def SSIMLoss(y_true, y_pred): return 1-tf.reduce_mean(tf.image.ssim(y_true, y_pred, 1.0))def MyLoss(true1, true2, out1, out2, out3): loss1 = tf.keras.losses.someloss1(out1, true1) customloss1 = SSIMLoss(out1,true1) loss2 = tf.keras.losses.someloss2(out2, true2) loss3 = tf.keras.losses.someloss3(out2, out3) customloss2 = SSIMLoss(out2,out3) loss = loss1 + loss2 + loss3 + customloss1 + customloss2 return loss
但我遇到了以下错误:
OperatorNotAllowedInGraphError: 在图执行中不允许将`tf.Tensor`用作Python的`bool`。请使用Eager执行模式或使用@tf.function装饰此函数。
我尝试使用@tf.function
装饰该函数,但得到了以下错误:
_SymbolicException: Eager执行函数的输入不能是Keras符号张量,但发现了[<tf.Tensor 'input_43:0' shape=(None, 128, 128, 1) dtype=float32>, <tf.Tensor 'conv2d_109/Sigmoid:0' shape=(None, 128, 128, 1) dtype=float32>]
我找到了这个(https://github.com/tensorflow/tensorflow/issues/32127)关于将keras损失函数与add_loss
结合的讨论,可能这就是问题所在,但我不知道如何解决它。
回答:
我在TF 2.3
中能够重现您上述的错误。但在TF 2.4
和夜间版本的TF 2.6
中,没有这样的问题,但当我尝试绘制模型时,我得到了另一个错误,尽管使用model.summary()
和.fit
进行训练时没有问题。然而,如果禁用eager模式,那么在TF 2.3 / 2.4
中就不会有问题。
详细信息
在TF 2.3
中,我可以重现您的问题,如下所示。要解决这个问题,只需禁用eager模式即可。
在TF 2.4
/ TF Nightly 2.6
中,我不需要禁用eager模式。模型编译得很好,并且按预期进行训练。但唯一的问题是当我尝试绘制模型时,它给出了以下错误
tf.keras.utils.plot_model(model)....AttributeError: 'tensorflow.python.framework.ops.EagerTensor' object has no attribute '_keras_history'
这个问题是由SSIMLoss
方法中的1-..
表达式引起的;这与类似的情况。但同样,通过禁用eager模式,问题可以解决。然而,总的来说,最好升级到TF 2.4
。
代码示例
在这里,我将向您展示一个类似于您的训练管道的示例。在这个示例中,我们有一个输入(28, 28, 3
)和三个输出(28, 28, 3
)。
from tensorflow.keras.layers import *from tensorflow.keras import Model import tensorflow as tf import numpy as np# tf.compat.v1.disable_eager_execution()print(tf.__version__)print(tf.executing_eagerly())2.4.1True
自定义损失函数。
def SSIMLoss(y_true, y_pred): return 1 - tf.reduce_mean(tf.image.ssim(y_true, y_pred, 1.0))def MyLoss(true1, true2, out1, out2, out3): loss1 = tf.keras.losses.cosine_similarity(out1, true1) loss2 = tf.keras.losses.cosine_similarity(out2, true2) loss3 = tf.keras.losses.cosine_similarity(out2, out3) customloss1 = SSIMLoss(true1, out1) customloss2 = SSIMLoss(out2, out3) loss = loss1 + loss2 + loss3 + customloss1 + customloss2 return loss
数据
imgA = tf.random.uniform([10, 28, 28, 3], minval=0, maxval=256)tarA = np.random.randn(10, 28, 28, 3)tarB = np.random.randn(10, 28, 28, 3)
模型
一个具有一个输入和三个输出的模型。
input = Input(shape=(28, 28, 3))middle = Conv2D(16, kernel_size=(3,3), padding='same')(input)outputA = Dense(3, activation='relu')(middle)outputB = Dense(3, activation='selu')(middle)outputC = Dense(3, activation='elu')(middle)target_inputA = Input(shape=(28, 28, 3))target_inputB = Input(shape=(28, 28, 3))model = Model([input, target_inputA, target_inputB], [outputA, outputB, outputC])model.add_loss(MyLoss(target_inputA, target_inputB, outputA, outputB, outputC))# tf.keras.utils.plot_model(model) # 禁用eager模式 model.summary()
编译和运行
model.compile(optimizer='adam', loss=None)model.fit([imgA, tarA, tarB], steps_per_epoch=5)5/5 [==============================] - 2s 20ms/step - loss: 1.4338<tensorflow.python.keras.callbacks.History at 0x7efde188d450>