Sự khác biệt của phạm vi tên và phạm vi biến trong dòng chảy là gì?


276

Sự khác biệt giữa các chức năng này là gì?

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

Trả về một trình quản lý bối cảnh để xác định một op tạo các biến. Trình quản lý bối cảnh này xác nhận rằng các giá trị đã cho là từ cùng một biểu đồ, đảm bảo rằng biểu đồ đó là biểu đồ mặc định và đẩy một phạm vi tên và phạm vi biến.


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

Trả về trình quản lý bối cảnh để sử dụng khi xác định Python op. Trình quản lý bối cảnh này xác nhận rằng các giá trị đã cho là từ cùng một biểu đồ, đảm bảo rằng biểu đồ đó là biểu đồ mặc định và đẩy phạm vi tên.


tf.name_scope(name)

Wrapper để Graph.name_scope()sử dụng biểu đồ mặc định. Xem Graph.name_scope()để biết thêm chi tiết.


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

Trả về một bối cảnh cho phạm vi biến. Phạm vi biến cho phép tạo các biến mới và chia sẻ các biến đã được tạo trong khi cung cấp séc để không tạo hoặc chia sẻ một cách tình cờ. Để biết chi tiết, hãy xem Cách biến phạm vi, ở đây chúng tôi chỉ trình bày một vài ví dụ cơ bản.


Câu trả lời:


377

Hãy bắt đầu bằng một giới thiệu ngắn về chia sẻ biến. Đó là một cơ chế TensorFlowcho phép chia sẻ các biến được truy cập trong các phần khác nhau của mã mà không chuyển các tham chiếu đến biến xung quanh.

Phương thức tf.get_variablecó thể được sử dụng với tên của biến làm đối số để tạo một biến mới có tên đó hoặc truy xuất biến đã được tạo trước đó. Điều này khác với việc sử dụng hàm tf.Variabletạo sẽ tạo một biến mới mỗi khi nó được gọi (và có khả năng thêm hậu tố vào tên biến nếu một biến có tên đó đã tồn tại).

Với mục đích của cơ chế chia sẻ biến, một loại phạm vi riêng biệt (phạm vi biến) đã được giới thiệu.

Kết quả là, cuối cùng chúng ta có hai loại phạm vi khác nhau:

Cả hai phạm vi đều có tác dụng như nhau đối với tất cả các hoạt động cũng như các biến được tạo bằng cách sử dụng tf.Variable, nghĩa là phạm vi sẽ được thêm làm tiền tố cho tên hoạt động hoặc tên biến.

Tuy nhiên, phạm vi tên được bỏ qua bởi tf.get_variable. Chúng ta có thể thấy rằng trong ví dụ sau:

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

Cách duy nhất để đặt một biến được truy cập bằng cách sử dụng tf.get_variabletrong một phạm vi là sử dụng một phạm vi biến, như trong ví dụ sau:

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

Điều này cho phép chúng tôi dễ dàng chia sẻ các biến trên các phần khác nhau của chương trình, ngay cả trong phạm vi tên khác nhau:

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

CẬP NHẬT

Kể từ phiên bản r0.11, op_scopevariable_op_scopecả hai đều không dùng nữa và được thay thế bởi name_scopevariable_scope.


41
Cảm ơn đã giải thích rõ ràng. Đương nhiên, một câu hỏi tiếp theo sẽ là " Tại sao Tensorflow có cả hai cơ chế tương tự khó hiểu này? Tại sao không thay thế chúng chỉ bằng một scopephương pháp có hiệu quả variable_scope?"
John

8
Tôi không nghĩ rằng tôi hiểu khái niệm tại sao sự khác biệt giữa variable_scopevs name_scopethậm chí là cần thiết. Nếu người ta tạo một biến (theo bất kỳ cách nào với tf.Variablehoặc tf.get_variable), đối với tôi, chúng ta sẽ luôn có thể có được nó nếu chúng ta chỉ định phạm vi hoặc tên đầy đủ của nó. Tôi không hiểu tại sao người ta bỏ qua điều tên phạm vi trong khi người kia thì không. Bạn có hiểu lý do cho hành vi kỳ lạ này?
Charlie Parker

23
Lý do là với phạm vi biến, người ta có thể xác định phạm vi riêng cho các biến có thể sử dụng lại mà không bị ảnh hưởng bởi phạm vi tên hiện tại được sử dụng để xác định các hoạt động.
Andrzej Pronobis

6
Xin chào bạn có thể giải thích tại sao tên biến trong biến_scope luôn kết thúc bằng a: 0 không? Điều này có nghĩa là có thể có các tên biến kết thúc bằng: 1 ,: 2, v.v., vậy làm thế nào điều này có thể xảy ra?
James Fan

2
@JamesFan Mỗi "khai báo" là một hoạt động, vì vậy khi bạn nói a = tf.Variable (.. name), bạn sẽ nhận lại được một tenxơ, nhưng nó thực sự cũng tạo ra một hoạt động. nếu bạn in a, bạn sẽ nhận được tenxơ với a: 0. Nếu bạn in a.op, bạn sẽ có được thao tác sẽ tính giá trị tenxơ đó.
Robert Lugg

84

Cả hai variable_op_scopeop_scope hiện đang chấp nhận và không nên được sử dụng ở tất cả.

Về hai người kia, tôi cũng đã có vấn đề tìm hiểu sự khác biệt giữa variable_scopename_scope (họ nhìn gần như giống nhau) trước khi tôi cố gắng hình dung tất cả mọi thứ bằng cách tạo ra một ví dụ đơn giản:

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

Ở đây tôi tạo một hàm tạo một số biến và hằng và nhóm chúng trong phạm vi (tùy thuộc vào loại tôi cung cấp). Trong hàm này, tôi cũng in tên của tất cả các biến. Sau đó, tôi thực hiện biểu đồ để lấy các giá trị của các giá trị kết quả và lưu các tệp sự kiện để điều tra chúng trong TensorBoard. Nếu bạn chạy cái này, bạn sẽ nhận được những điều sau đây:

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 

Bạn sẽ thấy mô hình tương tự nếu bạn mở TensorBoard (như bạn thấy bnằm ngoài scope_namehình chữ nhật):


Điều này cho bạn câu trả lời :

Bây giờ bạn thấy rằng tf.variable_scope()thêm một tiền tố vào tên của tất cả các biến (bất kể bạn tạo chúng như thế nào), ops, hằng số. Mặt khác, tf.name_scope()bỏ qua các biến được tạo tf.get_variable()bởi vì nó giả định rằng bạn biết biến nào và trong phạm vi bạn muốn sử dụng.

Một tài liệu tốt về các biến chia sẻ cho bạn biết rằng

tf.variable_scope(): Quản lý không gian tên cho tên được chuyển đến tf.get_variable().

Tài liệu tương tự cung cấp thêm chi tiết về cách hoạt động của Phạm vi biến và khi nào nó hữu ích.


2
Câu trả lời tuyệt vời với ví dụ và hình ảnh, hãy để câu trả lời này được mọi người ủng hộ!
David

43

Không gian tên là một cách để tổ chức tên cho các biến và toán tử theo cách phân cấp (ví dụ: "scopeA / scopeB / scopeC / op1")

  • tf.name_scope tạo không gian tên cho các toán tử trong biểu đồ mặc định.
  • tf.variable_scope tạo không gian tên cho cả biến và toán tử trong biểu đồ mặc định.

  • tf.op_scopetương tự tf.name_scope, nhưng đối với biểu đồ trong đó các biến được chỉ định đã được tạo.

  • tf.variable_op_scopetương tự tf.variable_scope, nhưng đối với biểu đồ trong đó các biến được chỉ định đã được tạo.

Liên kết đến các nguồn trên giúp phân biệt vấn đề tài liệu này.

Ví dụ này cho thấy rằng tất cả các loại phạm vi xác định không gian tên cho cả hai biến và toán tử với các khác biệt sau:

  1. phạm vi được xác định bởi tf.variable_op_scopehoặc tf.variable_scopetương thích với tf.get_variable(nó bỏ qua hai phạm vi khác)
  2. tf.op_scopetf.variable_op_scopechỉ chọn một biểu đồ từ danh sách các biến được chỉ định để tạo phạm vi cho. Khác với hành vi của họ bằng tf.name_scopetf.variable_scopetheo đó
  3. tf.variable_scopevariable_op_scopethêm trình khởi tạo được chỉ định hoặc mặc định.

Đối với biểu đồ trong đó các biến được chỉ định đã được tạo? Điều này có nghĩa là ví dụ trên của fabrizioM, với tf.variable_op_scope ([a, b], name, "mysum2") là phạm vi, ở đây tham số a và b không bị ảnh hưởng bởi hàm này và các biến được xác định trong phạm vi này có bị ảnh hưởng không?
Xiuyi Yang

Câu trả lời cho cả hai câu hỏi là có: biểu đồ trong đó các biến được chỉ định đã được tạo và chúng không được sửa đổi.
Alexander Gorban

Điều này có nghĩa là tf.name_scope và tf.variable_scope chỉ được sử dụng trong biểu đồ mặc định, nhưng khi bạn xác định rõ ràng và tạo ra một biểu đồ bằng tf.Graph (), hai hàm khác tf.op_scope và tf.variable_op_scope không thể được sử dụng biểu đồ này!
Xiuyi Yang

12

Hãy làm cho nó đơn giản: chỉ cần sử dụng tf.variable_scope. Trích dẫn một nhà phát triển TF , :

Hiện tại, chúng tôi khuyên mọi người nên sử dụng variable_scopevà không sử dụng name_scopengoại trừ mã nội bộ và thư viện.

Bên cạnh thực tế variable_scopelà chức năng của về cơ bản mở rộng những chức năng đó name_scope, hãy xem xét cách chúng không chơi tốt với nhau:

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

Bằng cách chỉ dính vào variable_scopebạn sẽ tránh được một số đau đầu do loại không tương thích này.


9

Đối với API r0.11 op_scopevariable_op_scopecả hai đều không dùng nữa . name_scopevariable_scopecó thể được lồng nhau:

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

Bạn có thể nghĩ chúng là hai nhóm: variable_op_scopeop_scopelấy một bộ biến làm đầu vào và được thiết kế để tạo ra các hoạt động. Sự khác biệt là cách chúng ảnh hưởng đến việc tạo các biến với 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

chú ý tên của biến vtrong hai ví dụ

tương tự cho 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

Bạn có thể đọc thêm về phạm vi biến trong hướng dẫn . Một câu hỏi tương tự đã được hỏi trước đây trên Stack Overflow.


2

Từ phần cuối của trang này của tài liệu tenorflow: Tên của ops trongtf.variable_scope()

[...] Khi chúng tôi làm with tf.variable_scope("name"), điều này ngầm mở ra một tf.name_scope("name"). Ví dụ:

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

Phạm vi tên có thể được mở ra ngoài một phạm vi biến, và sau đó chúng sẽ chỉ ảnh hưởng đến tên của các op chứ không ảnh hưởng đến các biến.

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"

Khi mở một phạm vi biến bằng cách sử dụng một đối tượng bị bắt thay vì một chuỗi, chúng tôi không thay đổi phạm vi tên hiện tại cho ops.


2

Câu trả lời tương thích Tensorflow 2.0 : Các giải thích Andrzej PronobisSalvador Dalirất chi tiết về các Hàm liên quan đến Scope.

Trong số các Hàm phạm vi được thảo luận ở trên, hiện đang hoạt động (ngày 17 tháng 2 năm 2020) variable_scopename_scope.

Chỉ định các cuộc gọi tương thích 2.0 cho các chức năng đó, chúng tôi đã thảo luận ở trên, vì lợi ích của cộng đồng.

Chức năng trong 1.x :

tf.variable_scope

tf.name_scope

Chức năng tương ứng trong 2.x :

tf.compat.v1.variable_scope

tf.name_scope( tf.compat.v2.name_scopenếu di chuyển từ 1.x to 2.x)

Để biết thêm thông tin về di chuyển từ 1.x đến 2.x, vui lòng tham khảo Hướng dẫn di chuyển này .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.