Quelle est la différence entre ces fonctions?
tf.variable_op_scope(values, name, default_name, initializer=None)
Retourne un gestionnaire de contexte pour définir un op qui crée des variables. Ce gestionnaire de contexte vérifie que les valeurs données proviennent du même graphique, s'assure que ce graphique est le graphique par défaut et envoie une étendue de nom et une étendue de variable.
tf.op_scope(values, name, default_name=None)
Renvoie un gestionnaire de contexte à utiliser lors de la définition d'un Python op. Ce gestionnaire de contexte vérifie que les valeurs données proviennent du même graphique, s'assure que ce graphique est le graphique par défaut et envoie une étendue de nom.
tf.name_scope(name)
Wrapper pour
Graph.name_scope()
en utilisant le graphique par défaut. VoirGraph.name_scope()
pour plus de détails.
tf.variable_scope(name_or_scope, reuse=None, initializer=None)
Retourne un contexte pour la variable scope. La portée des variables permet de créer de nouvelles variables et de partager celles qui ont déjà été créées tout en fournissant des contrôles pour ne pas créer ou partager par accident. Pour plus de détails, reportez-vous au manuel de portée variable, nous ne présentons ici que quelques exemples élémentaires.
Commençons par une brève introduction au partage de variables. C'est un mécanisme dans TensorFlow
qui permet de partager des variables accédées dans différentes parties du code sans passer de références à la variable.
La méthode tf.get_variable
peut être utilisée avec le nom de la variable comme argument permettant de créer une nouvelle variable portant ce nom ou d'extraire celui qui a été créé auparavant. Cela diffère de l'utilisation du constructeur tf.Variable
qui créera une nouvelle variable à chaque appel (et ajoutera éventuellement un suffixe au nom de la variable si une variable portant ce nom existe déjà).
C'est aux fins du mécanisme de partage de variables qu'un type de portée distinct (portée de variable) a été introduit.
En conséquence, nous finissons par avoir deux types d’étendues:
tf.name_scope
tf.variable_scope
Les deux portées ont le même effet sur toutes les opérations, ainsi que sur les variables créées à l'aide de _tf.Variable
_, c'est-à-dire que la portée sera ajoutée en tant que préfixe au nom de l'opération ou de la variable.
Cependant, la portée du nom est ignorée par _tf.get_variable
_. Nous pouvons voir cela dans l'exemple suivant:
_with tf.name_scope("my_scope"):
v1 = tf.get_variable("var1", [1], dtype=tf.float32)
v2 = tf.Variable(1, name="var2", dtype=tf.float32)
a = tf.add(v1, v2)
print(v1.name) # var1:0
print(v2.name) # my_scope/var2:0
print(a.name) # my_scope/Add:0
_
Le seul moyen de placer une variable accessible à l'aide de _tf.get_variable
_ dans une portée consiste à utiliser une portée de variable, comme dans l'exemple suivant:
_with tf.variable_scope("my_scope"):
v1 = tf.get_variable("var1", [1], dtype=tf.float32)
v2 = tf.Variable(1, name="var2", dtype=tf.float32)
a = tf.add(v1, v2)
print(v1.name) # my_scope/var1:0
print(v2.name) # my_scope/var2:0
print(a.name) # my_scope/Add:0
_
Cela nous permet de partager facilement des variables entre différentes parties du programme, même dans différentes portées de noms:
_with tf.name_scope("foo"):
with tf.variable_scope("var_scope"):
v = tf.get_variable("var", [1])
with tf.name_scope("bar"):
with tf.variable_scope("var_scope", reuse=True):
v1 = tf.get_variable("var", [1])
assert v1 == v
print(v.name) # var_scope/var:0
print(v1.name) # var_scope/var:0
_
À partir de la version r0.11, _op_scope
_ et _variable_op_scope
_ sont tous deux déconseillé et sont remplacés par _name_scope
_ et _variable_scope
_.
variable_op_scope et op_scope sont désormais obsolètes et ne doivent plus être utilisés.
En ce qui concerne les deux autres, j'ai également eu du mal à comprendre la différence entre variable_scope et name_scope (ils se ressemblaient presque) avant d'essayer de tout visualiser en créant un exemple simple:
import tensorflow as tf
def scoping(fn, scope1, scope2, vals):
with fn(scope1):
a = tf.Variable(vals[0], name='a')
b = tf.get_variable('b', initializer=vals[1])
c = tf.constant(vals[2], name='c')
with fn(scope2):
d = tf.add(a * b, c, name='res')
print '\n '.join([scope1, a.name, b.name, c.name, d.name]), '\n'
return d
d1 = scoping(tf.variable_scope, 'scope_vars', 'res', [1, 2, 3])
d2 = scoping(tf.name_scope, 'scope_name', 'res', [1, 2, 3])
with tf.Session() as sess:
writer = tf.summary.FileWriter('logs', sess.graph)
sess.run(tf.global_variables_initializer())
print sess.run([d1, d2])
writer.close()
Ici, je crée une fonction qui crée des variables et des constantes et les groupe en étendues (en fonction du type fourni). Dans cette fonction, j'imprime également les noms de toutes les variables. Après cela, j'exécute le graphique pour obtenir les valeurs des valeurs résultantes et enregistrer des fichiers d'événements pour les étudier dans TensorBoard. Si vous exécutez ceci, vous obtiendrez ce qui suit:
scope_vars
scope_vars/a:0
scope_vars/b:0
scope_vars/c:0
scope_vars/res/res:0
scope_name
scope_name/a:0
b:0
scope_name/c:0
scope_name/res/res:0
Vous voyez le même schéma si vous ouvrez TensorBoard (comme vous voyez b
est en dehors de scope_name
rectangulaire):
Ceci vous donne la réponse :
Maintenant, vous voyez que tf.variable_scope()
ajoute un préfixe aux noms de toutes les variables (peu importe la façon dont vous les créez), ops, constantes. D'autre part, tf.name_scope()
ignore les variables créées avec tf.get_variable()
, car il suppose que vous sachiez quelle variable et dans quelle étendue vous souhaitez utiliser.
Une bonne documentation sur variables de partage vous dit que
tf.variable_scope()
: gère les espaces de noms des noms transmis àtf.get_variable()
.
La même documentation fournit plus de détails sur le fonctionnement de Variable Scope et sur son utilité.
Les espaces de noms sont un moyen d'organiser les noms des variables et des opérateurs de manière hiérarchique (par exemple, "scopeA/scopeB/scopeC/op1")
tf.name_scope
crée un espace de noms pour les opérateurs dans le graphique par défaut.tf.variable_scope
crée un espace de noms pour les variables et les opérateurs dans le graphique par défaut.
tf.op_scope
identique à _tf.name_scope
_, mais pour le graphique dans lequel les variables spécifiées ont été créées.
tf.variable_op_scope
identique à _tf.variable_scope
_, mais pour le graphique dans lequel les variables spécifiées ont été créées.
Les liens vers les sources ci-dessus permettent de lever l’ambiguïté de ce problème de documentation.
Cet exemple montre que tous les types d'étendues définissent des espaces de nom pour les variables et les opérateurs avec les différences suivantes:
tf.variable_op_scope
_ ou _tf.variable_scope
_ sont compatibles avec _tf.get_variable
_ (elle ignore deux autres portées).tf.op_scope
_ et _tf.variable_op_scope
_ il suffit de sélectionner un graphique dans une liste de variables spécifiées pour créer une étendue. Autre que leur comportement égal à _tf.name_scope
_ et _tf.variable_scope
_ en conséquencetf.variable_scope
_ et _variable_op_scope
_ ajoutent les initialiseurs spécifiés ou par défaut.Comme pour l’API r0.11, op_scope
et variable_op_scope
sont tous deux obsolète . name_scope
et variable_scope
peuvent être imbriqués:
with tf.name_scope('ns'):
with tf.variable_scope('vs'): #scope creation
v1 = tf.get_variable("v1",[1.0]) #v1.name = 'vs/v1:0'
v2 = tf.Variable([2.0],name = 'v2') #v2.name= 'ns/vs/v2:0'
v3 = v1 + v2 #v3.name = 'ns/vs/add:0'
Soyons simples: il suffit d'utiliser tf.variable_scope
. Citant un développeur TF, :
Actuellement, nous recommandons à tous d'utiliser
variable_scope
et de ne pas utilisername_scope
sauf pour le code interne et les bibliothèques.
Outre le fait que la fonctionnalité de variable_scope
étend celle de name_scope
, considérez comment ils ne jouent pas si bien ensemble:
with tf.name_scope('foo'):
with tf.variable_scope('bar'):
x = tf.get_variable('x', shape=())
x2 = tf.square(x**2, name='x2')
print(x.name)
# bar/x:0
print(x2.name)
# foo/bar/x2:0
En vous en tenant à variable_scope
seulement, vous éviterez certains maux de tête dus à ce type d'incompatibilité.
Vous pouvez les penser en deux groupes: variable_op_scope
et op_scope
prennent un ensemble de variables en entrée et sont conçus pour créer des opérations. La différence réside dans la manière dont ils affectent la création de variables avec tf.get_variable
:
def mysum(a,b,name=None):
with tf.op_scope([a,b],name,"mysum") as scope:
v = tf.get_variable("v", 1)
v2 = tf.Variable([0], name="v2")
assert v.name == "v:0", v.name
assert v2.name == "mysum/v2:0", v2.name
return tf.add(a,b)
def mysum2(a,b,name=None):
with tf.variable_op_scope([a,b],name,"mysum2") as scope:
v = tf.get_variable("v", 1)
v2 = tf.Variable([0], name="v2")
assert v.name == "mysum2/v:0", v.name
assert v2.name == "mysum2/v2:0", v2.name
return tf.add(a,b)
with tf.Graph().as_default():
op = mysum(tf.Variable(1), tf.Variable(2))
op2 = mysum2(tf.Variable(1), tf.Variable(2))
assert op.name == 'mysum/Add:0', op.name
assert op2.name == 'mysum2/Add:0', op2.name
remarquez le nom de la variable v
dans les deux exemples.
idem pour tf.name_scope
et tf.variable_scope
:
with tf.Graph().as_default():
with tf.name_scope("name_scope") as scope:
v = tf.get_variable("v", [1])
op = tf.add(v, v)
v2 = tf.Variable([0], name="v2")
assert v.name == "v:0", v.name
assert op.name == "name_scope/Add:0", op.name
assert v2.name == "name_scope/v2:0", v2.name
with tf.Graph().as_default():
with tf.variable_scope("name_scope") as scope:
v = tf.get_variable("v", [1])
op = tf.add(v, v)
v2 = tf.Variable([0], name="v2")
assert v.name == "name_scope/v:0", v.name
assert op.name == "name_scope/Add:0", op.name
assert v2.name == "name_scope/v2:0", v2.name
Vous pouvez en savoir plus sur la portée variable dans le tutoriel . Une question similaire était posée auparavant sur Stack Overflow.
De la dernière section de cette page de la documentation de tensorflow: Noms des opérations dans tf.variable_scope()
[...] quand on fait
with tf.variable_scope("name")
, cela ouvre implicitement untf.name_scope("name")
. Par exemple:
with tf.variable_scope("foo"):
x = 1.0 + tf.get_variable("v", [1])
assert x.op.name == "foo/add"
Les étendues de nom peuvent être ouvertes en plus d'une étendue de variable, elles n'affecteront alors que les noms des op, mais pas les variables.
with tf.variable_scope("foo"):
with tf.name_scope("bar"):
v = tf.get_variable("v", [1])
x = 1.0 + v
assert v.name == "foo/v:0"
assert x.op.name == "foo/bar/add"
Lorsque vous ouvrez une étendue de variable en utilisant un objet capturé au lieu d'une chaîne, nous ne modifions pas l'étendue du nom actuel pour ops.