Was ist der Unterschied zwischen dem Namensbereich und einem variablen Bereich im Tensorflow?

276

Was sind die Unterschiede zwischen diesen Funktionen?

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

Gibt einen Kontextmanager zum Definieren einer Operation zurück, die Variablen erstellt. Dieser Kontextmanager überprüft, ob die angegebenen Werte aus demselben Diagramm stammen, stellt sicher, dass dieses Diagramm das Standarddiagramm ist, und überträgt einen Namensbereich und einen Variablenbereich.


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

Gibt einen Kontextmanager zur Verwendung beim Definieren einer Python-Operation zurück. Dieser Kontextmanager überprüft, ob die angegebenen Werte aus demselben Diagramm stammen, stellt sicher, dass dieses Diagramm das Standarddiagramm ist, und legt einen Namensbereich fest.


tf.name_scope(name)

Wrapper zur Graph.name_scope()Verwendung des Standarddiagramms. Siehe Graph.name_scope()für weitere Details.


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

Gibt einen Kontext für den variablen Bereich zurück. Der Variablenbereich ermöglicht das Erstellen neuer Variablen und das Freigeben bereits erstellter Variablen, während Überprüfungen bereitgestellt werden, um nicht versehentlich zu erstellen oder freizugeben. Einzelheiten finden Sie in der Anleitung zum variablen Umfang. Hier werden nur einige grundlegende Beispiele vorgestellt.

Xiuyi Yang
quelle

Antworten:

377

Beginnen wir mit einer kurzen Einführung in das Teilen von Variablen. Dies ist ein Mechanismus TensorFlow, mit dem Variablen, auf die in verschiedenen Teilen des Codes zugegriffen wird, gemeinsam genutzt werden können, ohne Verweise auf die Variable zu übergeben.

Die Methode tf.get_variablekann mit dem Namen der Variablen als Argument verwendet werden, um entweder eine neue Variable mit diesem Namen zu erstellen oder die zuvor erstellte Variable abzurufen. Dies unterscheidet sich von der Verwendung des tf.VariableKonstruktors, der bei jedem Aufruf eine neue Variable erstellt (und möglicherweise dem Variablennamen ein Suffix hinzufügt, wenn bereits eine Variable mit einem solchen Namen vorhanden ist).

Für den Zweck des Mechanismus zur gemeinsamen Nutzung von Variablen wurde ein separater Bereichstyp (variabler Bereich) eingeführt.

Infolgedessen haben wir zwei verschiedene Arten von Bereichen:

Beide Bereiche haben den gleichen Effekt auf alle Operationen sowie auf Variablen, die mit erstellt wurden tf.Variable, dh der Bereich wird als Präfix zum Operations- oder Variablennamen hinzugefügt.

Der Namensbereich wird jedoch von ignoriert tf.get_variable. Wir können das im folgenden Beispiel sehen:

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

Die einzige Möglichkeit, eine Variable, auf die mit zugegriffen wird, tf.get_variablein einem Bereich zu platzieren, besteht darin, einen Variablenbereich wie im folgenden Beispiel zu verwenden:

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

Auf diese Weise können wir Variablen problemlos über verschiedene Teile des Programms hinweg teilen, auch innerhalb verschiedener Namensbereiche:

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

AKTUALISIEREN

Ab Version r0.11, op_scopeund variable_op_scopesind sowohl veraltet und ersetzt durch name_scopeund variable_scope.

Andrzej Pronobis
quelle
41
Danke für die klare Erklärung. Eine Folgefrage wäre natürlich: " Warum hat Tensorflow diese beiden verwirrend ähnlichen Mechanismen? Warum ersetzen Sie sie nicht durch nur eine scopeMethode, die a effektiv ausführt variable_scope?"
John
8
Ich glaube nicht, dass ich konzeptionell verstehe, warum die Unterscheidung zwischen variable_scopevs name_scopeüberhaupt notwendig ist. Wenn man eine Variable erstellt (in irgendeiner Weise mit tf.Variableoder tf.get_variable), erscheint es mir natürlicher, dass wir sie immer erhalten können, wenn wir den Bereich oder ihren vollständigen Namen angeben. Ich verstehe nicht, warum einer den Scope-Namen ignoriert, während der andere dies nicht tut. Verstehst du das Rationale für dieses seltsame Verhalten?
Charlie Parker
23
Der Grund dafür ist, dass mit dem Variablenbereich separate Bereiche für wiederverwendbare Variablen definiert werden können, die nicht vom aktuellen Namensbereich betroffen sind, der zum Definieren von Operationen verwendet wird.
Andrzej Pronobis
6
Hallo , Kannst du erklären, warum der Variablenname in einem Variablenbereich immer mit: 0 endet? Bedeutet dies, dass es möglicherweise Variablennamen gibt, die mit: 1 ,: 2 usw. enden. Wie kann dies geschehen?
James Fan
2
@JamesFan Jede "Deklaration" ist eine Operation. Wenn Sie also a = tf.Variable (.. name) sagen, erhalten Sie einen Tensor zurück, der jedoch auch eine Operation erstellt. Wenn Sie a drucken, erhalten Sie den Tensor mit a: 0. Wenn Sie a.op drucken, erhalten Sie die Operation, die diesen Tensorwert berechnet.
Robert Lugg
84

Sowohl variable_op_scope als auch op_scope sind jetzt veraltet und sollten überhaupt nicht verwendet werden.

In Bezug auf die beiden anderen hatte ich auch Probleme, den Unterschied zwischen variable_scope und name_scope zu verstehen (sie sahen fast gleich aus), bevor ich versuchte, alles anhand eines einfachen Beispiels zu visualisieren:

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

Hier erstelle ich eine Funktion, die einige Variablen und Konstanten erstellt und sie in Bereichen gruppiert (abhängig von dem von mir angegebenen Typ). In dieser Funktion drucke ich auch die Namen aller Variablen. Danach führe ich das Diagramm aus, um die Werte der resultierenden Werte abzurufen und Ereignisdateien zu speichern, um sie in TensorBoard zu untersuchen. Wenn Sie dies ausführen, erhalten Sie Folgendes:

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 

Sie sehen das ähnliche Muster, wenn Sie TensorBoard öffnen (wie Sie sehen, baußerhalb des scope_nameRechtecks):


Dies gibt Ihnen die Antwort :

Jetzt sehen Sie, dass tf.variable_scope()den Namen aller Variablen (unabhängig davon, wie Sie sie erstellen), Operationen und Konstanten ein Präfix hinzugefügt wird. tf.name_scope()Ignoriert andererseits Variablen, die mit erstellt wurden, tf.get_variable()da davon ausgegangen wird, dass Sie wissen, welche Variable und in welchem ​​Bereich Sie verwenden möchten.

Eine gute Dokumentation zum Freigeben von Variablen zeigt Ihnen dies

tf.variable_scope(): Verwaltet Namespaces für Namen, die an übergeben werden tf.get_variable().

In derselben Dokumentation finden Sie weitere Informationen zur Funktionsweise des Variablenbereichs und zu dessen Nützlichkeit.

Salvador Dali
quelle
2
Fabelhafte Antwort mit dem Beispiel und der Grafik, lassen Sie uns diese Antwort hochstimmen, Leute!
David Parks
43

Namespaces ist eine Möglichkeit, Namen für Variablen und Operatoren hierarchisch zu organisieren (z. B. "scopeA / scopeB / scopeC / op1").

  • tf.name_scope Erstellt einen Namespace für Operatoren im Standarddiagramm.
  • tf.variable_scope Erstellt einen Namespace für Variablen und Operatoren im Standarddiagramm.

  • tf.op_scopewie tf.name_scope, jedoch für das Diagramm, in dem bestimmte Variablen erstellt wurden.

  • tf.variable_op_scopewie tf.variable_scope, jedoch für das Diagramm, in dem bestimmte Variablen erstellt wurden.

Links zu den oben genannten Quellen helfen dabei, dieses Dokumentationsproblem zu unterscheiden.

Dieses Beispiel zeigt, dass alle Arten von Bereichen Namespaces für Variablen und Operatoren mit folgenden Unterschieden definieren:

  1. Bereiche, die durch definiert sind tf.variable_op_scopeoder tf.variable_scopemit tf.get_variablediesen kompatibel sind (zwei andere Bereiche werden ignoriert)
  2. tf.op_scopeund tf.variable_op_scopewählen Sie einfach ein Diagramm aus einer Liste der angegebenen Variablen aus, um einen Bereich für zu erstellen. Anders als ihr Verhalten gleich tf.name_scopeund tf.variable_scopeentsprechend
  3. tf.variable_scopeund variable_op_scopefügen Sie den angegebenen oder Standardinitialisierer hinzu.
Alexander Gorban
quelle
Für das Diagramm, in dem die angegebenen Variablen erstellt wurden? Bedeutet dies, wie im obigen Beispiel von fabrizioM, mit tf.variable_op_scope ([a, b], Name, "mysum2") als Bereich, hier sind die Parameter a und b von dieser Funktion nicht betroffen und die in diesem Bereich definierten Variablen sind betroffen?
Xiuyi Yang
Die Antwort auf beide Fragen lautet Ja: Das Diagramm, in dem bestimmte Variablen erstellt wurden und das nicht geändert wird.
Alexander Gorban
Bedeutet dies, dass tf.name_scope und tf.variable_scope nur im Standarddiagramm verwendet werden. Wenn Sie jedoch offensichtlich ein Diagramm mit tf.Graph () definieren und konstruieren, können die beiden anderen Funktionen tf.op_scope und tf.variable_op_scope nicht verwendet werden diese Grafik!
Xiuyi Yang
12

Machen wir es einfach: Verwenden Sie einfach tf.variable_scope. Zitieren eines TF-Entwicklers :

Derzeit empfehlen wir jedem, ihn zu verwenden variable_scopeund nicht zu verwenden, name_scopeaußer für internen Code und Bibliotheken.

Neben der Tatsache, dass variable_scopedie Funktionalität im Wesentlichen die von erweitert name_scope, sollten Sie bedenken, dass sie nicht so gut zusammenspielen:

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

Wenn variable_scopeSie sich nur an diese halten, vermeiden Sie Kopfschmerzen aufgrund dieser Art von Inkompatibilität.

P-Gn
quelle
9

Wie für API r0.11, op_scopeund variable_op_scopebeide sind veraltet . name_scopeund variable_scopekann verschachtelt werden:

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
quelle
8

Sie können sie als zwei Gruppen betrachten: variable_op_scopeund op_scopeeine Reihe von Variablen als Eingabe verwenden, um Operationen zu erstellen. Der Unterschied besteht darin, wie sie sich auf die Erstellung von Variablen auswirken mit 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

Beachten Sie den Namen der Variablen vin den beiden Beispielen.

Gleiches gilt für tf.name_scopeund 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

Weitere Informationen zum Variablenbereich finden Sie im Lernprogramm . Eine ähnliche Frage wurde vor gefragt auf Stack - Überlauf.

fabrizioM
quelle
2

Aus dem letzten Abschnitt dieser Seite der Tensorflow-Dokumentation: Namen der Operationen intf.variable_scope()

[...] wenn wir dies tun with tf.variable_scope("name"), öffnet dies implizit a tf.name_scope("name"). Zum Beispiel:

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

Namensbereiche können zusätzlich zu einem Variablenbereich geöffnet werden und wirken sich dann nur auf die Namen der Operationen aus, nicht jedoch auf Variablen.

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"

Wenn Sie einen variablen Bereich mit einem erfassten Objekt anstelle einer Zeichenfolge öffnen, ändern wir den aktuellen Namensbereich für Operationen nicht.

Guillermo González de Garibay
quelle
2

Tensorflow 2.0-kompatible Antwort : Die Erläuterungen zu Andrzej Pronobisund Funktionen Salvador Dalisind sehr detailliert Scope.

Von den oben diskutierten Bereichsfunktionen, die ab sofort (17. Februar 2020) aktiv sind, sind variable_scopeund name_scope.

Festlegen der 2.0-kompatiblen Aufrufe für diese Funktionen, die wir oben zum Nutzen der Community erörtert haben.

Funktion in 1.x :

tf.variable_scope

tf.name_scope

Entsprechende Funktion in 2.x :

tf.compat.v1.variable_scope

tf.name_scope( tf.compat.v2.name_scopewenn migriert von 1.x to 2.x)

Weitere Informationen zur Migration von 1.x auf 2.x finden Sie in diesem Migrationshandbuch .

Tensorflow-Unterstützung
quelle