Qual é a diferença do escopo do nome e do escopo variável no fluxo tensor?


276

Quais são as diferenças entre essas funções?

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

Retorna um gerenciador de contexto para definir uma operação que cria variáveis. Esse gerenciador de contexto valida se os valores fornecidos são do mesmo gráfico, garante que o gráfico seja o gráfico padrão e envia um escopo de nome e um escopo variável.


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

Retorna um gerenciador de contexto para uso ao definir uma operação em Python. Esse gerenciador de contexto valida se os valores fornecidos são do mesmo gráfico, garante que esse gráfico seja o gráfico padrão e envia um escopo de nome.


tf.name_scope(name)

Wrapper para Graph.name_scope()usar o gráfico padrão. Veja Graph.name_scope()para mais detalhes.


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

Retorna um contexto para o escopo variável. O escopo variável permite criar novas variáveis ​​e compartilhar as já criadas, fornecendo verificações para não criar ou compartilhar por acidente. Para obter detalhes, consulte o Tutorial sobre escopo variável, aqui apresentamos apenas alguns exemplos básicos.


Respostas:


377

Vamos começar com uma breve introdução ao compartilhamento de variáveis. É um mecanismo TensorFlowque permite compartilhar variáveis ​​acessadas em diferentes partes do código sem passar referências à variável ao redor.

O método tf.get_variablepode ser usado com o nome da variável como argumento para criar uma nova variável com esse nome ou recuperar a que foi criada antes. Isso é diferente de usar o tf.Variableconstrutor que criará uma nova variável toda vez que for chamada (e potencialmente incluirá um sufixo no nome da variável, se já existir uma variável com esse nome).

É para o objetivo do mecanismo de compartilhamento de variáveis ​​que um tipo separado de escopo (escopo variável) foi introduzido.

Como resultado, acabamos tendo dois tipos diferentes de escopos:

Ambos os escopos têm o mesmo efeito em todas as operações, bem como nas variáveis ​​criadas usando tf.Variable, ou seja, o escopo será adicionado como um prefixo à operação ou ao nome da variável.

No entanto, o escopo do nome é ignorado por tf.get_variable. Podemos ver isso no seguinte exemplo:

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

A única maneira de colocar uma variável acessada usando tf.get_variableem um escopo é usar um escopo variável, como no exemplo a seguir:

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

Isso nos permite compartilhar facilmente variáveis ​​entre diferentes partes do programa, mesmo dentro de diferentes escopos de nome:

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

ATUALIZAR

A partir da versão r0.11 op_scopee variable_op_scopesão preteridos e substituídos por name_scopee variable_scope.


41
Obrigado pela explicação clara. Naturalmente, uma pergunta de acompanhamento seria " Por que o Tensorflow tem esses dois mecanismos semelhantes e confusos? Por que não substituí-los por apenas um scopemétodo que efetivamente faz um variable_scope?"
John

8
Acho que não entendo conceitualmente por que a distinção entre variable_scopevs name_scopeé necessária. Se alguém cria uma variável (de qualquer forma com tf.Variableou tf.get_variable), parece-me mais natural que sempre possamos obtê-la se especificarmos o escopo ou o nome completo. Não entendo por que um ignora a coisa do nome do escopo, enquanto o outro não. Você entende o racional desse comportamento estranho?
Charlie Parker

23
O motivo é que, com escopo de variável, é possível definir escopos separados para variáveis ​​reutilizáveis ​​que não são afetadas pelo escopo de nome atual usado para definir operações.
Andrzej Pronobis 21/03

6
Olá, você pode explicar por que o nome da variável em um variable_scope sempre termina com um: 0? Isso significa que pode haver nomes de variáveis ​​que terminem com: 1,: 2, etc., então como isso pode acontecer?
James Fan

2
@JamesFan Cada "declaração" é uma operação, portanto, quando você diz a = tf.Variable (.. name), você recebe de volta um tensor, mas na verdade ele também cria uma operação. se você imprimir a, receberá o tensor com a: 0. Se você imprimir a.op, obtém a operação que calculará esse valor do tensor.
precisa

84

Ambos variable_op_scope e op_scope estão agora obsoleto e não deve ser usado em tudo.

Em relação aos outros dois, também tive problemas para entender a diferença entre variable_scope e name_scope (eles pareciam quase iguais) antes de tentar visualizar tudo criando um exemplo simples:

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

Aqui, crio uma função que cria algumas variáveis ​​e constantes e as agrupa em escopos (dependendo do tipo que eu forneço). Nesta função, também imprimo os nomes de todas as variáveis. Depois disso, executo o gráfico para obter valores dos valores resultantes e salvar os arquivos de eventos para investigá-los no TensorBoard. Se você executar isso, obterá o seguinte:

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 

Você vê o padrão semelhante se abrir o TensorBoard (como você vê bfora do scope_nameretangular):


Isso fornece a resposta :

Agora você vê que tf.variable_scope()adiciona um prefixo aos nomes de todas as variáveis ​​(não importa como você as cria), ops, constantes. Por outro lado, tf.name_scope()ignora as variáveis ​​criadas com, tf.get_variable()porque pressupõe que você sabe qual variável e em qual escopo você deseja usar.

Uma boa documentação sobre o compartilhamento de variáveis informa que

tf.variable_scope(): Gerencia namespaces para nomes passados ​​para tf.get_variable().

A mesma documentação fornece mais detalhes de como o Escopo Variável funciona e quando é útil.


2
Resposta fabulosa com o exemplo e os visuais, vamos obter essa resposta pessoal votado!
David Parks

43

Namespaces é uma maneira de organizar nomes para variáveis ​​e operadores de maneira hierárquica (por exemplo, "scopeA / scopeB / scopeC / op1")

  • tf.name_scope cria um espaço para nome para operadores no gráfico padrão.
  • tf.variable_scope cria um espaço para nome para variáveis ​​e operadores no gráfico padrão.

  • tf.op_scopeigual a tf.name_scope, mas para o gráfico no qual as variáveis ​​especificadas foram criadas.

  • tf.variable_op_scopeigual a tf.variable_scope, mas para o gráfico no qual as variáveis ​​especificadas foram criadas.

Os links para as fontes acima ajudam a desambiguar esse problema de documentação.

Este exemplo mostra que todos os tipos de escopos definem namespaces para variáveis ​​e operadores com as seguintes diferenças:

  1. escopos definidos por tf.variable_op_scopeou tf.variable_scopecompatíveis comtf.get_variable (ignora outros dois escopos)
  2. tf.op_scopee tf.variable_op_scopeapenas selecione um gráfico de uma lista de variáveis ​​especificadas para criar um escopo. Diferente de seu comportamento igual tf.name_scopee tf.variable_scopeadequado
  3. tf.variable_scopee variable_op_scopeadicione o inicializador especificado ou padrão.

Para o gráfico em que variáveis ​​especificadas foram criadas? Isso significa como o exemplo acima do fabrizioM, com tf.variable_op_scope ([a, b], name, "mysum2") como escopo, aqui os parâmetros aeb não são afetados por essa função e as variáveis ​​definidas neste escopo são afetadas?
Xiuyi Yang

A resposta para ambas as perguntas é sim: o gráfico no qual as variáveis ​​especificadas foram criadas e elas não são modificadas.
precisa

Isso significa que tf.name_scope e tf.variable_scope são usados ​​apenas no gráfico padrão, mas quando você obviamente define e constrói um gráfico usando tf.Graph (), as outras duas funções tf.op_scope e tf.variable_op_scope não podem ser usadas no esse gráfico!
Xiuyi Yang

12

Vamos simplificar: basta usar tf.variable_scope. Citando um desenvolvedor TF, :

Atualmente, recomendamos que todos usem variable_scopee não usem, name_scopeexceto o código interno e as bibliotecas.

Além do fato de que variable_scopea funcionalidade basicamente estende a funcionalidade name_scope, considere como eles não são tão bons 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

Ao aderir variable_scopeapenas a você evita algumas dores de cabeça devido a esse tipo de incompatibilidade.


9

Quanto à API r0.11, op_scopee variable_op_scopeambos estão obsoletos . name_scopee variable_scopepode ser aninhado:

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'

8

Você pode pensar neles como dois grupos: variable_op_scopee op_scopepegar um conjunto de variáveis ​​como entrada e foram projetados para criar operações. A diferença está em como elas afetam a criação de variáveis ​​com 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 o nome da variável vnos dois exemplos.

o mesmo para tf.name_scopee 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

Você pode ler mais sobre o escopo da variável no tutorial . Uma pergunta semelhante foi feita antes no Stack Overflow.


2

Na última seção desta página da documentação do tensorflow: Nomes das operações emtf.variable_scope()

[...] quando o fazemos with tf.variable_scope("name"), isso abre implicitamente a tf.name_scope("name"). Por exemplo:

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

Os escopos de nome podem ser abertos além de um escopo de variável e, em seguida, afetarão apenas os nomes das operações, mas não as variáveis.

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"

Ao abrir um escopo de variável usando um objeto capturado em vez de uma sequência, não alteramos o escopo do nome atual para ops.


2

Resposta compatível com Tensorflow 2.0 : As explicações Andrzej Pronobise Salvador Dalisão muito detalhadas sobre as funções relacionadas a Scope.

Das funções de escopo discutidas acima, que estão ativas a partir de agora (17 de fevereiro de 2020) são variable_scopee name_scope.

Especificando as chamadas compatíveis com 2.0 para essas funções, discutimos acima, para o benefício da comunidade.

Função no 1.x :

tf.variable_scope

tf.name_scope

Função respectiva no 2.x :

tf.compat.v1.variable_scope

tf.name_scope( tf.compat.v2.name_scopese migrado de 1.x to 2.x)

Para obter mais informações sobre a migração de 1.x para 2.x, consulte este Guia de migração .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.