GradientTape теряет след переменной

У меня есть сценарий, который выполняет передачу нейронного стиля в стиле Гэтиса. Он использует потерю стиля и полную потерю вариации. Я использую GradientTape () для вычисления градиентов. Убытки, которые я реализовал, похоже, работают нормально, но новая потеря, которую я добавил, не учитывается должным образом с помощью GradientTape (). Я использую TensorFlow с включенным нетерпеливым исполнением.

Я подозреваю, что это как-то связано с тем, как я вычисляю потери на основе входной переменной. Входными данными является 4D тензор (пакет, h, w, каналы). На самом базовом уровне входными данными является изображение с плавающей запятой, и для вычисления этой новой потери мне нужно преобразовать его в двоичное изображение, чтобы вычислить отношение цвета одного пикселя к другому. Я не хочу на самом деле идти и менять изображение таким образом на каждой итерации, поэтому я просто делаю копию тензора (в форме numpy) и работаю с ним, чтобы вычислить потерю. Я не понимаю ограничений GradientTape, но я считаю, что это «теряет поток» того, как входная переменная используется для получения потерь при преобразовании в массив numpy.

Могу ли я сделать копию тензора изображения и с его помощью выполнить операции преобразования в двоичную форму и вычисление потерь? Или я прошу тензорный поток сделать что-то, чего он просто не может?

Моя новая функция потерь:

def compute_loss(self, **kwargs):
   loss = 0
   image = self.model.deprocess_image(kwargs['image'].numpy())
   binarized_image = self.image_decoder.binarize_image(image)
   volume_fraction = self.compute_volume_fraction(binarized_image)
   loss = np.abs(self.volume_fraction_target - volume_fraction)
   return loss

Моя реализация с использованием GradientTape:

def compute_grads_and_losses(self, style_transfer_state):
        """
        Computes gradients with respect to input image
        """
        with tf.GradientTape() as tape:
            loss = self.loss_evaluator.compute_total_loss(style_transfer_state)
        total_loss = loss['total_loss']
        return tape.gradient(total_loss, style_transfer_state['image']), loss

Пример, который, как мне кажется, может проиллюстрировать мое замешательство. Самое странное, что у моего кода нет проблем с запуском; похоже, что это совсем не сводит к минимуму новый срок убытков. Но этот пример даже не запустится из-за ошибки атрибута: AttributeError: 'numpy.float64' object has no attribute '_id'.

Пример:

import tensorflow.contrib.eager as tfe
import tensorflow as tf

def compute_square_of_value(x):
    a = turn_to_numpy(x['x'])
    return a**2

def turn_to_numpy(arg):
    return arg.numpy() #just return arg to eliminate the error

tf.enable_eager_execution()
x = tfe.Variable(3.0, dtype=tf.float32)
data_dict = {'x': x}
with tf.GradientTape() as tape:
  tape.watch(x)
  y = compute_square_of_value(data_dict)

dy_dx = tape.gradient(y, x) # Will compute to 6.0

print(dy_dx)

Редактировать:

Исходя из моего текущего понимания, возникает проблема, заключающаяся в том, что мое использование операции .numpy () - это то, что заставляет Gradient Tape терять отслеживание переменной, по которой вычисляется градиент. Моя первоначальная причина для этого заключается в том, что моя операция потери требует, чтобы я физически изменил значения тензора, и я не хочу фактически изменять значения, используемые для оптимизируемого тензора. Следовательно, для правильного вычисления потерь используется копия numpy (). Есть ли способ обойти это? Или я считаю, что мой расчет потерь невозможно реализовать из-за этого ограничения, заключающегося в необходимости выполнять практически необратимые операции с входным тензором?


person wandadars    schedule 26.07.2019    source источник


Ответы (1)


Первая проблема здесь в том, что GradientTape отслеживает операции только с объектами tf.Tensor. Когда вы вызываете tenor.numpy (), выполняемые там операции выходят за пределы ленты.

Вторая проблема заключается в том, что ваш первый пример никогда не вызывает tape.watche для изображения, которое вы хотите различать.

person Alexandre Passos    schedule 16.08.2019
comment
Спасибо @Alexandre. Если я использую режим активного выполнения, мне все равно нужно будет использовать часы? Моя оптимизация, кажется, работает нормально без ее использования (за исключением этой новой потери, конечно). - person wandadars; 16.08.2019
comment
Хорошо, вы перешли к сути моего вопроса. Для этой конкретной потери тензор представляет собой изображение (пакет, H, W, C). Чтобы вычислить потери, мне нужно взять значения пикселей изображения с плавающей запятой и сопоставить их с ближайшим значением (например, [0,0,0] и [255,255,255]), чтобы каждый пиксель был либо одним значение или другое. Затем я вычисляю соотношение, и это возвращаемая потеря объемной доли. Я не хочу связываться с исходным оптимизируемым изображением, поэтому я беру просто копию в виде массива numpy. Можете ли вы сделать тензорные копии? - person wandadars; 16.08.2019
comment
Тензоры TF неизменяемы, поэтому вы не можете с ними связываться, и вам не нужно делать копию. Однако, если вы настаиваете на его создании, вы всегда можете просто использовать tf.identity. Однако мне не кажется, что ваша потеря дифференцируема (поскольку бесконечно малые изменения в значении пикселей изображения не влияют на ввод) - person Alexandre Passos; 18.08.2019
comment
Понятно. Это вызывает у меня недоумение, потому что для того, чтобы оценить потерю доли объема, мне нужно сделать снимок тензора, который представляет сгенерированное изображение, и выполнить обработку изображения на пикселях, чтобы настроить их так, чтобы можно было вычислить расчет потерь. Изменения во входном изображении повлияют на потерю, потому что объемная доля пикселей A / пикселей B изменится (или, по крайней мере, я так думаю). - person wandadars; 18.08.2019