【问题标题】:Forcing dependence on a variable update强制依赖变量更新
【发布时间】:2016-02-29 07:04:37
【问题描述】:

假设我有一些函数f 的变量x

x = tf.Variable(1.0)
fx = x*x

还有一个更新x的操作:

new_x = x.assign(2.0)

我想从更新的x 中获取f 的值。本来以为

with tf.control_dependencies([new_x,]):
    new_fx = tf.identity(fx)    

会强制 new_fx 依赖更新 new_x,但情况似乎并非如此:

init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)

# prints 1.0, expected 4.0
print "new fx", sess.run(new_fx)

还有其他方法可以定义fx 的更新值吗?

显然,我可以通过编写 new_fx = new_x * new_x 之类的内容来创建一个新的独立副本,但这会扩大图形大小,并且还需要访问 fx 的定义,我更愿意将其视为黑盒。

编辑:为了激发这一点,这是我要编写的代码草图:

# Hamiltonian Monte Carlo update, simplified
def hmc_step(x, momentum, logpdf, n_steps=50): 
    # x and momentum are Variables
    # logpdf is a Tensor with potentially complicated dependence on x

    grad = tf.gradients(logpdf, x)[0]

    # initial position        
    new_x = x

    for i in range(n_steps):
        # update position
        new_x = x.assign(new_x + momentum)

        # update momentum using gradient at *current* position
        with tf.control_dependencies([new_x]):
             momentum = momentum + grad # DOESN'T WORK

        # DOES WORK BUT IS UGLY
        # new_logpdf = define_logpdf(new_x)
        # new_grad = tf.gradients(new_logpdf, new_x)[0]
        # momentum = momentum + new_grad

    # (do some stuff to accept/reject the new x)
    # ....

    return new_x

每次通过循环定义一个新的 logpdf 副本并重新导出梯度感觉真的很不雅:它需要访问 define_logpdf() 并将图形大小扩大 50 倍。有没有更好的方法来做到这一点(除了一些等价的 theano.scan)?

【问题讨论】:

  • 你可以在 sess.run([new_x]) 后面跟着 sess.run([other_stuff]),这保证 new_xother_stuff 之前被评估
  • 对。这也不理想,因为它将更新逻辑推送到运行时 Python 代码中,当我宁愿在 TF 操作中以声明方式封装和表示它时,但听起来我想要的可能只是不可能的,所以这是我的选项之一会考虑的。谢谢!

标签: python tensorflow


【解决方案1】:

with tf.control_dependencies([op]) 块将控制依赖于 op 到 with 块内的其他操作创建。在您的情况下,x*x 是在外部创建的,而 tf.identity 只是获取旧值。这就是你想要的:

with tf.control_dependencies([new_x,]):
  new_fx = x*x

【讨论】:

  • 为什么 tf.identity 不创建依赖于 new_x 更新的新操作?身份不是“操作”吗?我知道我可以定义一个新的函数副本,但这不是我想要的。
  • tf.identitynew_x 更新后评估,因为控制块。但是对tf.identity 的输入没有限制,因此可以在更新之前进行评估
  • 谢谢,这是有道理的。我更新了帖子,对我正在寻找的内容进行了更多说明。这听起来像是保证在分配后重新评估依赖项的唯一方法是根据新分配的值显式地重新定义该依赖项?