Модель с несколькими выходами и пользовательской функцией потерь - PullRequest
0 голосов
/ 17 апреля 2020

Я пытаюсь обучить модель, которая имеет несколько выходов и пользовательскую функцию потерь, используя керасы, но я получаю некоторую ошибку tensorflow.python.framework.errors_impl.OperatorNotAllowedInGraphError: iterating over ``tf.Tensor`` is not allowed in Graph execution. Use Eager execution or decorate this function with @tf.function.

Трудно отладить ее, потому что я делаю model.compile и model.fit. Я думаю, что это как-то связано с тем, как модели должны определяться при наличии нескольких выходов, но я не могу найти хорошую документацию по этому вопросу. В руководстве указано, как использовать модели с несколькими выходами, использующими функциональный API, и приведен пример для этого, но не разъясняется, как должны работать пользовательские функции потерь при создании подкласса Model API. Мой код выглядит следующим образом:

class DeepEnsembles(Model):

    def __init__(self, **kwargs):
        super(DeepEnsembles, self).__init__()

        self.num_models = kwargs.get('num_models')
        model = kwargs.get('model')

        self.mean = [model(**dict(**kwargs)) for _ in range(self.num_models)]

        self.variance = [model(**dict(**kwargs)) for _ in range(self.num_models)]

    def call(self, inputs, training=None, mask=None):
        mean_predictions = []
        variance_predictions = []
        for idx in range(self.num_models):
            mean_predictions.append(self.mean[idx](inputs, training=training))
            variance_predictions.append(self.variance[idx](inputs, training=training))
        mean_stack = tf.stack(mean_predictions)
        variance_stack = tf.stack(variance_predictions)

        return mean_stack, variance_stack

И где MLP выглядит следующим образом:

class MLP(Model):
    def __init__(self, **kwargs):
        super(MLP, self).__init__()

        # Initialization parameters
        self.num_inputs = kwargs.get('num_inputs', 779)
        self.num_outputs = kwargs.get('num_outputs', 1)
        self.hidden_size = kwargs.get('hidden_size', 256)
        self.activation = kwargs.get('activation', 'relu')

        # Optional parameters
        self.p = kwargs.get('p', 0.05)

        self.model = tf.keras.Sequential([
            layers.Dense(self.hidden_size, activation=self.activation, input_shape=(self.num_inputs,)),
            layers.Dropout(self.p),
            layers.Dense(self.hidden_size, activation=self.activation),
            layers.Dropout(self.p),
            layers.Dense(self.num_outputs)
         ])

    def call(self, inputs, training=None, mask=None):
        output = self.model(inputs, training=training)
        return output

Я пытаюсь минимизировать пользовательскую функцию потерь

class GaussianNLL(Loss):

    def __init__(self):
        super(GaussianNLL, self).__init__()

    def call(self, y_true, y_pred):

        mean, variance = y_pred
        variance = variance + 0.0001
        nll = (tf.math.log(variance) / 2 + ((y_true - mean) ** 2) / (2 * variance))
        nll = tf.math.reduce_mean(nll)
        return nll

Наконец, вот как я пытаюсь обучить это:

    ensembles_params = {'num_models': 5, 'model': MLP, 'p': 0}
    model = DeepEnsembles(**ensembles_params)
    loss_fn = GaussianNLL()
    optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
    epochs = 10000

    model.compile(optimizer='adam',
                  loss=loss_fn,
                  metrics=['mse', 'mae'])
    history = model.fit(x_train, y_train,
                        batch_size=2048,
                        epochs=10000,
                        verbose=0,
                        validation_data=(x_val, y_val))

, что приводит к вышеупомянутой ошибке. Есть указатели? В частности, трассировка всего стека составляет

Traceback (most recent call last):
  File "/home/emilio/anaconda3/lib/python3.7/contextlib.py", line 130, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/ops/variable_scope.py", line 2803, in variable_creator_scope
    yield
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_v2.py", line 235, in fit
    use_multiprocessing=use_multiprocessing)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_v2.py", line 593, in _process_training_inputs
    use_multiprocessing=use_multiprocessing)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training_v2.py", line 646, in _process_inputs
    x, y, sample_weight=sample_weights)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py", line 2360, in _standardize_user_data
    self._compile_from_inputs(all_inputs, y_input, x, y)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py", line 2618, in _compile_from_inputs
    experimental_run_tf_function=self._experimental_run_tf_function)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/training/tracking/base.py", line 457, in _method_wrapper
    result = method(self, *args, **kwargs)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py", line 446, in compile
    self._compile_weights_loss_and_weighted_metrics()
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/training/tracking/base.py", line 457, in _method_wrapper
    result = method(self, *args, **kwargs)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py", line 1592, in _compile_weights_loss_and_weighted_metrics
    self.total_loss = self._prepare_total_loss(masks)
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py", line 1652, in _prepare_total_loss
    per_sample_losses = loss_fn.call(y_true, y_pred)
  File "/home/emilio/fault_detection/tensorflow_code/tf_utils/loss.py", line 13, in call
    mean, variance = y_pred
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/framework/ops.py", line 539, in __iter__
    self._disallow_iteration()
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/framework/ops.py", line 535, in _disallow_iteration
    self._disallow_in_graph_mode("iterating over `tf.Tensor`")
  File "/home/emilio/anaconda3/lib/python3.7/site-packages/tensorflow_core/python/framework/ops.py", line 515, in _disallow_in_graph_mode
    " this function with @tf.function.".format(task))
tensorflow.python.framework.errors_impl.OperatorNotAllowedInGraphError: iterating over `tf.Tensor` is not allowed in Graph execution. Use Eager execution or decorate this function with @tf.function.


, поэтому она явно связана с функцией потерь. Но прямой проход модели выводит кортеж, который я распаковываю в функции потерь, поэтому я не знаю, почему это проблема.

1 Ответ

1 голос
/ 18 апреля 2020

С помощью быстрых тестов, я думаю, я решил проблему, заменив:

        mean, variance = y_pred
        variance = variance + 0.0001

на

        mean = y_pred[0]
        variance = y_pred[1] + 0.0001

Распаковка y_pred (что является тензором) вызывает метод Tensor.__iter__ что, по-видимому, приводит к ошибке, в то время как я предполагаю, что метод Tensor.__getitem__ не ...

Я не дошел до того момента, когда он начинает учиться, я думаю, что мои нынешние фиктивные x_train и y_train не являются точно правильной формы. Если вы заметите, что эта проблема возникнет позже, я попытаюсь разобраться.

РЕДАКТИРОВАТЬ:

Мне удалось запустить ваш код с помощью

x_train = np.random.random((10000, 779))
y_train = np.random.random ((10000, 1))

, изменив последняя строка метода DeepEnsembles.call с

        return tf.stack([mean_stack, variance_stack])

и закомментированием метрик (необходимо, потому что ожидается, что размеры y_true и y_pred будут разными, поэтому вы можете захотеть определить свои собственные версии mse и mae для использования в качестве метри c):

model.compile(optimizer='adam',
              loss=loss_fn,
              # metrics=['mse', 'mae']
)

Я полагаю, что это довольно близко к тому, что вы ожидаете.

Причина, по которой вы не возвращаете кортеж, заключается в том, что тензор потока будет интерпретируйте каждый элемент кортежа как выходной сигнал сети и примените потери независимо к каждому из них.

Вы можете проверить это, сохранив старую версию DeepEnsembles.call и вместо этого использовать

y_train_1 = np.random.random ((10000, 1))
y_train_2 = np.random.random ((10000, 1))
y_train = [y_train_1, y_train_2]

Будет выполнено, будет 10 MLP, но MLP_1 / 2 узнает среднее значение и дисперсию y_train_1, MLP_6 / 7 среднее значение и переменную y_train_2, а все остальные MLP ничего не изучат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...