J'ai trouvé un comportement qui n'est pas documenté sur le comportement des noms dans les couches personnalisées de tf.keras, alors faites-le moi savoir. Le "nom de la variable" mentionné ici n'est pas le nom de la variable dans la syntaxe Python, mais le nom (obligatoire comme argument) donné à la variable Tensorflow (tf.Variable).
Avant l'écriture recommandée, je vais vous expliquer un peu les noms de variables.
Ce n'est pas my.v1 ou self.v2 dans l'exemple de code ci-dessous, mais my_variable1 ou my_variable2.
import tensorflow as tf
#Exemple de code de calque personnalisé
#Couche entièrement connectée auto-fabriquée
class MyLayer(tf.keras.layers.Layer):
def __init__(self, output_dim):
super().__init__()
self.output_dim = output_dim
#Terme de biais
#Cela ne dépend pas de la taille des données d'entrée
self.v1 = self.add_weight(name='my_variable1', shape=[output_dim])
def build(self, input_shape):
#matrice affine
#Dépend de la taille des données d'entrée
self.v2 = self.add_weight(name='my_variable2', shape=[input_shape[1], self.output_dim])
self.built = True
def call(self, inputs, **kwargs):
return tf.matmul(inputs, self.v2) + self.v1
Le contenu ici est le contenu du tutoriel officiel.
Exécutons-le et vérifions-le.
model = MyLayer(output_dim=3)
#La méthode de construction est exécutée la première fois que vous entrez des données, entrez donc les données appropriées
x = tf.random.normal(shape=(3, 5))
y = model(x)
print(model.trainable_variables)
↓ C'est le nom
[<tf.Variable 'my_variable1:0' shape=(3,) dtype=float32, numpy=array([-0.56484747, 0.00200152, 0.42238712], dtype=float32)>,
↓ C'est le nom
<tf.Variable 'my_layer/my_variable2:0' shape=(5, 3) dtype=float32, numpy=
array([[ 0.47857696, -0.04394728, 0.31904382],
[ 0.37552172, 0.22522384, 0.07408607],
[-0.74956644, -0.61549807, -0.41261673],
[ 0.4850598 , -0.45188528, 0.56900233],
[-0.39462167, 0.40858668, -0.5422235 ]], dtype=float32)>]
my_variable1: 0
et my_layer / my_variable2: 0
.
Il y a quelque chose en plus, mais j'ai confirmé que les noms des variables sont respectivement my_variable1 et my_variable2, donc c'est OK.
Est ce juste?
Continuons avec l'exemple précédent.
#Lorsque vous empilez vos propres couches
model = tf.keras.Sequential([
MyLayer(3),
MyLayer(3),
MyLayer(3)
])
↓
[<tf.Variable 'my_variable1:0' shape=(3,) dtype=float32, (Abréviation)>,
<tf.Variable 'sequential/my_layer_1/my_variable2:0' shape=(5, 3) dtype=float32, (Abréviation))>,
<tf.Variable 'my_variable1:0' shape=(3,) dtype=float32, (Abréviation)>,
<tf.Variable 'sequential/my_layer_2/my_variable2:0' shape=(3, 3) dtype=float32, (Abréviation)>,
<tf.Variable 'my_variable1:0' shape=(3,) dtype=float32, (Abréviation)>,
<tf.Variable 'sequential/my_layer_3/my_variable2:0' shape=(3, 3) dtype=float32, (Abréviation)]
ma_variable1 est pleine (pleure). Indiscernable.
Quand j'ai dessiné un histogramme de variables sur Tensorboard, les noms se sont affrontés et je ne pouvais pas comprendre la raison.
class MyLayer(tf.keras.layers.Layer):
def __init__(self, output_dim):
super().__init__()
self.output_dim = output_dim
def build(self, input_shape):
#Terme de biais
#Cela ne dépend pas de la taille des données d'entrée
self.v1 = self.add_weight(name='my_variable1', shape=[output_dim])
#matrice affine
#Dépend de la taille des données d'entrée
self.v2 = self.add_weight(name='my_variable2', shape=[input_shape[1], self.output_dim])
self.built = True
def call(self, inputs, **kwargs):
return tf.matmul(inputs, self.v2) + self.v1
Déclarez simplement toutes les variables de la méthode de construction.
Étant donné que Tensorflow est également défini par exécution après la version 2, je pense qu'il ne peut pas être résolu tant que le modèle et l'ordre des couches ne sont pas exécutés en premier. Pour cette raison, je pense qu'il y a une grande différence entre la méthode \ _ \ _ init__ et la méthode biuld.
À propos, tf.keras.layers.Dense etc. sont tous déclarés dans la méthode de construction, vous pouvez donc l'utiliser en toute confiance.
Lorsque vous déclarez une variable dans une couche personnalisée, assurez-vous de la déclarer dans la méthode de construction. Ne déclarez pas dans la méthode \ _ \ _ init__.
Explication du comportement du traitement des noms
Il sera ajouté automatiquement selon les spécifications de Tensorflow. Lors de l'exécution sur un multi-GPU, une copie de la variable est faite pour chaque GPU, donc chacun est numéroté 0, 1, 2, ... dans cet ordre. Les spécifications ici sont les mêmes que dans la version 1.
Dans la version 2, vous pouvez vérifier en faisant la même chose que ci-dessus avec plusieurs GPU en utilisant tf.distribute.MirroredStrategy etc.
my_layer est le nom par défaut si vous n'avez pas explicitement nommé MyLayer. Le nom de la classe est automatiquement converti en cas de serpent.
De plus, lorsque tf.keras.Sequential est utilisé dans le deuxième exemple, il s'agit de my_layer_1, my_layer_2, my_layer_3. Ceci est automatiquement ajouté à la fin pour éviter les conflits de noms. En effet, le premier exemple a my_layer et le deuxième exemple est exécuté successivement.
Je pense que c'est le même comportement que dans la version 1. Au moins la bibliothèque de wrapper dm-sonnet de Tensorflow fait de même.