无法向自定义损失输入数据:渴望执行函数的输入不能是 Keras 符号张量

Posted

技术标签:

【中文标题】无法向自定义损失输入数据:渴望执行函数的输入不能是 Keras 符号张量【英文标题】:Can't input data to custom loss: Inputs to eager execution function cannot be Keras symbolic tensors 【发布时间】:2022-01-20 19:54:17 【问题描述】:

当我在测试我的 tensorflow keras 自定义损失时(使用额外的输入数据来计算损失),如下所示:

@tf.function
def build_walker_loss(labeled_output_t, unlabeled_output_t, label):
    similarity = tf.matmul(labeled_output_t, unlabeled_output_t, transpose_b=True)
    transition_prob_to_unlabeled = tf.nn.softmax(similarity,  name="transition_prob_to_unlabeled")
    transition_prob_to_labeled = tf.nn.softmax(tf.transpose(similarity),  name="transition_prob_to_labeled")

    roundtrip_prob = tf.matmul(transition_prob_to_unlabeled, transition_prob_to_labeled, name="roundtrip_prob")

    label = tf.reshape(label, [-1, 1])
    target_distribution = tf.cast(tf.equal(label, tf.transpose(label)),dtype=tf.float32)
    num_class = tf.compat.v1.reduce_sum(target_distribution, axis=1, keep_dims=True)
    target_distribution = target_distribution / num_class
    loss = tf.keras.losses.categorical_crossentropy(from_logits=False,
        y_true = target_distribution,
        y_pred = tf.math.log(1e-8 + roundtrip_prob),
    )
    print(loss)
    return loss

X = np.random.uniform(0,1, (1000,10))
y = np.random.uniform(0,1, 1000)
W = np.random.uniform(1,2, 1000)

inp = Input((10,))
true = Input((10,))
sample_weight = Input((10,))
x = Dense(32, activation='relu')(inp)
out = Dense(10)(x)
print(true)
print(out)
m = Model([inp,true, sample_weight], out)
m.add_loss( build_walker_loss( true, out, sample_weight ) )
m.compile(loss=None, optimizer='adam')

我收到错误消息:

    _SymbolicException                        Traceback (most recent call last)
<ipython-input-13-a0b380ce314d> in <module>
     37 print(out)
     38 m = Model([inp,true, sample_weight], out)
---> 39 m.add_loss( build_walker_loss( true, out, sample_weight ) )
     40 m.compile(loss=None, optimizer='adam')
     41 # history = m.fit([X, y, W], y=None, epochs=10)

E:\Anaconda3\envs\lrc\lib\site-packages\tensorflow\python\eager\def_function.py in __call__(self, *args, **kwds)
    578         xla_context.Exit()
    579     else:
--> 580       result = self._call(*args, **kwds)
    581 
    582     if tracing_count == self._get_tracing_count():

E:\Anaconda3\envs\lrc\lib\site-packages\tensorflow\python\eager\def_function.py in _call(self, *args, **kwds)
    648               *args, **kwds)
    649       # If we did not create any variables the trace we have is good enough.
--> 650       return self._concrete_stateful_fn._filtered_call(canon_args, canon_kwds)  # pylint: disable=protected-access
    651 
    652     def fn_with_cond(*inner_args, **inner_kwds):

E:\Anaconda3\envs\lrc\lib\site-packages\tensorflow\python\eager\function.py in _filtered_call(self, args, kwargs)
   1663          if isinstance(t, (ops.Tensor,
   1664                            resource_variable_ops.BaseResourceVariable))),
-> 1665         self.captured_inputs)
   1666 
   1667   def _call_flat(self, args, captured_inputs, cancellation_manager=None):

E:\Anaconda3\envs\lrc\lib\site-packages\tensorflow\python\eager\function.py in _call_flat(self, args, captured_inputs, cancellation_manager)
   1744       # No tape is watching; skip to running the function.
   1745       return self._build_call_outputs(self._inference_function.call(
-> 1746           ctx, args, cancellation_manager=cancellation_manager))
   1747     forward_backward = self._select_forward_and_backward_functions(
   1748         args,

E:\Anaconda3\envs\lrc\lib\site-packages\tensorflow\python\eager\function.py in call(self, ctx, args, cancellation_manager)
    596               inputs=args,
    597               attrs=attrs,
--> 598               ctx=ctx)
    599         else:
    600           outputs = execute.execute_with_cancellation(

E:\Anaconda3\envs\lrc\lib\site-packages\tensorflow\python\eager\execute.py in quick_execute(op_name, num_outputs, inputs, attrs, ctx, name)
     72       raise core._SymbolicException(
     73           "Inputs to eager execution function cannot be Keras symbolic "
---> 74           "tensors, but found ".format(keras_symbolic_tensors))
     75     raise e
     76   # pylint: enable=protected-access

_SymbolicException: Inputs to eager execution function cannot be Keras symbolic tensors, but found [<tf.Tensor 'input_14:0' shape=(None, 10) dtype=float32>, <tf.Tensor 'dense_9/Identity:0' shape=(None, 10) dtype=float32>, <tf.Tensor 'input_15:0' shape=(None, 10) dtype=float32>]

我按照Custom loss problem: inputs to eager execution function cannot be keras symbolic tensors but found的答案,但是没有考虑输入数据的正确性,当我把mse loss改成自己的loss function时,还是报这个错。

我不知道是哪一步导致我的函数出错。 我该怎么做才能将此损失函数添加到我的模型中?

【问题讨论】:

您是否有任何理由通过model.add_loss 而不是通过子类化tf.keras.losses.Loss 来实现自定义损失?如果没有,我建议您使用第二个选项。如果需要,将提供一个示例。 如果能拿到样品,我将不胜感激。^_^ 【参考方案1】:

可以通过子类化tf.keras.losses.Loss 来创建自定义损失。以这种方式产生的损失可以直接传递给优化器。让我通过一个焦点损失的例子来证明这一点 (arXiv:1708.02002)。

class focal_loss(tf.keras.losses.Loss):

    # function to initilize loss parameters
    def __init__(self, gamma):
        super().__init__()
        self.gamma = gamma
    
    # function to evaluate loss 
    # must accept exectly 3 parameters: true labels, predicted labels, and, possible,samples weights
    # must return loss value
    def __call__(self, y_true, y_pred, sample_weight=None):
        entropy = tf.keras.losses.binary_crossentropy( y_true, y_pred )
        focal_weight = tf.reduce_sum( y_true*tf.math.pow((1-y_pred),self.gamma), axis=-1 )
        loss = tf.math.multiply(entropy,focal_weight)
        # use sample weights, if provided
        if sample_weight is not None:
            sample_weight = tf.squeeze(sample_weight)
            loss = tf.math.multiply(loss,sample_weight)
        loss = tf.math.reduce_sum( loss )
    return loss

您还可以将其直接传递给优化器:

f_loss = focal_loss(2.)
model.compile(loss=f_loss, optimizer='adam')

如果以这种方式重写你的损失不起作用,那么它显然是一个实现错误(以你计算损失的方式)。需要进行更仔细的研究。

【讨论】:

【参考方案2】:

我尝试了您的代码并得到了另一个错误:

TypeError: 你正在通过 KerasTensor(type_spec=TensorSpec(shape=(None, 10), dtype=tf.float32, name='true'), name='true', description="created by layer 'true'"), an 中间 Keras 符号输入/输出,到不支持的 TF API 允许注册自定义调度程序,例如tf.condtf.function,渐变胶带,或tf.map_fn

这意味着问题出在@tf.function。如果您注释掉这一行,它将起作用

【讨论】:

我不确定这是不是版本问题,我使用的是 tensorflow 2.2.0 另外一个问题是,如果我这样做,我会得到一个返回值形状 (None,),并得到一个错误:ValueError: Shapes must be equal rank, but are 0 and 1 From merging shape 0 with other shapes. for 'node AddN = AddN[N=2, T=DT_FLOAT](model_3/tf_op_layer_AddN_1/AddN_1, model_3/tf_op_layer_Neg_3/Neg_3)' with input shapes: [], [?].,这意味着它会与另一个 costom 的形状冲突损失。

以上是关于无法向自定义损失输入数据:渴望执行函数的输入不能是 Keras 符号张量的主要内容,如果未能解决你的问题,请参考以下文章

Keras 中基于输入数据的自定义损失函数

带有额外输入数据的 tensorflow 自定义损失函数

自定义损失函数返回 - InvalidArgumentError:第二个输入必须是标量,但它具有形状 [64]

我的自定义损失函数是不是正确? (火炬)

Keras 中带有附加变量输入的自定义损失/目标函数

Keras 上的自定义损失函数