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
)
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() 来完成变量共享的功能。
Note: 重复使用某函数定义网络层时,如果使用with tf.name_scope(name_scope)会导致里面的变量已定义报错,所以要使用tf.variable_scope。
[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.
[TensorFlow学习笔记:共享变量]
-柚子皮-
在指定的变量域中调用:
# 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]
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')
[TensorFlow的变量共享]
解决示例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的两种方法]
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: