變量管理


import tensorflow as tf

TensorFlow提供了通過變量名稱來創建或者獲取一個變量的機制,通過這個機制,在不同的函數中可以直接通過變量的名字來使用變量,
而不需要將變量通過參數的形式到處傳遞,避免了參數太多而不好管理的情況出現。此機制通過 tf.variable_scopetf.get_variable 函數實現。

  1. TensorFlow 可通過 tf.Variable 來創建變量;
  2. tf.get_variable 函數可創建(創建的功能基本等價於tf.Variable)或者獲取一個變量。
# 下面的兩個定義是等價的
v1 = tf.Variable(tf.constant(1., shape = [1]), name= 'v1')
v2 = tf.get_variable('v2', shape=[1], initializer=tf.constant_initializer(1.))

TensorFlow 中的變量初始化函數

初始化函數 功能 主要參數
tf.constant_initializer 將變量初始化為給定常數 常數的取值
tf.random_normal_initializer 將變量初始化為滿足正態分布的隨機值 正態分布的均值和標准差
tf.truncated_normal_initializer 將變量初始化為滿足正態分布的隨機值,但若隨機值偏離平均值超過2個標准差,則這個數會被重新隨機 正態分布的均值和標准差
tf.random_uniform_initializer 將變量初始化為滿足平均分布的隨機值 最大、最小值
tf.uniform_unit_scaling_initializer 將變量初始化為滿足平均分布但不影響輸出數量級的隨機值 factor(產生隨機值時乘以的系數)
tf.zeros_initializer 將變量初始化為全0 變量維度
tf.ones_initializer 將變量初始化為全1 變量維度

更多參考:TensorFlow常用的函數

g1 = tf.Graph()
with g1.as_default():
    v = tf.get_variable('v', shape = [1], initializer=tf.zeros_initializer())

tf.get_variabletf.Variable 最大的區別在於指定變量名的參數:

  1. tf.Variable 變量名為可選
  2. tf.get_variable 的變量名為必填項,tf.get_variable會根據這個名字去創建或者獲取變量
  3. tf.get_variable首先會試圖去創建一個名字為v的參數,若創建失敗(比如已經有了同名的參數),則程序會報錯。這是為了避免無意識的變量復用造成的錯誤。

1. 在上下文管理器 foo 中創建變量 v

如果需要通過tf.get_variable獲取一個已經創建的變量,則需要通過tf.variable_scope來生成一個上下文管理器,並明確指定在這個上下文管理器中tf.get_variable將直接獲取已經生成的變量。

# 在名字為foo的命名空間內創建名字為v的變量
with tf.variable_scope("foo"):
    v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))

因為在命名空間foo中已經存在名字為v的變量,所以下面的程序會報錯:ValueError

with tf.variable_scope("foo"):
    v = tf.get_variable("v", [1])

在生成上下文管理器時,將參數reuse設置為True.這樣tf.get_variable會直接獲取已經聲明的變量。

with tf.variable_scope("foo", reuse=True):
    v1 = tf.get_variable("v", [1])
    print(v == v1) # 輸出為 `True`代表v, v1 是相同的變量
True

將參數reuse設置為True時,tf.get_variable 將只能獲取已經創建過的變量。
由於在命名空間bar中還沒有創建名字為v的變量,所以下面的程序會報錯

with tf.variable_scope("bar", reuse=True):
    v = tf.get_variable("v", [1])

總結
tf.variable_scope函數,在生成上下文管理器時:

  1. 將參數reuse設置為True.這樣tf.get_variable會直接獲取已經聲明的變量,如若變量不存在則會報錯;
  2. reuseNoneFalse 時,tf.get_variable 會創建新的變量,如若同名的變量已經存在,則會報錯。

2. 嵌套上下文管理器中reuse參數的使用。

with tf.variable_scope("root"):
    # 可以通過 `tf.get_variable_scope().reuse` 函數來獲取當前上下文管理器中的 `reuse`參數的取值。
    print(tf.get_variable_scope().reuse)  # 輸出 `False`,即最外層的`reuse`為 `False`
    
    with tf.variable_scope("foo", reuse=True):   # 新建一個嵌套的上下文管理器,
                                                    # 並指定 reuse=True
        print(tf.get_variable_scope().reuse)        # 輸出 `True`
        
        with tf.variable_scope("bar"):            # 新建一個嵌套的上下文管理器,但不指定 reuse,這時 reuse 的
                                                # 的取值會和外面一層保持一致
            print(tf.get_variable_scope().reuse)  # 輸出 `True`
            
    print(tf.get_variable_scope().reuse)        # 輸出 `False`。
                                                # 退出 reuse 設置為 True的上下文之后,reuse 的值又回到了 False
False
True
True
False

3. 通過variable_scope來管理變量

tf.variable_scope函數生成的上下文管理器也會創建一個TensoFlow中的命名空間,在命名空間內創建的變量名稱都會帶上這個命名空間名作為前綴。所以 tf.variable_scope函數除了可以控制 tf.get_variable 執行的功能外,也提供了一個管理變量命名空間的方式。

v1 = tf.get_variable("u", [1])   
print(v1.name)            # 輸出 `u:0`。 `u` 變量的名稱,`:0` 表示這個變量是生成變量這個運算的第一個結果。

with tf.variable_scope("foo"):
    v2 = tf.get_variable("u", [1])
    print(v2.name)                  # 在 `tf.variable_scope` 中創建的變量,名稱前面會加入命名空間的名稱,
                                     # 並通過 `/`來分隔命名空間名稱和變量名稱
u:0
foo/u:0
with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v3 = tf.get_variable("v", [1])
        print (v3.name)           # 命名空間可以嵌套,同時變量的名稱也會加入所有的命名空間的名稱作為前綴
        
    v4 = tf.get_variable("v1", [1])   # 當命名空間退出后,變量名稱也就不會被加入其前綴了
    print( v4.name)
foo/bar/v:0
foo/v1:0

4. 我們可以通過變量的名稱來獲取變量。

  • 創建一個名稱為空的命名空間,並設置 reuse=True
with tf.variable_scope("",reuse=True): 
    # 可以直接通過帶命名空間名稱的變量名來獲取其他命名空間下的變量
    v5 = tf.get_variable("foo/bar/v", [1])   # 指定名稱 "foo/bar/v" 來獲取在命名空間 "foo/bar" 中創建的變量
    print(v5 == v3)                          # 輸出 `True`
    v6 = tf.get_variable("foo/v1", [1])     
    print (v6 == v4)                        # 輸出 `True`
True
True

通過 tf.variable_scopetf.geT_variable 可對前向傳播進行改進

def inference(input_tensor, reuse = False):
    # 定義第一層神經網絡的變量和前向傳播結果
    with tf.variable_scope('layer1', reuse = reuse):
        # 根據傳進來的`reuse`來判斷是創建新的變量還是使用已經創建好了的。
        # 在第一次構造網絡時需要創建新的變量,以后每次調用這個函數都直接使用 `reuse = True`就不需要每次將變量傳進來了
        weights = tf.get_variable('weights', [INPUT_NODE, LAYER1_NODE], initializer = tf.truncated_normal_initializer(stddev=0.1))
        biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0))
        layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)
    
    # 類似地定義第二層神經網絡的變量和前向傳播結果
    with tf.variable_scope('layer2'):
        weights = get_weight_variable([LAYER1_NODE, OUTPUT_NODE], regularizer)
        biases = tf.get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
        layer2 = tf.matmul(layer1, weights) + biases
    # 返回最后的前向傳播結果
    return layer2

INPUT_NODE = 100
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y = inference(x)

在程序中需要使用訓練好的神經網絡進行推導時,可以直接調用 inference(new_x, True);
如果需要使用滑動平均模型,只需要將計算滑動平均的類傳到函數中即可,獲取或者創建變量的部分不需要改變。

new_x = ...
new_y = inference(new_x, True)

當神經網絡結構更加復雜,參數更多時,使用這種變量管理的方式將大大提高程序的可讀性。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM