web-dev-qa-db-fra.com

Quelle est la différence entre la portée du nom et une portée variable dans tensorflow?

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. Voir Graph.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.

243
Xiuyi Yang

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:

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
_

MISE À JOUR

À 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_.

347
Andrzej Pronobis

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): enter image description here


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é.

65
Salvador Dali

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:

  1. les portées définies par _tf.variable_op_scope_ ou _tf.variable_scope_ sont compatibles avec _tf.get_variable_ (elle ignore deux autres portées).
  2. _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équence
  3. _tf.variable_scope_ et _variable_op_scope_ ajoutent les initialiseurs spécifiés ou par défaut.
43
Alexander Gorban

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'
9
sgu

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 utiliser name_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é.

9
P-Gn

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.

7
fabrizioM

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 un tf.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.