name_scope: 爲了更好地管理變量的命名空間而提出的。比如在 tensorboard 中,因爲引入了 name_scope, 我們的 Graph 看起來才井然有序。
variable_scope: 大部分情況下,跟 tf.get_variable() 配合使用,實現變量共享的功能。with tf.variable_scope('scopename', reuse=True): func() #函數調用,則函數func內部定義的變量空間名 在前面也會加上scopename的空間。
Note: 要確立一種 Graph 的思想。在 TensorFlow 中,我們定義一個變量,相當於往 Graph 中添加了一個節點。和普通的 python 函數不一樣,在一般的函數中,我們對輸入進行處理,然後返回一個結果,而函數裏邊定義的一些局部變量我們就不管了。但是在 TensorFlow 中,我們在函數裏邊創建了一個變量,就是往 Graph 中添加了一個節點。出了這個函數後,這個節點還是存在於 Graph 中的。
tf.get_variable():tf.get_variable()方法是TensorFlow提供的比tf.Variable()稍微高級的創建/獲取變量的方法,它的工作方式根據當前的變量域(Variable Scope)的reuse屬性變化而變化,我們可以通過tf.get_variable_scope().reuse來查看這個屬性,它默認是False。tf.get_variable() 的機制跟 tf.Variable() 有很大不同,如果指定的變量名已經存在(即先前已經用同一個變量名通過 get_variable() 函數實例化了變量),那麼 get_variable()只會返回之前的變量,否則才創造新的變量。
tf.get_variable(
name,
shape=None,
dtype=None,
initializer=None,
regularizer=None,
trainable=None,
collections=None,
caching_device=None,
partitioner=None,
validate_shape=True,
use_resource=None,
custom_getter=None,
constraint=None,
synchronization=tf.VariableSynchronization.AUTO,
aggregation=tf.VariableAggregation.NONE
)
name_scope和varialbe_scope區別示例
with tf.name_scope('nsc1'):
v1 = tf.Variable([1], name='v1')
with tf.variable_scope('vsc1'):
v2 = tf.Variable([1], name='v2')
v3 = tf.get_variable(name='v3', shape=[])
print 'v1.name: ', v1.name
print 'v2.name: ', v2.name
print 'v3.name: ', v3.name
v1.name: nsc1/v1:0
v2.name: nsc1/vsc1/v2:0
v3.name: vsc1/v3:0
tf.name_scope() 並不會對 tf.get_variable() 創建的變量有任何影響。 tf.name_scope() 主要是用來管理命名空間的,這樣子讓我們的整個模型更加有條理。
而 tf.variable_scope() 的作用是爲了實現變量共享,它和 tf.get_variable() 來完成變量共享的功能。
[TensorFlow入門(七) 充分理解 name / variable_scope]
在變量域內初始化變量
每次初始化變量時都要傳入一個 initializer
,這實在是麻煩,而如果使用變量域的話,就可以批量初始化參數了:
with tf.variable_scope("foo", initializer=tf.constant_initializer(0.4)):
v = tf.get_variable("v", [1])
assert v.eval() == 0.4 # Default initializer as set above.
w = tf.get_variable("w", [1], initializer=tf.constant_initializer(0.3)):
assert w.eval() == 0.3 # Specific initializer overrides the default.
with tf.variable_scope("bar"):
v = tf.get_variable("v", [1])
assert v.eval() == 0.4 # Inherited default initializer.
with tf.variable_scope("baz", initializer=tf.constant_initializer(0.2)):
v = tf.get_variable("v", [1])
assert v.eval() == 0.2 # Changed default initializer.
tf.get_variable使用示例1:變量共享
在指定的變量域中調用:
# create var
with tf.variable_scope('embedding'):
entity = tf.get_variable(name='entity', initializer=...)
# reuse var
with tf.variable_scope('embedding', reuse=True):
entity = tf.get_variable(name='entity')
得到的變量entity的名字是embedding/entity。
更多變量共享在cnn、rnn中的使用參考[TensorFlow入門(七) 充分理解 name / variable_scope]
tf.get_variable使用示例2
1 tf.get_variable_scope().reuse == False
此時調用tf.get_variable(name, shape, dtype, initializer),TensorFlow 認爲你是要初始化一個新的變量,這個變量的名字爲name,維度是shape,數據類型是dtype,初始化方法是指定的initializer。如果名字爲name的變量已經存在的話,會導致ValueError。當然直接 tf.get_variable(name='entity')而沒有initializer 也會報錯:The initializer passed is not valid. It should be a callable with no arguments and the shape should not be provided or an instance of `tf.keras.initializers.*' and `shape` should be fully defined.。
一個例子如下:
# create var
entity = tf.get_variable(name='entity', initializer=...)
2 tf.get_variable_scope().reuse == True
此時調用tf.get_variable(name),TensorFlow 認爲你是要到程序裏面尋找變量名爲 scope name + name 的變量。如果這個變量不存在,會導致ValueError。當然如果直接 tf.get_variable(name='entity', initializer=...)也會報錯。
一個例子如下:
# reuse var
tf.get_variable_scope().reuse_variables() # set reuse to True
entity = tf.get_variable(name='entity')
tf.get_variable使用示例3
解決示例2的痛點可以直接使用tf.AUTO_REUSE自動判斷是不是第一次get這個變量,是則創建,否則get。
seq_a_embed = self.seq_embedding(seq_a, is_training)
seq_b_embed = self.seq_embedding(seq_b, is_training)
def seq_embedding(self, seq, is_training):
''' Word Embeddings '''
with tf.variable_scope('embedding', reuse=tf.AUTO_REUSE):
word_ids = self.vocab_words.lookup(seq)
bert = np.load(self.params['embeddings'])['embeddings'] # np.array # todo 移到外面
variable = np.vstack([bert, [[0.] * self.params['dim']]]) # add oov
variable = tf.cast(variable, dtype=tf.float32)
variable = tf.get_variable('embedding_matrix', initializer=variable, dtype=tf.float32, trainable=True)
variable = tf.Print(variable, [variable])
# shape=(vocab, 768)
embeddings = tf.nn.embedding_lookup(variable, word_ids)
# shape = (batch, lstm*2, emb) = (32, 200, 768)
embeddings = tf.keras.layers.Dropout(rate=self.params['dropout'])(embeddings, training=is_training)
# shape=(?, ?, 768)=(batch_size, max_seq_len_in_batch, word_emb_size)
# embeddings = tf.layers.batch_normalization(embeddings.transpose(1, 2)).transpose(1, 2)
return embeddings
Note:
1 每次checkpoint保存後再訓練Restoring parameters from results/model/model.ckpt時,tf.get_variable('embedding_matrix', initializer=..)會直接找到之前訓練好的變量,而不會再重新load一次預訓練的embedding而重新初始化embedding_matrix。
而reuse=tf.AUTO_REUSE在這裏的作用是兩次函數調用時,都使用同一個embedding_matrix來查找embeddings,每次batch訓練完成也是使用同一個更新後的embedding_matrix。所以variable = tf.Print(variable, [variable])每兩次輸出是一樣的:
[[0.197622746 1.53550613 -0.476417035...]...]
[[0.197622746 1.53550613 -0.476417035...]...]
[[0.198618323 1.53450835 -0.477412552...]...]
[[0.198618323 1.53450835 -0.477412552...]...]
[[0.199071899 1.53360093 -0.477980673...]...]
[[0.199071899 1.53360093 -0.477980673...]...]
...
2 vocab中的字符需要按頻次c降序輸出,這樣才能很好地在調試中variable = tf.Print(variable, [variable])中很好地觀察到高頻出現的字符的變化(否則低頻可能會讓你以爲數據每次讀取都一樣沒變)
variable = tf.Print(variable, [variable])可以在日誌中找到每次restore時variable的第一個word的embed值。
同時使用
from tensorflow.python.tools import inspect_checkpoint as ickpt
filename = '.../results/model/model.ckpt-315'
tensor_name = 'embedding/embedding_matrix'
ickpt.print_tensors_in_checkpoint_file(filename, tensor_name=tensor_name, all_tensors=False)
可以看到每次save的ckpt的embedding_matrix前幾個和後幾個的值,對比一下(除了第0個ckpt-0)第一個word大致是一樣的:
ckpt-0
[[0.197622746 1.53550613 -0.476417035...]...]
[[ 0.19762275 1.5355061 -0.47641703 ... -0.5410988 0.5473113
ckpt-168
[[0.204656258 1.54020405 -0.477185875...]...]
[[0.20465626 1.540204 -0.47718588 ... -0.5385146 0.5552384
...
ckpt-315
[[ 0.20471798 1.5401478 -0.47707108 ... -0.53842896 0.5551153
[[0.204717979 1.54014778 -0.477071077...]...]
[tensorflow: 對variable_scope進行reuse的兩種方法]
tf.get_variable使用示例4(錯誤示例)
with tf.variable_scope('embedding', reuse=tf.AUTO_REUSE):
word_ids = self.vocab_words.lookup(seq)
try:
variable = tf.get_variable('embedding_matrix')
print('get seq_embedding for the second time')
except Exception as e:
print(e)
bert = np.load(self.params['embeddings'])['embeddings']
variable = np.vstack([bert, [[0.] * self.params['dim']]]) # add oov
variable = tf.cast(variable, dtype=tf.float32)
variable = tf.get_variable('embedding_matrix', initializer=variable, dtype=tf.float32, trainable=True)
variable = tf.Print(variable, [variable])
每次restore時不能直接使用tf.get_variable('embedding_matrix')得到variable,會出錯輸出e爲The initializer passed is not valid...,但是在每次restore之間的訓練是可以直接使用tf.get_variable('embedding_matrix')的(這可能是因爲在restore之前tf總會先進行init再進行restore賦值(直接取上次checkpoint中的值覆蓋init值)[參考Tensorflow:模型保存和服務note 2,3])。所以沒必要使用try except。
from: -柚子皮-
ref: