在 TensorFlow 中,tf.identity 有什么用?
In TensorFlow, what is tf.identity used for?
我看到 tf.identity
在一些地方使用过,例如官方 CIFAR-10 教程和 Whosebug 上的批归一化实现,但我不明白为什么有必要。
它有什么用?谁能给出一两个用例?
一个建议的答案是它可以用于 CPU 和 GPU 之间的传输。我不清楚。问题的扩展,基于 this: loss = tower_loss(scope)
在 GPU 块下,这向我暗示 tower_loss
中定义的所有运算符都映射到 GPU。然后,在 tower_loss
的末尾,我们在返回之前看到 total_loss = tf.identity(total_loss)
。为什么?这里不使用 tf.identity
会有什么缺陷?
tf.identity
当你想在设备之间显式地 t运行 运动张量时很有用(比如,从 GPU 到 CPU)。
op 向图中添加 send/recv 个节点,当输入和输出的设备不同时,这些节点会进行复制。
默认行为是当操作发生在不同的设备上时隐式添加 send/recv 节点,但您可以想象在某些情况下(尤其是在 multi-threaded/distributed 设置中)它可能有用在 session.run
的单次执行中多次获取变量的值。 tf.identity
允许对何时应从源设备读取值进行更多控制。可能更适合此操作的名称是 read
.
另请注意,在tf.Variable
link的实现中,在构造函数中添加了identity op,确保对变量的所有访问都从源中复制数据只有一次。当变量存在于 GPU 上但它被多个 CPU 操作(或相反)读取时,多个副本可能会很昂贵。用户可以在需要时通过多次调用 tf.identity
来更改行为。
编辑:编辑问题后更新了答案。
此外,tf.identity
可以用作虚拟节点来更新对张量的引用。这对于各种控制流操作很有用。在 CIFAR 案例中,我们希望强制 ExponentialMovingAverageOp 将在检索损失值之前更新相关变量。这可以实现为:
with tf.control_dependencies([loss_averages_op]):
total_loss = tf.identity(total_loss)
在这里,tf.identity
除了在评估 loss_averages_op
.
后将 total_loss
张量标记为 运行 之外没有做任何有用的事情
经过一番摸索之后,我想我已经注意到一个用例适合我见过的所有示例。如果还有其他用例,请举例说明。
用例:
假设您希望在每次评估特定变量时都运行 一个运算符。例如,假设您希望在每次计算变量 y
时将 1 添加到 x
。这似乎可行:
x = tf.Variable(0.0)
x_plus_1 = tf.assign_add(x, 1)
with tf.control_dependencies([x_plus_1]):
y = x
init = tf.initialize_all_variables()
with tf.Session() as session:
init.run()
for i in xrange(5):
print(y.eval())
它不会:它会打印 0, 0, 0, 0, 0。相反,我们似乎需要在 control_dependencies
块内向图中添加一个新节点。所以我们使用这个技巧:
x = tf.Variable(0.0)
x_plus_1 = tf.assign_add(x, 1)
with tf.control_dependencies([x_plus_1]):
y = tf.identity(x)
init = tf.initialize_all_variables()
with tf.Session() as session:
init.run()
for i in xrange(5):
print(y.eval())
这有效:它打印 1、2、3、4、5。
如果在 CIFAR-10 教程中我们删除了 tf.identity
,那么 loss_averages_op
永远不会 运行。
我遇到了其他答案未完全涵盖的另一个用例。
def conv_layer(input_tensor, kernel_shape, output_dim, layer_name, decay=None, act=tf.nn.relu):
"""Reusable code for making a simple convolutional layer.
"""
# Adding a name scope ensures logical grouping of the layers in the graph.
with tf.name_scope(layer_name):
# This Variable will hold the state of the weights for the layer
with tf.name_scope('weights'):
weights = weight_variable(kernel_shape, decay)
variable_summaries(weights, layer_name + '/weights')
with tf.name_scope('biases'):
biases = bias_variable([output_dim])
variable_summaries(biases, layer_name + '/biases')
with tf.name_scope('convolution'):
preactivate = tf.nn.conv2d(input_tensor, weights, strides=[1, 1, 1, 1], padding='SAME')
biased = tf.nn.bias_add(preactivate, biases)
tf.histogram_summary(layer_name + '/pre_activations', biased)
activations = act(biased, 'activation')
tf.histogram_summary(layer_name + '/activations', activations)
return activations
大多数情况下,在构建卷积层时,您只需要返回激活值,以便将它们馈送到下一层。但是,有时 - 例如在构建自动编码器时 - 您需要预激活值。
在这种情况下,一个优雅的解决方案是传递 tf.identity
作为激活函数,有效地不激活层。
我发现 tf.identity 在 Tensorboard 中的另一个应用。
如果您使用 tf.shuffle_batch,它会同时 returns 多个张量,因此您在可视化图形时会看到混乱的画面,您无法将张量创建管道与实际输入张量分开:messy
但是使用tf.identity你可以创建重复的节点,这不会影响计算流程:nice
除了上述之外,我只是在需要为没有名称参数的操作分配名称时使用它,就像在 RNN 中初始化状态时一样:
rnn_cell = tf.contrib.rnn.MultiRNNCell([cells])
# no name arg
initial_state = rnn_cell.zero_state(batch_size,tf.float32)
# give it a name with tf.identity()
initial_state = tf.identity(input=initial_state,name="initial_state")
在分布式训练中,我们应该使用tf.identity,否则workers会在等待主worker初始化时挂起:
vec = tf.identity(tf.nn.embedding_lookup(embedding_tbl, id)) * mask
with tf.variable_scope("BiRNN", reuse=None):
out, _ = tf.nn.bidirectional_dynamic_rnn(fw, bw, vec, sequence_length=id_sz, dtype=tf.float32)
具体来说,如果没有身份,主worker会不恰当地将一些变量当作局部变量,而其他worker会等待无法结束的初始化操作
当我们的输入数据以字节为单位序列化时,我们想从这个数据集中提取特征。我们可以以键值格式这样做,然后为它获取一个占位符。当有多个特征并且每个特征必须以不同的格式读取时,它的好处会更明显。
#read the entire file in this placeholder
serialized_tf_example = tf.placeholder(tf.string, name='tf_example')
#Create a pattern in which data is to be extracted from input files
feature_configs = {'image': tf.FixedLenFeature(shape=[256], dtype=tf.float32),/
'text': tf.FixedLenFeature(shape=[128], dtype=tf.string),/
'label': tf.FixedLenFeature(shape=[128], dtype=tf.string),}
#parse the example in key: tensor dictionary
tf_example = tf.parse_example(serialized_tf_example, feature_configs)
#Create seperate placeholders operation and tensor for each feature
image = tf.identity(tf_example['image'], name='image')
text = tf.identity(tf_example['text'], name='text')
label = tf.identity(tf_example['text'], name='label')
我看到这种 hack 来检查断言:
assertion = tf.assert_equal(tf.shape(image)[-1], 3, message="image must have 3 color channels")
with tf.control_dependencies([assertion]):
image = tf.identity(image)
也只是用来起个名字:
image = tf.identity(image, name='my_image')
我看到 tf.identity
在一些地方使用过,例如官方 CIFAR-10 教程和 Whosebug 上的批归一化实现,但我不明白为什么有必要。
它有什么用?谁能给出一两个用例?
一个建议的答案是它可以用于 CPU 和 GPU 之间的传输。我不清楚。问题的扩展,基于 this: loss = tower_loss(scope)
在 GPU 块下,这向我暗示 tower_loss
中定义的所有运算符都映射到 GPU。然后,在 tower_loss
的末尾,我们在返回之前看到 total_loss = tf.identity(total_loss)
。为什么?这里不使用 tf.identity
会有什么缺陷?
tf.identity
当你想在设备之间显式地 t运行 运动张量时很有用(比如,从 GPU 到 CPU)。
op 向图中添加 send/recv 个节点,当输入和输出的设备不同时,这些节点会进行复制。
默认行为是当操作发生在不同的设备上时隐式添加 send/recv 节点,但您可以想象在某些情况下(尤其是在 multi-threaded/distributed 设置中)它可能有用在 session.run
的单次执行中多次获取变量的值。 tf.identity
允许对何时应从源设备读取值进行更多控制。可能更适合此操作的名称是 read
.
另请注意,在tf.Variable
link的实现中,在构造函数中添加了identity op,确保对变量的所有访问都从源中复制数据只有一次。当变量存在于 GPU 上但它被多个 CPU 操作(或相反)读取时,多个副本可能会很昂贵。用户可以在需要时通过多次调用 tf.identity
来更改行为。
编辑:编辑问题后更新了答案。
此外,tf.identity
可以用作虚拟节点来更新对张量的引用。这对于各种控制流操作很有用。在 CIFAR 案例中,我们希望强制 ExponentialMovingAverageOp 将在检索损失值之前更新相关变量。这可以实现为:
with tf.control_dependencies([loss_averages_op]):
total_loss = tf.identity(total_loss)
在这里,tf.identity
除了在评估 loss_averages_op
.
total_loss
张量标记为 运行 之外没有做任何有用的事情
经过一番摸索之后,我想我已经注意到一个用例适合我见过的所有示例。如果还有其他用例,请举例说明。
用例:
假设您希望在每次评估特定变量时都运行 一个运算符。例如,假设您希望在每次计算变量 y
时将 1 添加到 x
。这似乎可行:
x = tf.Variable(0.0)
x_plus_1 = tf.assign_add(x, 1)
with tf.control_dependencies([x_plus_1]):
y = x
init = tf.initialize_all_variables()
with tf.Session() as session:
init.run()
for i in xrange(5):
print(y.eval())
它不会:它会打印 0, 0, 0, 0, 0。相反,我们似乎需要在 control_dependencies
块内向图中添加一个新节点。所以我们使用这个技巧:
x = tf.Variable(0.0)
x_plus_1 = tf.assign_add(x, 1)
with tf.control_dependencies([x_plus_1]):
y = tf.identity(x)
init = tf.initialize_all_variables()
with tf.Session() as session:
init.run()
for i in xrange(5):
print(y.eval())
这有效:它打印 1、2、3、4、5。
如果在 CIFAR-10 教程中我们删除了 tf.identity
,那么 loss_averages_op
永远不会 运行。
我遇到了其他答案未完全涵盖的另一个用例。
def conv_layer(input_tensor, kernel_shape, output_dim, layer_name, decay=None, act=tf.nn.relu):
"""Reusable code for making a simple convolutional layer.
"""
# Adding a name scope ensures logical grouping of the layers in the graph.
with tf.name_scope(layer_name):
# This Variable will hold the state of the weights for the layer
with tf.name_scope('weights'):
weights = weight_variable(kernel_shape, decay)
variable_summaries(weights, layer_name + '/weights')
with tf.name_scope('biases'):
biases = bias_variable([output_dim])
variable_summaries(biases, layer_name + '/biases')
with tf.name_scope('convolution'):
preactivate = tf.nn.conv2d(input_tensor, weights, strides=[1, 1, 1, 1], padding='SAME')
biased = tf.nn.bias_add(preactivate, biases)
tf.histogram_summary(layer_name + '/pre_activations', biased)
activations = act(biased, 'activation')
tf.histogram_summary(layer_name + '/activations', activations)
return activations
大多数情况下,在构建卷积层时,您只需要返回激活值,以便将它们馈送到下一层。但是,有时 - 例如在构建自动编码器时 - 您需要预激活值。
在这种情况下,一个优雅的解决方案是传递 tf.identity
作为激活函数,有效地不激活层。
我发现 tf.identity 在 Tensorboard 中的另一个应用。 如果您使用 tf.shuffle_batch,它会同时 returns 多个张量,因此您在可视化图形时会看到混乱的画面,您无法将张量创建管道与实际输入张量分开:messy
但是使用tf.identity你可以创建重复的节点,这不会影响计算流程:nice
除了上述之外,我只是在需要为没有名称参数的操作分配名称时使用它,就像在 RNN 中初始化状态时一样:
rnn_cell = tf.contrib.rnn.MultiRNNCell([cells])
# no name arg
initial_state = rnn_cell.zero_state(batch_size,tf.float32)
# give it a name with tf.identity()
initial_state = tf.identity(input=initial_state,name="initial_state")
在分布式训练中,我们应该使用tf.identity,否则workers会在等待主worker初始化时挂起:
vec = tf.identity(tf.nn.embedding_lookup(embedding_tbl, id)) * mask
with tf.variable_scope("BiRNN", reuse=None):
out, _ = tf.nn.bidirectional_dynamic_rnn(fw, bw, vec, sequence_length=id_sz, dtype=tf.float32)
具体来说,如果没有身份,主worker会不恰当地将一些变量当作局部变量,而其他worker会等待无法结束的初始化操作
当我们的输入数据以字节为单位序列化时,我们想从这个数据集中提取特征。我们可以以键值格式这样做,然后为它获取一个占位符。当有多个特征并且每个特征必须以不同的格式读取时,它的好处会更明显。
#read the entire file in this placeholder
serialized_tf_example = tf.placeholder(tf.string, name='tf_example')
#Create a pattern in which data is to be extracted from input files
feature_configs = {'image': tf.FixedLenFeature(shape=[256], dtype=tf.float32),/
'text': tf.FixedLenFeature(shape=[128], dtype=tf.string),/
'label': tf.FixedLenFeature(shape=[128], dtype=tf.string),}
#parse the example in key: tensor dictionary
tf_example = tf.parse_example(serialized_tf_example, feature_configs)
#Create seperate placeholders operation and tensor for each feature
image = tf.identity(tf_example['image'], name='image')
text = tf.identity(tf_example['text'], name='text')
label = tf.identity(tf_example['text'], name='label')
我看到这种 hack 来检查断言:
assertion = tf.assert_equal(tf.shape(image)[-1], 3, message="image must have 3 color channels")
with tf.control_dependencies([assertion]):
image = tf.identity(image)
也只是用来起个名字:
image = tf.identity(image, name='my_image')