web-dev-qa-db-fra.com

TensorFlow 2.0 Comment obtenir des variables entraînables à partir de couches tf.keras.layers, comme Conv2D ou Dense

J'ai essayé d'obtenir les variables entraînables de mes couches et je n'arrive pas à trouver un moyen de le faire fonctionner. Voici donc ce que j'ai essayé:

J'ai essayé d'accéder directement à l'attribut noyau et biais de l'objet Dense ou Conv2D, mais en vain. Le type de résultat que j'obtiens est "L'objet dense n'a pas d'attribut 'noyau'".

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

De même, j'ai essayé d'utiliser l'attribut "trainable_variables" de la manière suivante:

trainable_variables.extend(conv_layer.trainable_variables)

D'après ce que je sais, cela est censé renvoyer une liste de deux variables, le poids et les variables de biais. Cependant, ce que je reçois est une liste vide.

Une idée de comment obtenir les variables des étiquettes dans TensorFlow 2.0? Je veux pouvoir alimenter ces variables ultérieurement vers un optimiseur, d'une manière similaire à la suivante:

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

Edit: Voici une partie de mon code actuel pour servir d'exemple et aider à répondre à la question (j'espère qu'il est lisible)

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

Comme vous pouvez le voir, j'essaie de rassembler toutes mes variables entraînables dans un attribut de liste appelé trainable_variables. Cependant, comme je l'ai mentionné, ce code ne fonctionne pas car j'obtiens une erreur lorsque j'essaie d'acquérir le noyau et les attributs de biais de ces objets de couche.

3
MattSt

Ok, donc je pense avoir trouvé le problème.

Les variables entraînables n'étaient pas disponibles jusqu'à ce que j'utilise l'objet de couche donné. Après avoir exécuté ma passe avant, je pouvais récupérer les attributs de l'objet tf.keras.layers.Layer comme trainable_variables et les poids.

Cependant, avant ma passe avant, j'ai reçu une liste vide. Pour rendre les choses un peu plus claires:

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

Sur le code ci-dessus, l'attribut trainable_variables est une liste vide avant d'exécuter self.forward_pass. Cependant, juste après, j'ai pu récupérer le noyau et biaiser les tableaux numpy.

2
MattSt

Permettez-moi de commencer par avoir un modèle simple comme exemple pour le rendre plus facile à expliquer et à comprendre.

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

Lorsque vous utilisez du ruban dégradé, vous passez model.trainable_weights qui renvoie les poids et les biais de l'ensemble du modèle et utilise l'optimiseur pour appliquer les gradients.

Si vous imprimez la sortie de model.trainable_weights, vous obtiendrez cette sortie. J'ai supprimé les poids et biais réels pour plus de lisibilité.

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

Comme vous pouvez le voir, le noyau et le biais de chaque couche ont été générés sous forme de liste. Il s'agit de la même sortie que vous passez à la bande de dégradé. Si vous souhaitez passer juste une couche spécifique, vous pouvez découper la liste et obtenir les poids souhaités que vous souhaitez entraîner.

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

Qui ne produira que les poids et les biais de la première couche conv.

[<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)>]
1