web-dev-qa-db-fra.com

Comment faire une perte personnalisée avec une entrée supplémentaire dans Tensorflow 2.0

J'ai beaucoup de mal à obtenir une fonction de perte personnalisée avec un argument supplémentaire pour fonctionner dans TF 2.0 en utilisant tf.keras et un ensemble de données.

Dans le cas suivant, l'argument supplémentaire correspond aux données d'entrée dans le modèle, qui sont contenues dans un Dataset. Dans le cas 1.14, j'exécuterais .make_one_shot_iterator().get_next() sur l'ensemble de données et passerais ensuite le tenseur que j'entrerais dans la fonction de perte. La même chose ne fonctionne pas en 2.0.

class WeightedSDRLoss(keras.losses.Loss):

    def __init__(self, noisy_signal, reduction=keras.losses.Reduction.AUTO, name='WeightedSDRLoss'):
        super().__init__(reduction=reduction, name=name)
        self.noisy_signal = noisy_signal

    def sdr_loss(self, sig_true, sig_pred):
        return (-tf.reduce_mean(sig_true * sig_pred) /
                tf.reduce_mean(tf.norm(tensor=sig_pred) * tf.norm(tensor=sig_true)))

    def call(self, y_true, y_pred):
        noise_true = self.noisy_signal - y_true
        noise_pred = self.noisy_signal - y_pred
        alpha = (tf.reduce_mean(tf.square(y_true)) /
                 tf.reduce_mean(tf.square(y_true) + tf.square(self.noisy_signal - y_pred)))
        return alpha * self.sdr_loss(y_true, y_pred) + (1 - alpha) * self.sdr_loss(noise_true, noise_pred)

data_x = np.random.Rand(5, 4, 1)
data_y = np.random.Rand(5, 4, 1)

x = keras.layers.Input([4, 1])
y = keras.layers.Activation('tanh')(x)
model = keras.models.Model(inputs=x, outputs=y)

train_dataset = tf.data.Dataset.from_tensor_slices((data_x, data_y))
x_dataset = train_dataset.map(lambda x, y: x)

model.compile(loss=WeightedSDRLoss(x_dataset), optimizer='Adam')
model.fit(train_dataset)

Mais j'obtiens l'erreur suivante dans tensorflow:

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/training/tracking/base.py:457: in _method_wrapper
    result = method(self, *args, **kwargs)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/keras/engine/training.py:377: in compile
    self._compile_weights_loss_and_weighted_metrics()
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/training/tracking/base.py:457: in _method_wrapper
    result = method(self, *args, **kwargs)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/keras/engine/training.py:1618: in _compile_weights_loss_and_weighted_metrics
    self.total_loss = self._prepare_total_loss(masks)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/keras/engine/training.py:1678: in _prepare_total_loss
    per_sample_losses = loss_fn.call(y_true, y_pred)
...:144: in call
    noise_true = self.noisy_signal - y_true
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/ops/math_ops.py:924: in r_binary_op_wrapper
    x = ops.convert_to_tensor(x, dtype=y.dtype.base_dtype, name="x")
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/ops.py:1184: in convert_to_tensor
    return convert_to_tensor_v2(value, dtype, preferred_dtype, name)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/ops.py:1242: in convert_to_tensor_v2
    as_ref=False)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/ops.py:1296: in internal_convert_to_tensor
    ret = conversion_func(value, dtype=dtype, name=name, as_ref=as_ref)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/constant_op.py:286: in _constant_tensor_conversion_function
    return constant(v, dtype=dtype, name=name)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/constant_op.py:227: in constant
    allow_broadcast=True)
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/constant_op.py:265: in _constant_impl
    allow_broadcast=allow_broadcast))
../../anaconda3/envs/.../lib/python3.6/site-packages/tensorflow_core/python/framework/tensor_util.py:449: in make_tensor_proto
    _AssertCompatible(values, dtype)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

values = <MapDataset shapes: (...), types: tf.float32>
dtype = tf.float32

    def _AssertCompatible(values, dtype):
      if dtype is None:
        fn = _check_not_tensor
      else:
        try:
          fn = _TF_TO_IS_OK[dtype]
        except KeyError:
          # There isn't a specific fn, so we try to do the best possible.
          if dtype.is_integer:
            fn = _check_int
          Elif dtype.is_floating:
            fn = _check_float
          Elif dtype.is_complex:
            fn = _check_complex
          Elif dtype.is_quantized:
            fn = _check_quantized
          else:
            fn = _check_not_tensor

      try:
        fn(values)
      except ValueError as e:
        [mismatch] = e.args
        if dtype is None:
          raise TypeError("List of Tensors when single Tensor expected")
        else:
          raise TypeError("Expected %s, got %s of type '%s' instead." %
>                         (dtype.name, repr(mismatch), type(mismatch).__name__))
E         TypeError: Expected float32, got <MapDataset shapes: (...), types: tf.float32> of type 'MapDataset' instead.

Le problème semble être que je passe un ensemble de données dans la fonction de perte, mais il veut un tenseur évalué avec impatience.

Au lieu de cela, j'ai essayé de passer la couche d'entrée dans la perte personnalisée, mais cela ne fonctionne pas non plus:

data_x = np.random.Rand(5, 4, 1)
data_y = np.random.Rand(5, 4, 1)

x = keras.layers.Input(shape=[4, 1])
y = keras.layers.Activation('tanh')(x)
model = keras.models.Model(inputs=x, outputs=y)

train_dataset = tf.data.Dataset.from_tensor_slices((data_x, data_y)).batch(1)

model.compile(loss=WeightedSDRLoss(x), optimizer='Adam')
model.fit(train_dataset)

Au lieu de cela, j'obtiens l'erreur:

op_name = '__inference_distributed_function_169', num_outputs = 2
inputs = [<tf.Tensor: id=82, shape=(), dtype=resource, numpy=<unprintable>>, <tf.Tensor: id=83, shape=(), dtype=variant, numpy=<unprintable>>, <tf.Tensor 'input_1:0' shape=(None, 4, 1) dtype=float32>]
attrs = ('executor_type', '', 'config_proto', b'\n\x07\n\x03GPU\x10\x00\n\x07\n\x03CPU\x10\x012\x02J\x008\x01')
ctx = <tensorflow.python.eager.context.Context object at 0x11785f4e0>
name = None

    def quick_execute(op_name, num_outputs, inputs, attrs, ctx, name=None):
      """Execute a TensorFlow operation.

      Args:
        op_name: Name of the TensorFlow operation (see REGISTER_OP in C++ code) to
          execute.
        num_outputs: The number of outputs of the operation to fetch.
                     (Explicitly provided instead of being inferred for performance
                     reasons).
        inputs: A list of inputs to the operation. Each entry should be a Tensor, or
          a value which can be passed to the Tensor constructor to create one.
        attrs: A Tuple with alternating string attr names and attr values for this
          operation.
        ctx: The value of context.context().
        name: Customized name for the operation.

      Returns:
        List of output Tensor objects. The list is empty if there are no outputs

      Raises:
        An exception on error.
      """
      device_name = ctx.device_name
      # pylint: disable=protected-access
      try:
        ctx.ensure_initialized()
        tensors = pywrap_tensorflow.TFE_Py_Execute(ctx._handle, device_name,
                                                   op_name, inputs, attrs,
>                                                  num_outputs)
E                                                  TypeError: An op outside of the function building code is being passed
E                                                  a "Graph" tensor. It is possible to have Graph tensors
E                                                  leak out of the function building context by including a
E                                                  tf.init_scope in your function building code.
E                                                  For example, the following function will fail:
E                                                    @tf.function
E                                                    def has_init_scope():
E                                                      my_constant = tf.constant(1.)
E                                                      with tf.init_scope():
E                                                        added = my_constant * 2
E                                                  The graph tensor has name: input_1:0

../../../lib/python3.6/site-packages/tensorflow_core/python/eager/execute.py:61: TypeError

During handling of the above exception, another exception occurred:

    def test_loss():

        data_x = np.random.Rand(5, 4, 1)
        data_y = np.random.Rand(5, 4, 1)

        x = keras.layers.Input(shape=[4, 1])
        y = keras.layers.Activation('tanh')(x)
        model = keras.models.Model(inputs=x, outputs=y)

        train_dataset = tf.data.Dataset.from_tensor_slices((data_x, data_y)).batch(1)
        print(train_dataset)

        model.compile(loss=WeightedSDRLoss(x))
>       model.fit(train_dataset)

test_preprocess.py:162: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../lib/python3.6/site-packages/tensorflow_core/python/keras/engine/training.py:734: in fit
    use_multiprocessing=use_multiprocessing)
../../../lib/python3.6/site-packages/tensorflow_core/python/keras/engine/training_v2.py:324: in fit
    total_epochs=epochs)
../../../lib/python3.6/site-packages/tensorflow_core/python/keras/engine/training_v2.py:123: in run_one_Epoch
    batch_outs = execution_function(iterator)
../../../training_v2_utils.py:86: in execution_function
    distributed_function(input_fn))
../../../def_function.py:445: in __call__
    return self._concrete_stateful_fn._filtered_call(Canon_args, Canon_kwds)  # pylint: disable=protected-access
../../../function.py:1141: in _filtered_call
    self.captured_inputs)
../../../function.py:1224: in _call_flat
    ctx, args, cancellation_manager=cancellation_manager)
../../../function.py:511: in call
    ctx=ctx)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

op_name = '__inference_distributed_function_169', num_outputs = 2
inputs = [<tf.Tensor: id=82, shape=(), dtype=resource, numpy=<unprintable>>, <tf.Tensor: id=83, shape=(), dtype=variant, numpy=<unprintable>>, <tf.Tensor 'input_1:0' shape=(None, 4, 1) dtype=float32>]
attrs = ('executor_type', '', 'config_proto', b'\n\x07\n\x03GPU\x10\x00\n\x07\n\x03CPU\x10\x012\x02J\x008\x01')
ctx = <tensorflow.python.eager.context.Context object at 0x11785f4e0>
name = None

    def quick_execute(op_name, num_outputs, inputs, attrs, ctx, name=None):
      """Execute a TensorFlow operation.

      Args:
        op_name: Name of the TensorFlow operation (see REGISTER_OP in C++ code) to
          execute.
        num_outputs: The number of outputs of the operation to fetch.
                     (Explicitly provided instead of being inferred for performance
                     reasons).
        inputs: A list of inputs to the operation. Each entry should be a Tensor, or
          a value which can be passed to the Tensor constructor to create one.
        attrs: A Tuple with alternating string attr names and attr values for this
          operation.
        ctx: The value of context.context().
        name: Customized name for the operation.

      Returns:
        List of output Tensor objects. The list is empty if there are no outputs

      Raises:
        An exception on error.
      """
      device_name = ctx.device_name
      # pylint: disable=protected-access
      try:
        ctx.ensure_initialized()
        tensors = pywrap_tensorflow.TFE_Py_Execute(ctx._handle, device_name,
                                                   op_name, inputs, attrs,
                                                   num_outputs)
      except core._NotOkStatusException as e:
        if name is not None:
          message = e.message + " name: " + name
        else:
          message = e.message
        six.raise_from(core._status_to_exception(e.code, message), None)
      except TypeError as e:
        keras_symbolic_tensors = [
            x for x in inputs if ops._is_keras_symbolic_tensor(x)
        ]
        if keras_symbolic_tensors:
          raise core._SymbolicException(
              "Inputs to eager execution function cannot be Keras symbolic "
>             "tensors, but found {}".format(keras_symbolic_tensors))
E         tensorflow.python.eager.core._SymbolicException: Inputs to eager execution function cannot be Keras symbolic tensors, but found [<tf.Tensor 'input_1:0' shape=(None, 4, 1) dtype=float32>]

Des idées sur la façon de faire fonctionner cela? Je ne veux pas utiliser une boucle d'entraînement personnalisée, car je perds alors beaucoup de la commodité des keras.

10
Luke

[~ # ~] uniquement [~ # ~] TF 2.0.0-beta1 [~ # ~] pas [~ # ~] rc

Pour moi ta deuxième tentative

data_x = np.random.Rand(5, 4, 1)
data_y = np.random.Rand(5, 4, 1)

x = keras.layers.Input([4, 1])
y = keras.layers.Activation('tanh')(x)
model = keras.models.Model(inputs=x, outputs=y)

train_dataset = tf.data.Dataset.from_tensor_slices((data_x, data_y)).batch(1)

model.compile(loss=WeightedSDRLoss(x), optimizer='Adam')
model.fit(train_dataset)

fonctionne bien. Je devais juste spécifier un optimiseur.

Je reçois seulement l'avertissement Expected a shuffled dataset but input dataset `x` is not shuffled. Please invoke `shuffle()` on input dataset. qui peut être évité en ajoutant train_dataset = train_dataset.shuffle(1) avant l'entraînement.

4
McLP