Как создать пользовательский слой в Keras с переменными / тензорными состояниями? - PullRequest
2 голосов
/ 08 марта 2020

Я хотел бы попросить вас помочь с созданием моего пользовательского слоя. То, что я пытаюсь сделать, на самом деле довольно просто: создание выходного слоя с переменными состояния, то есть тензорами, значение которых обновляется в каждом пакете.

Чтобы сделать все более понятным, вот фрагмент что я хотел бы сделать:

def call(self, inputs)

   c = self.constant
   m = self.extra_constant

   update = inputs*m + c 
   X_new = self.X_old + update 

   outputs = X_new

   self.X_old = X_new   

   return outputs

Идея здесь довольно проста:

  • X_old инициализируется в 0 в def__ init__(self, ...)
  • update вычисляется как функция входных данных для слоя
  • Выходная мощность слоя вычисляется (т.е. X_new)
  • значение X_old устанавливается равным X_new, так что в следующей партии X_old больше не равен нулю, а равен X_new от предыдущей партии.

Я обнаружил, что K.update выполняет свою работу , как показано в примере:

 X_new = K.update(self.X_old, self.X_old + update)

Проблема здесь в том, что, если я тогда попытаюсь определить выходы слоя как:

outputs = X_new

return outputs

, я получу следующую ошибку когда я пытаюсь model.fit ():

ValueError: An operation has `None` for gradient. Please make sure that all of your ops have 
gradient defined (i.e. are differentiable). Common ops without gradient: K.argmax, K.round, K.eval.

И у меня продолжает появляться эта ошибка, хотя я наложил layer.trainable = False и я не определял никакого смещения или веса для слоя. С другой стороны, если я просто сделаю self.X_old = X_new, значение X_old не будет обновлено.

У вас, ребята, есть решение для реализации этого? Я полагаю, что это не должно быть так сложно, так как RNN, работающие с состоянием, имеют «похожее» функционирование.

Заранее спасибо за вашу помощь!

1 Ответ

0 голосов
/ 08 марта 2020

Определение пользовательского слоя может несколько раз запутать. Некоторые из методов, которые вы переопределяете, будут вызываться один раз, но создается впечатление, что, как и многие другие библиотеки / платформы OO, они будут вызываться много раз.

Вот что я имею в виду: Когда вы определяете слой и используете его в модели, код python, который вы пишете для переопределения метода call, не будет вызываться напрямую при прямой или обратной передаче. Вместо этого он вызывается только один раз, когда вы звоните model.compile. Он компилирует код python в вычислительный граф, и тот граф, в котором будут течь тензоры, - это то, что выполняет вычисления во время обучения и прогнозирования.

Вот почему, если вы хотите отладить свою модель, поставив print заявление, что это не сработает; вам нужно использовать tf.print, чтобы добавить команду печати к графику.

Это та же самая ситуация с переменной состояния, которую вы хотите иметь. Вместо простого присвоения old + update для new вам нужно вызвать функцию Keras, которая добавляет эту операцию к графу.

И обратите внимание, что тензоры являются неизменяемыми, поэтому вам нужно определить состояние как tf.Variable в __init__ метод.

Так что я считаю, что этот код больше похож на то, что вы ищете:

class CustomLayer(tf.keras.layers.Layer):
  def __init__(self, **kwargs):
    super(CustomLayer, self).__init__(**kwargs)
    self.state = tf.Variable(tf.zeros((3,3), 'float32'))
    self.constant = tf.constant([[1,1,1],[1,0,-1],[-1,0,1]], 'float32')
    self.extra_constant = tf.constant([[1,1,1],[1,0,-1],[-1,0,1]], 'float32')
    self.trainable = False

  def call(self, X):
    m = self.constant    
    c = self.extra_constant
    outputs = self.state + tf.matmul(X, m) + c
    tf.keras.backend.update(self.state, tf.reduce_sum(outputs, axis=0))

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