トレーニング中に特定のレイヤーの出力に関するモデルの損失関数の勾配を取得したいと思います。次に私がそれでやりたいのは、その勾配の値を使用して、次の学習エポックでレイヤー内の何かを変更することです。では、その勾配を取得する方法は?
これは最小限の例です。 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))
わかりましたので、私が最終的に見つけた1つの答えはここに隠されています: https://stackoverflow.com/a/56567364/475017 。これでサブクラス化モデルを使用することもできます。
さらに、AttributeErrorの問題は奇妙です。Modelをサブクラス化する代わりにSequentialを使用すると、AttributeErrorが魔法のように消えたためです。おそらく、この問題に関連している可能性があります https://github.com/tensorflow/tensorflow/issues/34834 ?
それでも、レイヤーの出力を2番目の引数としてtape.gradientに渡すことができない理由を知りたいのですが。