web-dev-qa-db-ja.com

TensorFlow 2.0 Conv2DやDenseなどのtf.keras.layersレイヤーからトレーニング可能な変数を取得する方法

私は自分のレイヤーからトレーニング可能な変数を取得しようとしていますが、それを機能させる方法を理解できません。だからここに私が試したものがあります:

DenseまたはConv2Dオブジェクトのカーネルお​​よびバイアス属性に直接アクセスしてみましたが、役に立ちませんでした。取得する結果のタイプは、「密なオブジェクトには属性「カーネル」がありません」です。

trainable_variables.append(conv_layer.kernel)
trainable_variables.append(conv_layer.bias)

同様に、「trainable_variables」属性を次のように使用してみました。

trainable_variables.extend(conv_layer.trainable_variables)

私が知っていることから、これは重みとバイアス変数の2つの変数のリストを返すことになっています。ただし、空のリストが表示されます。

TensorFlow 2.0でラベルから変数を取得する方法のアイデアはありますか?これらの変数を後で次のような方法でオプティマイザーに供給できるようにしたいと思います。

gradients = tape.gradient(loss, trainable_variables)
optimizer.apply_gradients(Zip(gradients, trainable_variables))

編集:例として機能し、質問への回答を助けるための現在のコードの一部を次に示します(読みやすいことを願っています)

from tensorflow.keras.layers import Dense, Conv2D, Conv2DTranspose, Reshape, Flatten

... 

class Network:
    def __init__(self, params):
        weights_initializer = tf.initializers.GlorotUniform(seed=params["seed"])
        bias_initializer = tf.initializers.Constant(0.0)

        self.trainable_variables = []

        self.conv_layers = []
        self.conv_activations = []
        self.create_conv_layers(params, weights_initializer, bias_initializer)

        self.flatten_layer = Flatten()


        self.dense_layers = []
        self.dense_activations = []
        self.create_dense_layers(params, weights_initializer, bias_initializer)

        self.output_layer = Dense(1, kernel_initializer=weights_initializer, bias_initializer=bias_initializer)
        self.trainable_variables.append(self.output_layer.kernel)
        self.trainable_variables.append(self.output_layer.bias)

    def create_conv_layers(self, params, weight_init, bias_init):
        nconv = len(params['stride'])
        for i in range(nconv):
            conv_layer = Conv2D(filters=params["nfilter"][i],
                                kernel_size=params["shape"][i], kernel_initializer=weight_init,
                                kernel_regularizer=spectral_norm,
                                use_bias=True, bias_initializer=bias_init,
                                strides=params["stride"][i],
                                padding="same", )
            self.conv_layers.append(conv_layer)
            self.trainable_variables.append(conv_layer.kernel)
            self.trainable_variables.append(conv_layer.bias)
            self.conv_activations.append(params["activation"])

    def create_conv_layers(self, params, weight_init, bias_init):
        nconv = len(params['stride'])
        for i in range(nconv):
            conv_layer = Conv2D(filters=params["nfilter"][i],
                                kernel_size=params["shape"][i], kernel_initializer=weight_init,
                                kernel_regularizer=spectral_norm,
                                use_bias=True, bias_initializer=bias_init,
                                strides=params["stride"][i],
                                padding="same", )
            self.conv_layers.append(conv_layer)
            self.trainable_variables.append(conv_layer.kernel)
            self.trainable_variables.append(conv_layer.bias)
            self.conv_activations.append(params["activation"])

ご覧のように、トレーニング可能な変数をすべて、trainable_variablesというリスト属性にまとめようとしています。ただし、前述のように、これらのレイヤーオブジェクトのカーネルとバイアスの属性を取得しようとするとエラーが発生するため、このコードは機能しません。

3
MattSt

わかりました、私は問題を見つけたと思います。

指定されたレイヤーオブジェクトを使用するまで、トレーニング可能な変数は使用できませんでした。フォワードパスを実行した後、trainable_variablesやweightsなどのtf.keras.layers.Layerオブジェクトの属性を取得できました。

しかし、フォワードパスの前に、空のリストを受け取りました。もう少し明確にするために:

with tf.GradientTape() as tape:
    print(dense_layers[0].trainable_variables)
    self.forward_pass(X)
    self.compute_loss()
    print(dense_layers[0].trainable_variables)

上記のコードでは、self.forward_passを実行する前の属性trainable_variablesは空のリストです。しかし、その直後に、カーネルを取得して、numpy配列にバイアスをかけることができました。

2
MattSt

説明と理解を容易にするために、例として単純なモデルを使用することから始めましょう。

model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(1, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(tf.keras.layers.MaxPooling2D((2, 2)))
model.add(tf.keras.layers.Conv2D(1, (3, 3), activation='relu'))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(10, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='relu'))
model.add(tf.keras.layers.Dense(3, activation='softmax'))

勾配テープを使用する場合は、モデル全体の重みとバイアスを返すmodel.trainable_weightsを渡し、オプティマイザーを使用して勾配を適用します。

model.trainable_weightsの出力を印刷すると、この出力が得られます。 読みやすさのために、実際の重みとバイアスを削除しました。

[<tf.Variable 'conv2d/kernel:0' shape=(3, 3, 3, 1) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'conv2d/bias:0' shape=(1,) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'conv2d_1/kernel:0' shape=(3, 3, 1, 1) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'conv2d_1/bias:0' shape=(1,) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'dense/kernel:0' shape=(169, 10) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'dense/bias:0' shape=(10,) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'dense_1/kernel:0' shape=(10, 10) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'dense_2/kernel:0' shape=(10, 3) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'dense_2/bias:0' shape=(3,) dtype=float32, numpy=array([...], dtype=float32)>]

ご覧のとおり、各レイヤーのカーネルとバイアスがリストとして出力されました。これは、グラデーションテープに渡す出力と同じです。特定のレイヤーのみを渡したい場合は、リストをスライスして、トレーニングしたいウェイトを取得できます。

model.trainable_weights[0:2] # Get the first conv layer weights at index 0 and bias at index 1.

これは、最初のコンバージョンレイヤーの重みとバイアスのみを出力します。

[<tf.Variable 'conv2d/kernel:0' shape=(3, 3, 3, 1) dtype=float32, numpy=array([...], dtype=float32)>,
 <tf.Variable 'conv2d/bias:0' shape=(1,) dtype=float32, numpy=array([...], dtype=float32)>]