tf.identity 与 tf.control_dependencies

先来看看官方解释

def identity(input, name=None):  # pylint: disable=redefined-builtin
  r"""Return a tensor with the same shape and contents as input.

返回一个和输入 相同形状和内容 的 Tensor;

我们来分析一下:

1. 形状和内容都相同,那不就是等于吗?为什么还要这个函数呢?

2. 返回一个 Tensor,说明 tf.identity 是一个 op,因为 Tensor 是 op 的输入输出;

3. 也就是说,tf.identity 接受任意的 input,都强制输出 Tensor;

下面我们来验证一下

首先,以 constant 为例

##### constant 本身是个操作, 它的 = 也是操作
c = tf.constant(1.)
print(c)        # Tensor("Const:0", shape=(), dtype=float32)
d = tf.identity(c, name='d')
print(d)        # Tensor("d:0", shape=(), dtype=float32)
e = c
print(e)        # Tensor("Const:0", shape=(), dtype=float32)

tf.identity 和 = 没什么差别,只是加了个名字,然后我们可以不加的啊,不加名字,一模一样

接着,以 Variable 为例

##### Variable 本身不是操作,它的 = 不是操作
v = tf.Variable(2.)
print(v)        # <tf.Variable 'Variable:0' shape=() dtype=float32_ref>
no_name = tf.identity(v) print(no_name) # Tensor("Identity:0", shape=(), dtype=float32) x = tf.identity(v, name='x') print(x) # Tensor("x:0", shape=(), dtype=float32)
y = v print(y) # <tf.Variable 'Variable:0' shape=() dtype=float32_ref>

tf.identity 是个 op,而 = 不是 op;

此时,我们可以得出这样的结论:

tf.identity 是一个操作,把任意输入强制转换成 Tensor,如果 输入就是 Tensor,那他就等价于 =;

而 = 不是操作,只是内存拷贝;

然而这玩意有什么用呢? 

这就要提到另一个方法 tf.control_dependencies,它只对其内部的操作起作用,

我们以一个累加的例子来说明

##### test1
x = tf.Variable(1.0)
print(x)                            # <tf.Variable 'Variable:0' shape=() dtype=float32_ref>
x_plus_1 = tf.assign_add(x, 1)      ### 返回一个op

# control_dependencies的意义是:
# 在执行with包含的内容(y = x)前,先执行 control_dependencies 参数中的内容(x_plus_1),这里的解释不准确,先接着看。。。
with tf.control_dependencies([x_plus_1]):
    y = x
    print(y)            # <tf.Variable 'Variable_6:0' shape=() dtype=float32_ref>       ### y 只是个变量,不是 op
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    for i in range(5):
        print(sess.run(y))      # 1 1 1 1 1

先执行 with 包含的内容的前提是 ,该内容是个 操作,然而 y=x 不是操作,那么 control_dependencies 的参数 x_plus_1 没有执行,x 就还是 1, y=x 自然也是1,故输出 5 个 1

修改如下

##### test2
x = tf.Variable(1.0)
x_plus_1 = tf.assign_add(x, 1)

with tf.control_dependencies([x_plus_1]):
    y = x + 0.0
    print(y)        # Tensor("add:0", shape=(), dtype=float32)      ### y 变成 add op
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    for i in range(5):
        print(sess.run(y))      # 2 3 4 5 6

with 内是个操作,x_plus_1 先执行,输出符合预期;

只是这样写有点别扭,y=x 和 y=x+0 产生了截然不同的结果;

继续修改

##### test3
x = tf.Variable(1.0)
x_plus_1 = tf.assign_add(x, 1)

with tf.control_dependencies([x_plus_1]):
    y = tf.identity(x, name='x')
    print(y)                # Tensor("x:0", shape=(), dtype=float32)        ### y 变成 add op
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)
    for i in range(5):
        print(sess.run(y))          # 2 3 4 5 6

tf.identity 登场了,他把 y=x 变成了一个 操作,结果符合预期;

当然也可以这样写

##### test4
x = tf.Variable(1.0)
x = tf.assign_add(x, 1)

with tf.control_dependencies([x]):
    y = x                   ### 等价于 y = tf.assign_add(x, 1)
    print(y)                # Tensor("AssignAdd_3:0", shape=(), dtype=float32_ref)
init = tf.initialize_all_variables()
with tf.Session() as session:
    init.run()
    for i in range(5):
        print(y.eval())     # 2 3 4 5 6         ### 相当于sess.run(y)

总结:对于 control_dependencies 这个管理器,只有当里面的操作是一个 op 时,才会生效,也就是先执行传入的参数 op,再执行里面的 op;

而 y=x 仅仅是 一个简单赋值,不是 op,在 graph 中不会形成一个节点,这样该管理器就失效了;

tf.identity 是返回一个一模一样新的 tensor 的 op,这会增加一个新节点到 gragh 中,这时 control_dependencies 就会生效了

参考资料:

https://blog.csdn.net/hu_guan_jie/article/details/78495297

https://yq.aliyun.com/articles/487737

https://www.360kuai.com/pc/935e0f65c4a884856?cota=4&kuai_so=1&tj_url=so_rec&sign=360_57c3bbd1&refer_scene=so_1

原文地址:https://www.cnblogs.com/yanshw/p/12371034.html