Как получить градиент потерь по выходу внутреннего слоя в tenorflow 2? - PullRequest
4 голосов
/ 01 марта 2020

Я хотел бы получить градиент функции потерь модели относительно заданного c вывода слоя во время обучения. Далее я хочу использовать значение этого градиента для изменения чего-либо в слое в следующей эпохе обучения. Итак, как получить этот градиент?

Вот минимальный пример. Код MinimalRNNCell копируется с веб-сайта TensorFlow, а данные игрушки предоставляются только для воспроизведения поведения.

import tensorflow as tf 
from tensorflow.keras.layers import RNN, SimpleRNNCell, SimpleRNN, Layer, Dense, AbstractRNNCell
from tensorflow.keras import Model
import numpy as np
import tensorflow.keras.backend as K


class MinimalRNNCell(AbstractRNNCell):

    def __init__(self, units, **kwargs):
      self.units = units
      super(MinimalRNNCell, self).__init__(**kwargs)

    @property
    def state_size(self):
      return self.units

    def build(self, input_shape):
      self.kernel = self.add_weight(shape=(input_shape[-1], self.units),
                                    initializer='uniform',
                                    name='kernel')
      self.recurrent_kernel = self.add_weight(
          shape=(self.units, self.units),
          initializer='uniform',
          name='recurrent_kernel')
      self.built = True

    def call(self, inputs, states):
      prev_output = states[0]
      h = K.dot(inputs, self.kernel)
      output = h + K.dot(prev_output, self.recurrent_kernel)
      return output, output


class MyModel(Model):
    def __init__(self, size):
        super(MyModel, self).__init__()
        self.minimalrnn=RNN(MinimalRNNCell(size), name='minimalrnn')
        self.out=Dense(4)

    def call(self, inputs):
        out=self.minimalrnn(inputs)
        out=self.out(out)
        return out


x=np.array([[[3.],[0.],[1.],[2.],[3.]],[[3.],[0.],[1.],[2.],[3.]]])
y=np.array([[[0.],[1.],[2.],[3.]],[[0.],[1.],[2.],[3.]]])

model=MyModel(2)
model.compile(optimizer='sgd', loss='mse')
model.fit(x,y,epochs=10, batch_size=1, validation_split=0.2)



Теперь я хочу получить градиент вывода слоя minimalrnn MyModel (после каждой партии данных).

Как это сделать? Я полагаю, что я могу попробовать с GradientTape смотреть модель.get_layer ('minimalrnn'). Output, но мне нужно больше учебных ресурсов или примеров.

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

Я использовал GradientTape как в коде, предоставленном Tia go Martins Peres, но я специально хочу получить вывод градиентного слоя, и я до сих пор не в состоянии достичь этого.

Теперь после определения классов мой код выглядит следующим образом:


x=np.array([[[3.],[0.],[1.],[2.],[3.]],[[3.],[0.],[1.],[2.],[3.]]])
y=np.array([[0., 1., 2., 3.],[0., 1., 2., 3.]])

model=MyModel(2)

#inputs = tf.keras.Input(shape=(2,5,1))
#model.call(x)

def gradients(model, inputs, targets):
    with tf.GradientTape() as tape:
        tape.watch(model.get_layer('minimalrnn').output)
        loss_value = loss_fn(model, inputs, targets)
    return tape.gradient(loss_value, model.trainable_variables)

def loss_fn(model, inputs, targets):
    error = model(inputs) - targets
    return tf.reduce_mean(tf.square(error))

optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
print("Initial loss: {:.3f}".format(loss_fn(model, x, y)))
for i in range(10):
    grads = gradients(model, x, y)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))
    print("Loss at step {:03d}: {:.3f}".format(i, loss_fn(model, x, y)))
print("Final loss: {:.3f}".format(loss_fn(model, x, y)))

Как вы можете видеть, я добавил tape.watch в определение функции градиентов, потому что я хочу наблюдать за выводом слоя. Однако я получаю сообщение об ошибке:

Traceback (most recent call last):
  File "/home/.../test2.py", line 73, in <module>
    grads = gradients(model, x, y)
  File "/home/.../test2.py", line 58, in gradients
    print(model.get_layer('minimalrnn').output)
  File "/home/.../.venv/lib/python3.6/site-packages/tensorflow_core/python/keras/engine/base_layer.py", line 1553, in output
    raise AttributeError('Layer ' + self.name + ' has no inbound nodes.')
AttributeError: Layer minimalrnn has no inbound nodes.

Я также пытался вызвать модель на входе с указанным размером (закомментированные строки), в соответствии с ответом на этот вопрос: Доступ к вводу / выводу слоя с использованием модели Tensorflow 2.0 Sub-причислять . Это не помогло. Указание формы ввода в функции инициализации модели, как показано ниже, также не помогает - все та же ошибка.

self.minimalrnn=RNN(MinimalRNNCell(size), name='minimalrnn', input_shape=(2,5,1))

Ответы [ 2 ]

2 голосов
/ 05 марта 2020

Да, вы можете использовать GradientTape . Целью tf.GradientTape является запись операций для автоматического дифференцирования c или для вычисления градиента операции или вычисления относительно ее входных переменных.

Согласно Что нового в TensorFlow 2.0 , чтобы сначала реализовать простое обучение модели с помощью tf.GradientTape, вызовите прямой проход для тензора ввода внутри диспетчера контекста tf.GradentTape, а затем вычислите функцию потерь. Это гарантирует, что все вычисления будут записаны на градиентной ленте.

Затем вычислите градиенты для всех обучаемых переменных в модели. Как только градиенты вычислены, любое желаемое ограничение градиента, нормализация или преобразование могут быть выполнены перед передачей их оптимизатору, чтобы применить их к переменным модели. Взгляните на следующий пример:

NUM_EXAMPLES = 2000

input_x = tf.random.normal([NUM_EXAMPLES])
noise = tf.random.normal([NUM_EXAMPLES])
input_y = input_x * 5 + 2 + noise

def loss_fn(model, inputs, targets):
  error = model(inputs) - targets
  return tf.reduce_mean(tf.square(error))

def gradients(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss_fn(model, inputs, targets)
  return tape.gradient(loss_value, model.trainable_variables)

model = tf.keras.Sequential(tf.keras.layers.Dense(1))
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
print("Initial loss: {:.3f}".format(loss_fn(model, input_x, input_y)))
for i in range(500):
  grads = gradients(model, input_x, input_y)
  optimizer.apply_gradients(zip(grads, model.trainable_variables))
  if i % 20 == 0:
    print("Loss at step {:03d}: {:.3f}".format(i, loss_fn(model, input_x, input_y)))
print("Final loss: {:.3f}".format(loss(model, input_x, input_y)))
print("W = {}, B = {}".format(*model.trainable_variables))
1 голос
/ 06 марта 2020

Хорошо, поэтому один ответ, который я наконец нашел, спрятан здесь: { ссылка }. Я даже могу использовать подклассную модель с этим.

Кроме того, проблема с AttributeError странная, потому что когда я использовал Sequential вместо подкласса Model, AttributeError волшебным образом исчезла, возможно, это связано с этой проблемой https://github.com/tensorflow/tensorflow/issues/34834?

Тем не менее, я хотел бы знать, почему я не могу просто передать вывод слоя в качестве второго аргумента в tape.gradient.

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