Jaka jest różnica zakresu nazwy i zakresu zmiennej w tensorflow?

276

Jakie są różnice między tymi funkcjami?

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

Zwraca menedżer kontekstu do definiowania operacji, która tworzy zmienne. Ten menedżer kontekstu sprawdza, czy podane wartości pochodzą z tego samego wykresu, zapewnia, że ​​wykres ten jest wykresem domyślnym, i przesuwa zakres nazw i zakres zmiennych.


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

Zwraca menedżer kontekstu do użycia podczas definiowania op. Ten menedżer kontekstu sprawdza, czy podane wartości pochodzą z tego samego wykresu, zapewnia, że ​​wykres ten jest wykresem domyślnym, i przesuwa zakres nazw.


tf.name_scope(name)

Opakowanie do Graph.name_scope()korzystania z domyślnego wykresu. Zobacz Graph.name_scope()po więcej szczegółów.


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

Zwraca kontekst dla zakresu zmiennego. Zmienny zakres pozwala tworzyć nowe zmienne i udostępniać już utworzone, zapewniając kontrole, aby nie tworzyć lub dzielić się przypadkowo. Aby uzyskać szczegółowe informacje, zobacz Poradnik dotyczący zmiennych, tutaj przedstawiamy tylko kilka podstawowych przykładów.

Xiuyi Yang
źródło

Odpowiedzi:

377

Zacznijmy od krótkiego wprowadzenia do udostępniania zmiennych. Jest to mechanizm, TensorFlowktóry pozwala na dzielenie się zmiennymi dostępnymi w różnych częściach kodu bez przekazywania odniesień do zmiennej wokół.

Metodę tf.get_variablemożna zastosować z nazwą zmiennej jako argumentem, aby albo utworzyć nową zmienną o takiej nazwie, albo pobrać wcześniej utworzoną. Różni się to od używania tf.Variablekonstruktora, który będzie tworzył nową zmienną przy każdym wywołaniu (i potencjalnie doda sufiks do nazwy zmiennej, jeśli zmienna o takiej nazwie już istnieje).

Do celów mechanizmu współużytkowania zmiennych wprowadzono odrębny typ zakresu (zakres zmienny).

W rezultacie otrzymujemy dwa różne typy zakresów:

Oba zakresy mają taki sam wpływ na wszystkie operacje, jak i zmienne utworzone przy użyciu tf.Variable, tzn. Zakres zostanie dodany jako przedrostek do nazwy operacji lub zmiennej.

Jednak zakres nazw jest ignorowany przez tf.get_variable. Widzimy to w następującym przykładzie:

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

Jedynym sposobem umieszczenia zmiennej dostępnej za tf.get_variablepomocą zakresu jest użycie zakresu zmiennej, jak w poniższym przykładzie:

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

To pozwala nam łatwo udostępniać zmienne w różnych częściach programu, nawet w różnych zakresach nazw:

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

AKTUALIZACJA

Począwszy od wersji r0.11, op_scopei variable_op_scopesą zarówno przestarzała i zastąpiona name_scopea variable_scope.

Andrzej Pronobis
źródło
41
Dzięki za jasne wyjaśnienie. Oczywiście, kolejnym pytaniem byłoby: „ Dlaczego Tensorflow ma oba te myląco podobne mechanizmy? Dlaczego nie zastąpić ich tylko jedną scopemetodą, która skutecznie działa variable_scope?”.
John
8
Nie sądzę, że rozumiem koncepcyjnie, dlaczego nawet rozróżnienie między variable_scopevs name_scopejest potrzebne. Jeśli ktoś tworzy zmienną (w jakikolwiek sposób za pomocą tf.Variablelub tf.get_variable), wydaje mi się bardziej naturalne, że zawsze powinniśmy być w stanie ją uzyskać, jeśli określimy zakres lub jego pełną nazwę. Nie rozumiem, dlaczego jedna ignoruje nazwę zakresu, a druga nie. Czy rozumiesz racjonalność tego dziwnego zachowania?
Charlie Parker,
23
Powodem jest to, że w zakresie zmiennych można zdefiniować osobne zakresy zmiennych wielokrotnego użytku, na które nie ma wpływu bieżący zakres nazw używany do definiowania operacji.
Andrzej Pronobis
6
Witaj, czy możesz wyjaśnić, dlaczego nazwa zmiennej w zmiennej_zakresie zawsze kończy się na: 0? Czy to oznacza, że ​​nazwy zmiennych mogą kończyć się na: 1,: 2 itd., Więc jak to się dzieje?
James Fan
2
@JamesFan Każda „deklaracja” jest operacją, więc kiedy powiesz a = tf.Variable (.. name), otrzymasz tensor, ale tak naprawdę tworzy operację. jeśli wydrukujesz a, otrzymasz tensor o: 0. Jeśli wydrukujesz a. Op, otrzymasz operację, która obliczy tę wartość tensora.
Robert Lugg,
84

Zarówno variable_op_scope i op_scope są obecnie przestarzałe i nie powinny być w ogóle stosowane.

W odniesieniu do pozostałych dwóch, ja też miałem problemy ze zrozumieniem różnicy między variable_scope i name_scope (wyglądały prawie tak samo) przed Starałem się wyobrazić wszystko, tworząc prosty przykład:

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

Tutaj tworzę funkcję, która tworzy niektóre zmienne i stałe i grupuje je w zakresach (w zależności od podanego typu). W tej funkcji wypisuję również nazwy wszystkich zmiennych. Następnie wykonuję wykres, aby uzyskać wartości wynikowych wartości i zapisać pliki zdarzeń, aby zbadać je w TensorBoard. Jeśli to uruchomisz, otrzymasz:

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 

Podobny wzór zobaczysz, jeśli otworzysz TensorBoard (jak widzisz bpoza scope_nameprostokątem):


To daje odpowiedź :

Teraz widzisz, że tf.variable_scope()dodaje przedrostek do nazw wszystkich zmiennych (bez względu na to, jak je tworzysz), ops, stałe. Z drugiej strony tf.name_scope()ignoruje zmienne utworzone, tf.get_variable()ponieważ zakłada, że ​​wiesz, której zmiennej i w jakim zakresie chcesz użyć.

Dobra dokumentacja na temat udostępniania zmiennych mówi o tym

tf.variable_scope(): Zarządza przestrzeniami nazw przekazywanych do tf.get_variable().

Ta sama dokumentacja zawiera bardziej szczegółowe informacje o tym, jak działa Zmienny zakres i kiedy jest przydatny.

Salvador Dali
źródło
2
Wspaniała odpowiedź z przykładem i efektami wizualnymi, zdobądźmy tę odpowiedź pozytywnie nastawieni ludzie!
David Parks
43

Przestrzenie nazw to sposób organizowania nazw zmiennych i operatorów w sposób hierarchiczny (np. „ScopeA / scopeB / scopeC / op1”)

  • tf.name_scope tworzy przestrzeń nazw dla operatorów na domyślnym wykresie.
  • tf.variable_scope tworzy przestrzeń nazw dla zmiennych i operatorów na domyślnym wykresie.

  • tf.op_scopetak samo jak tf.name_scope, ale dla wykresu, na którym zostały utworzone określone zmienne.

  • tf.variable_op_scopetak samo jak tf.variable_scope, ale dla wykresu, na którym zostały utworzone określone zmienne.

Odnośniki do powyższych źródeł pomagają w jednoznacznym rozwiązaniu tego problemu z dokumentacją.

Ten przykład pokazuje, że wszystkie typy zakresów definiują przestrzenie nazw dla zmiennych i operatorów z następującymi różnicami:

  1. zakresy zdefiniowane przez tf.variable_op_scopelub tf.variable_scopesą z nimi zgodne tf.get_variable(ignoruje dwa inne zakresy)
  2. tf.op_scopei tf.variable_op_scopepo prostu wybierz wykres z listy określonych zmiennych, aby utworzyć zakres. Inne niż ich zachowanie równe tf.name_scopei tf.variable_scopeodpowiednio
  3. tf.variable_scopei variable_op_scopedodaj określony lub domyślny inicjator.
Alexander Gorban
źródło
Dla wykresu, na którym stworzono określone zmienne? Czy to oznacza, tak jak w powyższym przykładzie fabrizioM, z tf.variable_op_scope ([a, b], name, „mysum2”) jako zakresem, tutaj parametr a i b nie ma wpływu na tę funkcję i zmienne zdefiniowane w tym zakresie?
Xiuyi Yang
Odpowiedź na oba pytania brzmi „tak”: wykres, na którym zostały utworzone określone zmienne i nie są one modyfikowane.
Alexander Gorban,
Czy to oznacza, że ​​tf.name_scope i tf.variable_scope mogą być używane tylko na domyślnym wykresie, ale kiedy oczywiście definiujesz i konstruujesz wykres za pomocą tf.Graph (), pozostałe dwie funkcje tf.op_scope i tf.variable_op_scope nie mogą być używane w ten wykres!
Xiuyi Yang,
12

Uprośćmy: po prostu użyj tf.variable_scope. Cytując programistę TF :

Obecnie zalecamy wszystkim korzystanie variable_scopei nieużywanie, name_scopez wyjątkiem wewnętrznego kodu i bibliotek.

Oprócz tego, że variable_scopejego funkcjonalność zasadniczo rozszerza funkcje name_scope, zastanów się, jak nie grają razem tak ładnie:

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

Trzymając się variable_scopetylko tego, unikniesz bólu głowy z powodu tego rodzaju niezgodności.

P-Gn
źródło
9

Podobnie jak w przypadku interfejsu API r0.11 op_scopei variable_op_scopeoba są przestarzałe . name_scopei variable_scopemoże być zagnieżdżony:

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
źródło
8

Można myśleć je jako dwie grupy: variable_op_scopei op_scopewziąć zestaw zmiennych jako wejście i są przeznaczone do tworzenia operacji. Różnica polega na tym, jak wpływają one na tworzenie zmiennych za pomocą 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

zwróć uwagę na nazwę zmiennej vw dwóch przykładach.

to samo dla tf.name_scopei 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

Więcej informacji na temat zakresu zmiennego można znaleźć w samouczku . Podobne pytanie zostało zadane wcześniej w przypadku przepełnienia stosu.

fabrizioM
źródło
2

Z ostatniej sekcji tej strony dokumentacji tensorflow: Nazwy operacji wtf.variable_scope()

[...] kiedy to robimy with tf.variable_scope("name"), domyślnie otwiera się tf.name_scope("name"). Na przykład:

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

Zakresy nazw można otwierać oprócz zakresu zmiennych, a wtedy będą one wpływać tylko na nazwy operacji, ale nie na zmienne.

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"

Podczas otwierania zakresu zmiennej za pomocą przechwyconego obiektu zamiast ciągu nie zmieniamy bieżącego zakresu nazwy dla operacji.

Guillermo González de Garibay
źródło
2

Odpowiedź zgodna z Tensorflow 2.0 : Objaśnienia Andrzej Pronobisi Salvador Daliszczegółowe informacje na temat związanych z nimi funkcji Scope.

Z omówionych powyżej funkcji zakresu, które są aktywne od teraz (17 lutego 2020 r.), Są variable_scopeiname_scope .

Określanie wywołań zgodnych z 2.0 dla tych funkcji, które omówiliśmy powyżej, z korzyścią dla społeczności.

Funkcja w 1.x :

tf.variable_scope

tf.name_scope

Odpowiednia funkcja w 2.x :

tf.compat.v1.variable_scope

tf.name_scope( tf.compat.v2.name_scopejeśli migrowano z 1.x to 2.x)

Aby uzyskać więcej informacji na temat migracji z wersji 1.x do wersji 2.x, zapoznaj się z niniejszym przewodnikiem migracji .

Obsługa Tensorflow
źródło