¿Cuál es la diferencia del alcance del nombre y un alcance variable en el tensorflow?

276

¿Cuáles son las diferencias entre estas funciones?

tf.variable_op_scope(values, name, default_name, initializer=None)

Devuelve un administrador de contexto para definir una operación que crea variables. Este administrador de contexto valida que los valores dados son del mismo gráfico, garantiza que ese gráfico sea el gráfico predeterminado y aplica un ámbito de nombre y un ámbito variable.


tf.op_scope(values, name, default_name=None)

Devuelve un administrador de contexto para usar al definir una operación de Python. Este administrador de contexto valida que los valores dados son del mismo gráfico, asegura que ese gráfico es el gráfico predeterminado y empuja un ámbito de nombre.


tf.name_scope(name)

Contenedor para Graph.name_scope()usar el gráfico predeterminado. Ver Graph.name_scope()para más detalles.


tf.variable_scope(name_or_scope, reuse=None, initializer=None)

Devuelve un contexto para el alcance variable. El alcance variable permite crear nuevas variables y compartir las ya creadas al tiempo que proporciona comprobaciones para no crear o compartir por accidente. Para obtener más información, consulte el Cómo de alcance variable, aquí solo presentamos algunos ejemplos básicos.

Xiuyi Yang
fuente

Respuestas:

377

Comencemos con una breve introducción al intercambio de variables. Es un mecanismo TensorFlowque permite compartir variables a las que se accede en diferentes partes del código sin pasar referencias a la variable.

El método tf.get_variablese puede usar con el nombre de la variable como argumento para crear una nueva variable con dicho nombre o recuperar la que se creó anteriormente. Esto es diferente de usar el tf.Variableconstructor que creará una nueva variable cada vez que se llame (y potencialmente agregará un sufijo al nombre de la variable si ya existe una variable con dicho nombre).

Es para el propósito del mecanismo de intercambio variable que se introdujo un tipo separado de alcance (alcance variable).

Como resultado, terminamos teniendo dos tipos diferentes de ámbitos:

Ambos ámbitos tienen el mismo efecto en todas las operaciones, así como en las variables creadas usando tf.Variable, es decir, el ámbito se agregará como prefijo a la operación o al nombre de la variable.

Sin embargo, el alcance del nombre es ignorado por tf.get_variable. Podemos ver eso en el siguiente ejemplo:

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

La única forma de colocar una variable a la que se accede usando tf.get_variableun alcance es usar un alcance variable, como en el siguiente ejemplo:

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

Esto nos permite compartir fácilmente variables en diferentes partes del programa, incluso dentro de diferentes ámbitos de nombre:

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

ACTUALIZAR

A partir de la versión r0.11, op_scopey variable_op_scopeestán en desuso y reemplazados por name_scopey variable_scope.

Andrzej Pronobis
fuente
41
Gracias por la explicación clara. Naturalmente, una pregunta de seguimiento sería " ¿ Por qué Tensorflow tiene estos dos mecanismos confusamente similares? ¿Por qué no reemplazarlos con un solo scopemétodo que efectivamente tiene un variable_scope?"
John
8
No creo entender conceptualmente por qué es necesaria la distinción entre variable_scopevs. name_scopeSi uno crea una variable (de alguna manera con tf.Variableo tf.get_variable), me parece más natural que siempre podamos obtenerla si especificamos el alcance o su nombre completo. No entiendo por qué uno ignora el nombre del alcance mientras que el otro no. ¿Entiendes lo racional de este comportamiento extraño?
Charlie Parker
23
La razón es que con el alcance variable, se pueden definir ámbitos separados para las variables reutilizables que no se ven afectadas por el alcance del nombre actual utilizado para definir operaciones.
Andrzej Pronobis
66
Hola, ¿puedes explicar por qué el nombre de la variable en un variable_scope siempre termina con un: 0? ¿Esto significa que puede haber nombres de variables que terminan en: 1,: 2, etc., entonces, cómo puede suceder esto?
James Fan el
2
@JamesFan Cada "declaración" es una operación, así que cuando dices a = tf.Variable (.. name) obtienes un tensor, pero en realidad también crea una operación. si imprime un, obtendrá el tensor con un: 0. Si imprime a.op, obtiene la operación que calculará ese valor tensorial.
Robert Lugg
84

Tanto variable_op_scope como op_scope ahora están en desuso y no deberían usarse en absoluto.

Con respecto a los otros dos, también tuve problemas para comprender la diferencia entre variable_scope y name_scope (se veían casi iguales) antes de intentar visualizar todo creando un ejemplo 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()

Aquí creo una función que crea algunas variables y constantes y las agrupa en ámbitos (según el tipo que proporcioné). En esta función, también imprimo los nombres de todas las variables. Después de eso, ejecuto el gráfico para obtener valores de los valores resultantes y guardar archivos de eventos para investigarlos en TensorBoard. Si ejecuta esto, obtendrá lo siguiente:

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 

Verá el patrón similar si abre TensorBoard (como ve que bestá fuera del scope_namerectangular):


Esto te da la respuesta :

Ahora verá que tf.variable_scope()agrega un prefijo a los nombres de todas las variables (no importa cómo las cree), operaciones, constantes. Por otro lado, tf.name_scope()ignora las variables creadas tf.get_variable()porque supone que usted sabe qué variable y en qué ámbito desea usar.

Una buena documentación sobre Compartir variables le dice que

tf.variable_scope(): Administra espacios de nombres para los nombres pasados ​​a tf.get_variable().

La misma documentación proporciona más detalles sobre cómo funciona Variable Scope y cuándo es útil.

Salvador Dalí
fuente
2
Fabulosa respuesta con el ejemplo y las imágenes, ¡obtengamos esta respuesta amigos votados!
David Parks
43

Los espacios de nombres son una forma de organizar nombres para variables y operadores de manera jerárquica (por ejemplo, "scopeA / scopeB / scopeC / op1")

  • tf.name_scope crea espacio de nombres para operadores en el gráfico predeterminado.
  • tf.variable_scope crea espacio de nombres para variables y operadores en el gráfico predeterminado.

  • tf.op_scopeigual que tf.name_scope, pero para el gráfico en el que se crearon las variables especificadas.

  • tf.variable_op_scopeigual que tf.variable_scope, pero para el gráfico en el que se crearon las variables especificadas.

Los enlaces a las fuentes anteriores ayudan a desambiguar este problema de documentación.

Este ejemplo muestra que todos los tipos de ámbitos definen espacios de nombres para variables y operadores con las siguientes diferencias:

  1. ámbitos definidos por tf.variable_op_scopeo tf.variable_scopeson compatibles con tf.get_variable(ignora otros dos ámbitos)
  2. tf.op_scopey tf.variable_op_scopesimplemente seleccione un gráfico de una lista de variables especificadas para crear un alcance. Aparte de su comportamiento igual tf.name_scopey en tf.variable_scopeconsecuencia
  3. tf.variable_scopey variable_op_scopeagregue el inicializador especificado o predeterminado.
Alexander Gorban
fuente
Para el gráfico en el que se crearon las variables especificadas? ¿Significa esto, como el ejemplo anterior de fabrizioM, con tf.variable_op_scope ([a, b], name, "mysum2") como alcance, aquí los parámetros ayb no se ven afectados por esta función y las variables definidas en este alcance se ven afectadas?
Xiuyi Yang
La respuesta para ambas preguntas es sí: el gráfico en el que se crearon las variables especificadas y no se modifican.
Alexander Gorban
¿Significa esto que tf.name_scope y tf.variable_scope solo se pueden usar en el gráfico predeterminado, pero cuando obviamente define y construye un gráfico usando tf.Graph (), las otras dos funciones tf.op_scope y tf.variable_op_scope no se pueden usar en este gráfico!
Xiuyi Yang
12

Hagámoslo simple: solo utilízalo tf.variable_scope. Citando a un desarrollador de TF :

Actualmente, recomendamos que todos usen variable_scopey no usen, name_scopeexcepto el código interno y las bibliotecas.

Además del hecho de que variable_scopesu funcionalidad básicamente extiende las de name_scope, considere cómo no juegan tan bien juntos:

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

Al apegarte variable_scopesolo a evitar algunos dolores de cabeza debido a este tipo de incompatibilidad.

P-Gn
fuente
9

En cuanto a API r0.11, op_scopey variable_op_scopeambos están en desuso . name_scopey variable_scopese puede anidar:

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'
sgu
fuente
8

Puede pensarlos como dos grupos: variable_op_scopey op_scopetomar un conjunto de variables como entrada y están diseñados para crear operaciones. La diferencia está en cómo afectan la creación de variables con 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

observe el nombre de la variable ven los dos ejemplos.

lo mismo para tf.name_scopey 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

Puede leer más sobre el alcance variable en el tutorial . Se hizo una pregunta similar antes sobre Stack Overflow.

fabrizioM
fuente
2

Desde la última sección de esta página de la documentación de tensorflow: nombres de operaciones entf.variable_scope()

[...] cuando lo hacemos with tf.variable_scope("name"), esto abre implícitamente a tf.name_scope("name"). Por ejemplo:

with tf.variable_scope("foo"):
  x = 1.0 + tf.get_variable("v", [1])
assert x.op.name == "foo/add"

Los ámbitos de nombre se pueden abrir además de un ámbito variable, y luego solo afectarán los nombres de las operaciones, pero no de las 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"

Al abrir un ámbito variable utilizando un objeto capturado en lugar de una cadena, no modificamos el ámbito del nombre actual para las operaciones.

Guillermo González de Garibay
fuente
2

Respuesta compatible con Tensorflow 2.0 : Las explicaciones de Andrzej Pronobisy Salvador Daliestán muy detalladas sobre las funciones relacionadas Scope.

De las funciones de alcance discutidas anteriormente, que están activas a partir de ahora (17 de febrero de 2020) son variable_scopey name_scope.

Especificamos las llamadas compatibles 2.0 para esas funciones, discutimos anteriormente, para el beneficio de la comunidad.

Función en 1.x :

tf.variable_scope

tf.name_scope

Función respectiva en 2.x :

tf.compat.v1.variable_scope

tf.name_scope( tf.compat.v2.name_scopesi migró de 1.x to 2.x)

Para obtener más información sobre la migración de 1.xa 2.x, consulte esta Guía de migración .

Soporte Tensorflow
fuente