Tensorflow «обнаруженные ребра(ы) , создающие цикл(ы)» в адаптированном обучении модели keras

#tensorflow #keras #optimization #neural-network #tensorflow2.0

Вопрос:

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

Поэтому я адаптировал пример из https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit и попытался перезаписать train_step метод. Это, однако, выдает мне следующее сообщение об ошибке, которое, как я думаю, отключает выполнение графика в tensorflow и запускает код в режиме быстрого выполнения:

 E tensorflow/core/grappler/optimizers/meta_optimizer.cc:808] layout failed: Invalid argument: MutableGraphView::SortTopologically error: detected edge(s) creating cycle(s) {'while/body/_1/while/clip_by_value' -> 'while/body/_1/while/Identity_2', 'while/body/_1/while/AssignAddVariableOp' -> 'while/body/_1/while/Identity_2', 'Func/while/body/_1/output_control_node/_97' -> 'while/next_iteration/_43', 'while/body/_1/while/AssignAddVariableOp_1' -> 'while/body/_1/while/Identity_2'}.
 

Чего я не понимаю, так это почему возникает эта ошибка. Без сверточного слоя сообщение об ошибке исчезает, а также при установке descent_direction в теле цикла tf.zeros значения . Является ли мой подход просто неправильным? Что я могу сделать, чтобы это сработало? Я действительно не понимаю, как работают оптимизаторы захвата. Любая помощь будет признательна.

Мой код следующий (я только добавил tf.while_loop() в train_step() ):

 import tensorflow as tf

class CustomModel(tf.keras.Model):
    def __init__(self):
        super(CustomModel, self).__init__()
        self.sequential = tf.keras.models.Sequential([
            tf.keras.layers.Conv2D(32, 3, activation='relu'),
            tf.keras.layers.Flatten(),
            tf.keras.layers.Dense(128, activation='relu'),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.Dense(10)
        ])

    def call(self, inputs):
        return self.sequential(inputs)

    def train_step(self, data):
        x, y = data
        tol = 1e-7
        max_iterations = 5
        epsilon = 0.09
        sigma = 10.0

        x_lower = tf.clip_by_value(tf.subtract(x, epsilon), 0, 1)
        x_upper = tf.clip_by_value(tf.add(x, epsilon), 0, 1)

        def pgd_iterations_cond(xk, gradients_xk):
            """Should be a stopping criterion depending on the gradient."""
            return True

        def pgd_iterations_body(xk, gradients_xk):
            """Calculate gradient w.r.t. current iterate xk and do projected step of size sigma in descent direction."""
            with tf.GradientTape() as tape:
                tape.watch(xk)
                y_pred = self(xk, training=True)
                loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)
                neg_loss = tf.negative(loss)
            gradients_xk_step = tape.gradient(neg_loss, xk)
            descent_direction = tf.negative(gradients_xk_step)

            xk_step = tf.add(xk, tf.scalar_mul(sigma, descent_direction))
            xk_step = tf.clip_by_value(xk_step, x_lower, x_upper)
            return xk_step, gradients_xk_step

        gradients_x = tf.ones_like(x)
        x_perturbed, _ = tf.while_loop(pgd_iterations_cond, pgd_iterations_body, [x, gradients_x],
                                       maximum_iterations=max_iterations)

        with tf.GradientTape() as tape:
            y_pred = self(x_perturbed, training=True)
            loss = self.compiled_loss(y, y_pred, regularization_losses=self.losses)

        trainable_vars = self.trainable_variables
        gradients = tape.gradient(loss, trainable_vars)
        self.optimizer.apply_gradients(zip(gradients, trainable_vars))
        self.compiled_metrics.update_state(y, y_pred)
        return {m.name: m.result() for m in self.metrics}


if __name__ == "__main__":
    mnist = tf.keras.datasets.mnist
    (x_train, y_train), _ = mnist.load_data()
    x_train = x_train / 255.0
    x_train = x_train[..., tf.newaxis].astype('float32')

    Model = CustomModel()
    loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
    Model.compile(optimizer='adam',
                  loss=loss_fn,
                  metrics=['accuracy'])
    Model.fit(x_train, y_train, epochs=5)
 

РЕДАКТИРОВАТЬ: На случай, если кто-то хочет знать: Не смог решить эту проблему таким образом, пришлось написать свой собственный цикл обучения, и ошибка исчезла.