import tensorflow as tf
TensorFlow
提供了通過變量名稱來創建或者獲取一個變量的機制,通過這個機制,在不同的函數中可以直接通過變量的名字來使用變量,
而不需要將變量通過參數的形式到處傳遞,避免了參數太多而不好管理的情況出現。此機制通過 tf.variable_scope
和 tf.get_variable
函數實現。
TensorFlow
可通過tf.Variable
來創建變量;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_variable
和 tf.Variable
最大的區別在於指定變量名的參數:
tf.Variable
變量名為可選tf.get_variable
的變量名為必填項,tf.get_variable
會根據這個名字去創建或者獲取變量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
函數,在生成上下文管理器時:
- 將參數
reuse
設置為True
.這樣tf.get_variable
會直接獲取已經聲明的變量,如若變量不存在則會報錯; reuse
為None
或False
時,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_scope
和 tf.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)
當神經網絡結構更加復雜,參數更多時,使用這種變量管理的方式將大大提高程序的可讀性。