web-dev-qa-db-ja.com

TensorFlow 2で内部層の出力に関する損失勾配を取得するにはどうすればよいですか?

トレーニング中に特定のレイヤーの出力に関するモデルの損失関数の勾配を取得したいと思います。次に私がそれでやりたいのは、その勾配の値を使用して、次の学習エポックでレイヤー内の何かを変更することです。では、その勾配を取得する方法は?

これは最小限の例です。 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)



次に、MyModelのminimalrnnレイヤーの出力の勾配を取得します(データのバッチごとに)。

これを行う方法? GradientTapeでmodel.get_layer( 'minimalrnn')。outputを試してみることができると思いますが、さらに学習リソースや例が必要です。

[〜#〜]編集[〜#〜]

Tiago Martins Peresが提供するコードのようにGradientTapeを使用しましたが、特にグラデーションのwrtレイヤー出力を取得したいのですが、それでもまだ達成できません。

クラスを定義すると、コードは次のようになります。


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)))

ご覧のとおり、レイヤー出力を監視したいので、gradients関数の定義に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モデルのサブクラス化を使用してレイヤーの入力/出力にアクセスする 。それは助けにはならなかった。以下のように、モデルのinit関数で入力形状を指定しても、同じエラーが発生します。

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

わかりましたので、私が最終的に見つけた1つの答えはここに隠されています: https://stackoverflow.com/a/56567364/475017 。これでサブクラス化モデルを使用することもできます。

さらに、AttributeErrorの問題は奇妙です。Modelをサブクラス化する代わりにSequentialを使用すると、AttributeErrorが魔法のように消えたためです。おそらく、この問題に関連している可能性があります https://github.com/tensorflow/tensorflow/issues/34834

それでも、レイヤーの出力を2番目の引数としてtape.gradientに渡すことができない理由を知りたいのですが。

1
lida