Tensorflow中的name_scope和variable_scope

Tensorflow是一個編程模型,幾乎成爲了一種編程語言(裏面有變量、有操做......)。 Tensorflow編程分爲兩個階段:構圖階段+運行時。 Tensorflow構圖階段其實就是在對圖進行一些描述性語言,跟html很像,很適合用標記性語言來描述。 Tensorflow是有向圖,是一個有向無環圖。張量爲邊,操做爲點,數據在圖中流動。 Tensorflow爲每一個結點都起了惟一的一個名字。html

import tensorflow as tf

a = tf.constant(3)  # name=Const:0
b = tf.Variable(4)  # name=Variable:0
print(a.name, b.name)

如上所示,即使你沒有指明變量的name屬性,tensorflow也會給它起個默認名字。python

在C++中有namespace的概念,命名空間的好處就是減小了命名衝突,咱們能夠在命名空間中使用較簡易的標識符。爲了便於用戶定義變量的name,tensorflow也提出了name_scope編程

with tf.name_scope("my"):
    a = tf.constant(3)  # my/Const:0
    b = tf.add(a, b)  # my/Add:0
    print(a.name, b.name)
    # 使用get_variable卻無論用
    c = tf.get_variable("c", shape=1, dtype=tf.int32, initializer=tf.constant_initializer(2))  # c:0
    print(c.name)

如上所示,在name_scope中的屬性,會用相似文件路徑的方式來定義變量的name屬性 可是關於name_scope須要明白兩點:編程語言

  • 使用tf.get_variable函數建立的變量不會受name_scope的影響
  • 只有新建立的變量,如tf.constant,tf.Variable等新建結點的操做纔會受到name_scope的影響

總而言之,name_scope做用比較單一,僅僅是爲了更改變量的name屬性,便於命名變量。而variable_scope做用就很豐富了,它不只可以改變變量的name屬性,還可以實現變量管理功能。函數

with tf.variable_scope("ha"):
    a = tf.constant(2, name="myconstant")  # ha/myconstant:0
    b = tf.get_variable("b", shape=1, dtype=tf.int32, initializer=tf.constant_initializer(2))  # ha/b:0
    print(a.name, b.name)

在改變變量name屬性這方面,variable_scope和name_scope基本沒有差異,惟一的區別就是get_variable不會受到name_scope的影響,卻會受到variable_scope的影響。測試

variable_scope更加劇要的功能是實現變量管理,其中最突出的一點就是變量共享spa

# 下面咱們來驗證一下變量共享機制
def get_share_variable(reuse):
    with tf.variable_scope("share", reuse=reuse):
        a = tf.get_variable("a", shape=(1), dtype=tf.int32)
        return a


one = get_share_variable(False)
two = get_share_variable(True)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run([one, two, tf.assign(one, [2])]))

再上面的例子中,經過reuse屬性能夠控制variable_scope中的變量是否須要從新建立,若是指定reuse=false,則必然會執行新建變量的操做。 上面代碼one和two輸出值都變成了2,這說明它倆引用的是同一個對象。 使用variable_scope實現變量共享須要注意如下幾點:設計

  • 重用以前必須保證已經建立過,不然報錯
  • 使用時必須指明shape和dtype,不然沒法複用已建立的變量

變量共享機制很是重要。一個很是經常使用的場景就是:訓練完成以後保存模型,加載模型以後整個圖已經創建好了,這時就須要經過variable_scope機制複用已經建好的圖,而後測試。code

爲了驗證以上兩點,請看下例:htm

複用不曾建立過的變量會報錯

with tf.variable_scope("ha", default_name="what", reuse=True):
    try:
        m = tf.get_variable("m")
    except Exception as ex:
        print(ex)  # Variable ha/m does not exist, or was not created with tf.get_variable(). Did you mean to set reuse=tf.AUTO_REUSE in VarScope?

它建議咱們使用reuse=tf.AUTO_REUSE,這個屬性值的含義表示:若是變量存在則複用,不存在則建立之。

之前,reuse屬性的取值爲True和False,沒有tf.AUTO_REUSE。可是在tensorflow將來的版本中,有可能會把reuse的取值弄成枚舉類型。這就略微有點小坑了,不知AUTO_REUSE的使用場景是否對得起這麼不優雅的設計。

  • AUTO_REUSE=1
  • REUSE_FALSE = 2
  • REUSE_TRUE = 3

下面驗證第二點,必須指明變量的形狀和類型才能夠複用

with tf.variable_scope("ha", default_name="what", reuse=tf.AUTO_REUSE):
    try:
        m = tf.get_variable("m")
        print(m.name)
    except Exception as ex:
        print(ex)  # ValueError: Shape of a new variable (ha/m) must be fully defined, but instead was <unknown>.  這個錯誤是在說:建立變量必須指明變量的類型

variable_scope的主要做用是變量共享。 說到變量共享,不得不說tf.get_variable()函數。tf.Variable()是構造函數,必定會建立新的變量。tf.get_variable則能夠直接訪問已經建立的變量(只能在variable_scope中且reuse=True或者AUTO_REUSE的狀況下),也能夠建立新的變量(在variable_scope內部或者外面均可以,如果內部,則必須reuse=FALSE或者AUTO_REUSE)。

經過以上過程能夠發現,get_variable跟variable_scope很是合得來。get_variable不必定非在variable_scope中使用,可是不在variable_scope中使用,它就只能用來建立變量而沒法複用變量了,由於只有variable_scope才擁有reuse屬性。

# reuse變量的惟一方式就是使用name_scope
x = tf.Variable(3, False, name='x')
print(x.name, x.shape)  # x:0 ()
y = tf.get_variable("x", shape=x.shape)
print(y.name, y.shape)  # x_1:0 ()

使用get_variable時也有一些微操做,好比指定trainable屬性指明該變量是否能夠訓練。

with tf.variable_scope("ha", default_name="what", reuse=tf.AUTO_REUSE):
    # 當variable_scope reuse變量時,依舊能夠對變量進行一些微操做:設置trainable=False,表示這個節點不可訓練
    # 當獲取變量時,dtype類型必須對應正確
    a = tf.get_variable("b", trainable=False, dtype=tf.int32)
    print(tf.trainable_variables("ha"))

相比name_scope功能的單一,tensorflow對variable_scope玩了不少花樣:

  • tf.get_trainable_variables() 獲取所有可訓練的變量
  • tf.get_variable_scope()獲取當前的變量做用域

爲了對比說明問題,下面用嵌套的方式來實現做用域。

with tf.variable_scope("one"):
    """
    使用name_scope只會影響變量的名字,它要解決的問題是變量重名問題
    """
    with tf.name_scope("two"):
        x = tf.constant(3)  # one/two/Const:0
        print(x.name)
        # variable_scope.name是根目錄的名字
        print(tf.get_variable_scope().name, tf.get_variable_scope().original_name_scope)  # 輸出爲:one    one/
    with tf.variable_scope("three"):
        x = tf.constant(3)  # one/three/Const:0
        print(x.name)
        print(tf.get_variable_scope().name, tf.get_variable_scope().original_name_scope)  # 輸出爲one/three  one/three/

可見,name_scope對於tf.get_variable_scope()來講幾乎是不可見的,但卻會對變量的命名產生影響,但卻僅僅對變量名產生影響。

函數不會阻隔with做用域

# 使用函數依舊不會影響做用域
def ha():
    x = tf.Variable(3)  # ha_3/Variable:0
    print(x.name)


with tf.variable_scope("ha"):  # 這個變量做用域已經定義過好幾回了,它的實際名字變成了ha_3
    ha()

Tensorflow中每一個結點的name都不會重複,若是重複了怎麼辦?

# 若是重複定義變量
a = tf.constant(2, name='a')  # a:0
b = tf.constant(2, name='a')  # a_1:0
print(a.name, b.name)

# 若是重複定義name_scope
with tf.name_scope("my"):
    a = tf.constant(2)  # my_1/Const:0
    print(a.name)
"""
可見tensorflow對於一切重名的東西都會在末尾加上下劃線+數字
"""
with tf.name_scope("my_4"):
    a = tf.constant(2)
    print(a.name)  # my_4/Const:0
with tf.name_scope("my"):
    a = tf.constant(2)
    print(a.name)  # my_2/Const:0
with tf.name_scope("my_4"):
    a = tf.constant(2)  # my_4_1/Const:0
    print(a.name)

經過以上例子能夠發現,tensorflow對於命名重複問題使用如下規則解決: 一、要使用的name不存在,能夠直接使用 二、要使用的name已經存在,執行下列循環:

i=1
while 1:
    now_name="%s_%d"%(name,i)
    if exists(now_name):
       i+=1
    else:
        return now_name
相關文章
相關標籤/搜索