web-dev-qa-db-ja.com

tensorflow 2.0の追加入力でカスタム損失を作成する方法

Tf.kerasとデータセットを使用してTF 2.0で動作するように、追加の引数を持つカスタム損失関数を取得するのに多くの問題を抱えています。

次の場合、追加の引数はモデルへの入力データであり、Datasetに含まれています。 1.14の場合、データセットに対して.make_one_shot_iterator().get_next()を実行し、取得したテンソルを損失関数に渡します。同じことが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)

しかし、テンソルフローで次のエラーが発生します。

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../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.

問題は、データセットを損失関数に渡すことですが、熱心に評価されるテンソルが必要です。

代わりに、カスタムロスに入力レイヤーを渡そうとしましたが、それも機能しません。

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)

代わりに私はエラーを受け取ります:

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>]

これを機能させる方法に関するアイデアはありますか?カスタムトレーニングループを使用したくありません。これは、ケラの利便性の多くを失うためです。

10
Luke

[〜#〜]のみ[〜#〜]TF 2.0.0-beta1[〜 #〜]ない[〜#〜]rc

私にとってあなたの2回目の試み

_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)
_

正常に動作します。 オプティマイザを指定する必要がありました。

警告Expected a shuffled dataset but input dataset `x` is not shuffled. Please invoke `shuffle()` on input dataset.のみが表示されます。これは、トレーニングの前にtrain_dataset = train_dataset.shuffle(1)を追加することで回避できます。

4
McLP