ความแตกต่างของขอบเขตชื่อและขอบเขตตัวแปรใน tensorflow คืออะไร?


276

ความแตกต่างระหว่างฟังก์ชั่นเหล่านี้คืออะไร?

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

ส่งคืนตัวจัดการบริบทสำหรับการกำหนด op ที่สร้างตัวแปร ตัวจัดการบริบทนี้ตรวจสอบว่าค่าที่กำหนดมาจากกราฟเดียวกันตรวจสอบให้แน่ใจว่ากราฟนั้นเป็นกราฟเริ่มต้นและผลักขอบเขตขอบเขตชื่อและขอบเขตตัวแปร


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

ส่งคืนตัวจัดการบริบทสำหรับใช้เมื่อกำหนด Python op ตัวจัดการบริบทนี้ตรวจสอบว่าค่าที่กำหนดมาจากกราฟเดียวกันตรวจสอบให้แน่ใจว่ากราฟนั้นเป็นกราฟเริ่มต้นและผลักขอบเขตขอบเขตชื่อ


tf.name_scope(name)

Wrapper สำหรับการGraph.name_scope()ใช้กราฟเริ่มต้น ดูGraph.name_scope()รายละเอียดเพิ่มเติมได้ที่


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

ส่งคืนบริบทสำหรับขอบเขตตัวแปร ขอบเขตตัวแปรช่วยให้สามารถสร้างตัวแปรใหม่และใช้ร่วมกันสร้างขึ้นแล้วในขณะที่ให้การตรวจสอบที่จะไม่สร้างหรือแบ่งปันโดยไม่ได้ตั้งใจ สำหรับรายละเอียดดูขอบเขตตัวแปรวิธีได้ที่นี่เรานำเสนอตัวอย่างพื้นฐานเพียงไม่กี่


สำเนาที่เป็น
nbro

คำตอบ:


377

เริ่มจากการแนะนำสั้น ๆ เกี่ยวกับการแชร์ตัวแปร มันเป็นกลไกTensorFlowที่ช่วยให้การแชร์ตัวแปรเข้าถึงได้ในส่วนต่าง ๆ ของรหัสโดยไม่ต้องผ่านการอ้างอิงกับตัวแปรรอบ ๆ

วิธีการนี้tf.get_variableสามารถใช้กับชื่อของตัวแปรเป็นอาร์กิวเมนต์เพื่อสร้างตัวแปรใหม่ที่มีชื่อดังกล่าวหรือดึงข้อมูลที่สร้างขึ้นมาก่อน สิ่งนี้แตกต่างจากการใช้tf.VariableConstructor ซึ่งจะสร้างตัวแปรใหม่ทุกครั้งที่เรียกว่า (และอาจเพิ่มคำต่อท้ายให้กับชื่อตัวแปรหากมีตัวแปรที่มีชื่อดังกล่าวอยู่แล้ว)

มันมีวัตถุประสงค์เพื่อวัตถุประสงค์ของกลไกการแบ่งปันตัวแปรที่แนะนำประเภทแยกขอบเขต (ขอบเขตตัวแปร) ได้รับการแนะนำ

ด้วยเหตุนี้เราจึงสิ้นสุดการมีขอบเขตสองประเภท:

  • ขอบเขตชื่อสร้างขึ้นโดยใช้tf.name_scope
  • ขอบเขตตัวแปรที่สร้างขึ้นโดยใช้tf.variable_scope

ขอบเขตทั้งสองมีผลเหมือนกันกับการดำเนินการทั้งหมดรวมถึงตัวแปรที่สร้างขึ้นโดยใช้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

UPDATE

ขณะที่รุ่น r0.11, op_scopeและvariable_op_scopeมีทั้งที่เลิกไปและแทนที่ด้วยและname_scopevariable_scope


41
ขอบคุณสำหรับคำอธิบายที่ชัดเจน ธรรมชาติติดตามคำถามจะเป็น " ทำไม Tensorflow จะมีทั้งของกลไกที่คล้ายกันพลุกพล่านเหล่านี้หรือไม่ทำไมไม่แทนที่พวกเขามีเพียงหนึ่งscopeวิธีการที่มีประสิทธิภาพไม่ได้variable_scope?"
จอห์น

8
ฉันไม่คิดว่าฉันเข้าใจแนวความคิดว่าเหตุใดจึงจำเป็นต้องแยกความแตกต่างระหว่างvariable_scopevs name_scopeหากมีการสร้างตัวแปร (ในทางใดทางหนึ่งด้วยtf.Variableหรือtf.get_variable) มันดูเป็นธรรมชาติมากขึ้นสำหรับฉันที่เราควรจะได้รับมันเสมอถ้าเราระบุขอบเขตหรือชื่อเต็มของมัน ฉันไม่เข้าใจว่าทำไมไม่สนใจชื่อขอบเขตในขณะที่อีกชื่อหนึ่งไม่ได้ คุณเข้าใจเหตุผลของพฤติกรรมประหลาดนี้หรือไม่?
Charlie Parker

23
เหตุผลก็คือด้วยขอบเขตตัวแปรหนึ่งสามารถกำหนดขอบเขตที่แยกต่างหากสำหรับตัวแปรที่ใช้งานได้อีกครั้งซึ่งไม่ได้รับผลกระทบจากขอบเขตชื่อปัจจุบันที่ใช้เพื่อกำหนดการดำเนินการ
Andrzej Pronobis

6
สวัสดีคุณสามารถอธิบายได้ว่าทำไมชื่อตัวแปรใน variable_scope จึงลงท้ายด้วย: 0 เสมอ หมายความว่าอาจมีชื่อตัวแปรลงท้ายด้วย: 1,: 2, ฯลฯ ดังนั้นสิ่งนี้จะเกิดขึ้นได้อย่างไร
James Fan

2
@JamesFan "การประกาศ" ทุกครั้งเป็นการดำเนินการดังนั้นเมื่อคุณพูด a = tf.Variable (.. ชื่อ) คุณจะได้รับเทนเซอร์กลับคืนมา แต่มันก็เป็นการสร้างการดำเนินการด้วยเช่นกัน ถ้าคุณพิมพ์ a คุณจะได้เมตริกซ์เป็น a: 0 หากคุณพิมพ์ a.op คุณจะได้รับการดำเนินการที่จะคำนวณค่าเมตริกซ์นั้น
Robert Lugg

84

ทั้งvariable_op_scopeและop_scopeนั้นเลิกใช้แล้วและไม่ควรใช้เลย

เกี่ยวกับอีกสองที่ฉันยังมีปัญหาในการเข้าใจความแตกต่างระหว่างvariable_scopeและname_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 (ตามที่คุณเห็นbอยู่ด้านนอกของscope_nameรูปสี่เหลี่ยมผืนผ้า):


นี่ให้คำตอบคุณ :

ตอนนี้คุณจะเห็นว่าtf.variable_scope()เพิ่มคำนำหน้าให้กับชื่อของตัวแปรทั้งหมด (ไม่ว่าคุณจะสร้างมันขึ้นมาอย่างไร), ops, ค่าคงที่ ในทางตรงกันข้ามtf.name_scope()ละเว้นตัวแปรที่สร้างด้วยtf.get_variable()เพราะมันจะถือว่าคุณรู้ว่าตัวแปรใดและอยู่ในขอบเขตที่คุณต้องการใช้

เอกสารที่ดีเกี่ยวกับตัวแปรการแชร์จะบอกคุณว่า

tf.variable_scope(): การบริหารจัดการ namespaces tf.get_variable()สำหรับชื่อส่งผ่านไปยัง

เอกสารเดียวกันให้รายละเอียดเพิ่มเติมว่าขอบเขตตัวแปรทำงานอย่างไรและเมื่อมีประโยชน์


2
คำตอบที่ยอดเยี่ยมพร้อมตัวอย่างและภาพลองมาดูคำตอบนี้กันเถอะ!
David Parks

43

Namespaces เป็นวิธีการจัดระเบียบชื่อสำหรับตัวแปรและตัวดำเนินการในลักษณะลำดับชั้น (เช่น "scopeA / scopeB / scopeC / op1")

  • tf.name_scope สร้างเนมสเปซสำหรับโอเปอเรเตอร์ในกราฟเริ่มต้น
  • tf.variable_scope สร้างเนมสเปซสำหรับทั้งตัวแปรและโอเปอเรเตอร์ในกราฟเริ่มต้น

  • tf.op_scopeเหมือนกันtf.name_scopeแต่สำหรับกราฟที่สร้างตัวแปรที่ระบุ

  • tf.variable_op_scopeเหมือนกันtf.variable_scopeแต่สำหรับกราฟที่สร้างตัวแปรที่ระบุ

ลิงค์ไปยังแหล่งข้อมูลด้านบนช่วยในการแก้ปัญหาเอกสารนี้

ตัวอย่างนี้แสดงว่าขอบเขตทุกประเภทกำหนดเนมสเปซสำหรับทั้งตัวแปรและโอเปอเรเตอร์ที่มีความแตกต่างดังต่อไปนี้:

  1. ขอบเขตที่กำหนดโดยtf.variable_op_scopeหรือtf.variable_scopeเข้ากันได้กับtf.get_variable(จะละเว้นขอบเขตอื่นสองรายการ)
  2. tf.op_scopeและtf.variable_op_scopeเพียงเลือกกราฟจากรายการตัวแปรที่ระบุเพื่อสร้างขอบเขต นอกเหนือจากพฤติกรรมของพวกเขาเท่ากับtf.name_scopeและtf.variable_scopeตาม
  3. tf.variable_scopeและvariable_op_scopeเพิ่มตัวกำหนดค่าเริ่มต้นที่ระบุหรือค่าเริ่มต้น

สำหรับกราฟที่ตัวแปรที่ระบุถูกสร้างขึ้น? นี่หมายความว่าเช่นตัวอย่างข้างต้นโดย fabrizioM โดยมี tf.variable_op_scope ([a, b], ชื่อ, "mysum2") เป็นขอบเขตพารามิเตอร์ที่นี่และ b ไม่ได้รับผลกระทบจากฟังก์ชันและตัวแปรที่กำหนดในขอบเขตนี้หรือไม่
Xiuyi Yang

คำตอบสำหรับคำถามทั้งสองข้อคือใช่: กราฟที่สร้างตัวแปรที่ระบุและไม่ได้แก้ไข
Alexander Gorban

นี่หมายความว่า tf.name_scope และ tf.variable_scope จะใช้ในกราฟเริ่มต้นเท่านั้น แต่เมื่อคุณเห็นได้ชัดและกำหนดกราฟที่ใช้ tf.Graph () อีกสองฟังก์ชัน tf.op_scope และ tf.variable_op_scope นั้นไม่สามารถใช้ใน กราฟนี้!
Xiuyi Yang

12

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เพียงคุณหลีกเลี่ยงอาการปวดหัวบางอย่างเนื่องจากความไม่ลงรอยกันแบบนี้


9

สำหรับ API r0.11, op_scopeและvariable_op_scopeมีทั้งที่เลิกใช้งานแล้ว name_scopeและvariable_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'

8

คุณสามารถคิดว่าพวกเขาเป็นสองกลุ่ม: variable_op_scopeและop_scopeใช้ชุดของตัวแปรเป็นอินพุตและถูกออกแบบมาเพื่อสร้างการดำเนินงาน ความแตกต่างคือสิ่งที่พวกเขาส่งผลกระทบต่อการสร้างตัวแปรด้วย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

สังเกตชื่อของตัวแปรvในสองตัวอย่าง

เหมือนกันสำหรับtf.name_scopeและ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

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับขอบเขตตัวแปรในการกวดวิชา คำถามที่คล้ายกันถูกถามก่อนใน Stack Overflow


2

จากส่วนสุดท้ายของหน้านี้ของเอกสารเทนเซอร์โฟลว์: ชื่อของ opstf.variable_scope()

[ ... ] เมื่อเราทำนี้โดยปริยายเปิด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


2

Tensorflow 2.0 คำตอบที่เข้ากันได้ : คำอธิบายของAndrzej Pronobisและมีรายละเอียดมากเกี่ยวกับฟังก์ชั่นที่เกี่ยวข้องกับSalvador DaliScope

ฟังก์ชั่นของขอบเขตที่กล่าวข้างต้นซึ่งมีการใช้งาน ณ ขณะนี้ (17 กุมภาพันธ์ 2020) เป็นและvariable_scopename_scope

การระบุการเรียกใช้ที่เข้ากันได้ 2.0 สำหรับฟังก์ชั่นเหล่านั้นเราได้กล่าวถึงข้างต้นเพื่อประโยชน์ของชุมชน

ฟังก์ชั่นใน 1.x :

tf.variable_scope

tf.name_scope

ฟังก์ชั่นที่เกี่ยวข้องใน 2.x :

tf.compat.v1.variable_scope

tf.name_scope( tf.compat.v2.name_scopeถ้าย้ายจาก1.x to 2.x)

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการย้ายข้อมูลจาก 1.x ถึง 2.x โปรดดูคู่มือการย้ายข้อมูลนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.