web-dev-qa-db-ja.com

テンソルフローにおける名前スコープと変数スコープの違いは何ですか?

これらの機能の違いは何ですか?

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

変数を作成する操作を定義するためのコンテキストマネージャを返します。このコンテキストマネージャは、与えられた値が同じグラフからのものであることを検証し、そのグラフがデフォルトグラフであることを確認し、そして名前スコープと変数スコープをプッシュします。


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

Pythonの操作を定義するときに使用するコンテキストマネージャを返します。このコンテキストマネージャは、与えられた値が同じグラフからのものであることを検証し、そのグラフがデフォルトのグラフであることを確認し、そして名前スコープをプッシュします。


tf.name_scope(name)

デフォルトのグラフを使ったGraph.name_scope()のラッパー。詳しくはGraph.name_scope()をご覧ください。


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

変数スコープのコンテキストを返します。変数スコープは、新しい変数を作成し、すでに作成されたものを共有しながら、偶然に作成または共有しないようにチェックを提供します。詳細については、可変範囲の操作方法を参照してください。ここでは、基本的な例をいくつか紹介します。

233
user2886263

変数共有の簡単な紹介から始めましょう。これはTensorFlow内のメカニズムであり、変数への参照を渡すことなくコードのさまざまな部分でアクセスされた変数を共有することを可能にします。

メソッド tf.get_variable を引数として変数の名前と共に使用して、そのような名前で新しい変数を作成するか、以前に作成されたものを取得することができます。これは、呼び出されるたびに新しい変数を作成する tf.Variable コンストラクタを使用するのとは異なります(そのような名前の変数がすでに存在する場合は、変数名にサフィックスを追加することもできます)。

変数共有メカニズムの目的のために、別のタイプのスコープ(変数スコープ)が導入されました。

結果として、2つの異なる種類のスコープを持つことになります。

両方のスコープは、すべての操作とtf.Variableを使用して作成された変数に同じ効果を持ちます。つまり、スコープは操作または変数名の接頭辞として追加されます。

ただし、名前の有効範囲はtf.get_variableでは無視されます。次の例でそれがわかります。

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

スコープ内でtf.get_variableを使用してアクセスされた変数を配置する唯一の方法は、次の例のように変数スコープを使用することです。

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

これにより、異なる名前スコープ内であっても、プログラムのさまざまな部分で変数を簡単に共有できます。

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

更新

バージョンr0.11の時点で、op_scopevariable_op_scopeはどちらも deprecated であり、name_scopevariable_scopeに置き換えられています。

332

variable_op_scopeop_scope の両方は現在推奨されていないため、まったく使用しないでください。

他の2つに関しては、簡単な例を作成してすべてを視覚化する前に、 variable_scopename_scope の違いを理解するのに問題がありました(それらはほぼ同じに見えました)。

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

ここでは、いくつかの変数と定数を作成し、それらをスコープ内にグループ化する関数を作成します(提供したタイプによって異なります)。この関数では、すべての変数の名前も表示します。その後、グラフを実行して結果の値を取得し、イベントファイルを保存してTensorBoardでそれらを調べます。これを実行すると、次のようになります。

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 

TensorBoardを開くと、同様のパターンが表示されます(bscope_nameの矩形の外側にあるように見えます): enter image description here


これはあなたに答えを与える

これで、tf.variable_scope()は(変数の作成方法に関係なく)すべての変数、ops、定数の名前に接頭辞を追加することがわかりました。一方、tf.name_scope()は、tf.get_variable()で作成された変数を無視します。

変数の共有 に関する優れたドキュメントは、

tf.variable_scope()tf.get_variable()に渡される名前の名前空間を管理します。

同じドキュメントに、変数スコープの機能の詳細と有用性に関する詳細が記載されています。

56
Salvador Dali

名前空間は、変数と演算子の名前を階層的に整理する方法です(例: "scopeA/scopeB/scopeC/op1")。

  • tf.name_scope はデフォルトグラフの演算子の名前空間を作成します。
  • tf.variable_scope はデフォルトグラフの変数と演算子の両方の名前空間を作成します。

  • tf.op_scopetf.name_scopeと同じですが、指定された変数が作成されたグラフ用です。

  • tf.variable_op_scopetf.variable_scopeと同じですが、指定された変数が作成されたグラフ用です。

上記の資料へのリンクは、このドキュメントの問題を明確にするのに役立ちます。

この例 は、すべての種類のスコープが、変数と演算子の両方に対して名前空間を定義していることを示しています。

  1. tf.variable_op_scopeまたはtf.variable_scopeで定義されたスコープはtf.get_variableと互換性があります(他の2つのスコープは無視されます)。
  2. tf.op_scopetf.variable_op_scopeはスコープを作成するために指定された変数のリストからグラフを選択するだけです。それ以外の動作はtf.name_scopeおよびtf.variable_scopeと同等です
  3. tf.variable_scopevariable_op_scopeは、指定された、またはデフォルトの初期化子を追加します。
42

API r0.11に関しては、op_scopevariable_op_scopeは両方とも 非推奨 です。 name_scopevariable_scopeは入れ子にすることができます。

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

variable_op_scopeop_scopeは、入力として変数のセットを受け取り、操作を作成するように設計されている2つのグループとして考えることができます。違いは、それらが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

2つの例の変数vの名前に注目してください。

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

tutorial で変数スコープについてもっと読むことができます。 以前に尋ねられた スタックオーバーフローについての同様の質問。

7
fabrizioM

簡単にしましょう:tf.variable_scopeを使うだけです。 TF開発者の名言、

現在、内部コードとライブラリを除いて、誰もがvariable_scopeを使用し、name_scopeを使用しないことをお勧めします。

variable_scopeの機能が基本的にname_scopeの機能を拡張しているという事実に加えて、それらがどのように一緒にそれほどうまく遊ばないのかを考えてください:

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

variable_scopeだけに固執することによって、あなたはこの種の非互換性のためにいくつかの頭痛を避けます。

6
P-Gn

テンソルフロードキュメントのこのページの最後のセクションから: tf.variable_scope()内のopsの名前

[...] with tf.variable_scope("name")を実行すると、暗黙のうちにtf.name_scope("name")が開きます。例えば:

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

変数スコープに加えて名前スコープを開くことができます。その場合、それらはopsの名前にのみ影響し、変数の名前には影響しません。

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"

文字列ではなくキャプチャされたオブジェクトを使用して変数のスコープを開くときに、opsの現在の名前のスコープを変更しません。