Tensorflow 无法通过变量获取梯度,但可以通过张量获取梯度

Tensorflow cannot get gradient wrt a Variable, but can wrt a Tensor

我对计算损失的梯度很感兴趣,损失的梯度是通过使用 Eager Execution 的 TensorFlow 中的矩阵乘积计算得出的。如果乘积被计算为张量,我可以这样做,但如果它被 assign()ed 到一个变量,我就不能这样做。这是 大大 减少的代码:

import tensorflow as tf
import numpy as np
tf.enable_eager_execution()

multipliers_net = tf.get_variable("multipliers", shape=(1, 3, 3, 1),
                                  initializer=tf.random_normal_initializer())
activations_net = tf.Variable(tf.ones_like(multipliers_net))
output_indices = [(0, 1, 2, 0)]

def step():
    global activations_net

    #### PROBLEMATIC ####
    activations_net.assign(multipliers_net * activations_net)
    #### NO PROBLEM ####
    # activations_net = multipliers_net * activations_net

    return tf.gather_nd(activations_net, output_indices)


def train(targets):
    for y in targets:
        with tf.GradientTape() as tape:
            out = step()
            print("OUT", out)
            loss = tf.reduce_mean(tf.square(y - out))
            print("LOSS", loss)
        de_dm = tape.gradient(loss, multipliers_net)
        print("GRADIENT", de_dm, sep="\n")
        multipliers_net.assign(LEARNING_RATE * de_dm)


targets = [[2], [3], [4], [5]]

train(targets)

就目前而言,此代码将显示正确的 OUT 和 LOSS 值,但 GRADIENT 将打印为 None。但是,如果 "PROBLEMATIC" 下面的行被注释掉而 "NO PROBLEM" 没有被注释掉,那么梯度计算就很好了。我推断这是因为在第二种情况下,activations_net 变成了张量,但我不知道为什么这突然使梯度可计算,而之前不是。

我很确定我应该保留 activations_netmultiplier_net 作为变量,因为在更大的方案中,两者都是动态更新的,据我所知,这样的东西是最好的保留为变量而不是不断重新分配张量。

我会尽力解释我所知道的。 问题出现在这一行

de_dm = tape.gradient(loss, multipliers_net)

如果您在 "PROBLEMATIC" 和 "NO PROBLEM" 两种情况下都 print(tape.watched_variables(),您会看到在第一种情况下磁带 'watches' 相同的 multipliers_net 变量两次. 你可以试试tape.reset()tape.watch(),但是只要你把assign op传进去就没有效果了。 如果你在 tf.GradientTape() 中尝试 multipliers_net.assign(any_variable),你会发现它不起作用。但是如果你尝试分配一些产生张量的东西,例如tf.ones_like(),会起作用的。

multipliers_net.assign(LEARNING_RATE * de_dm)

这也是出于同样的原因。好像只接受eager_tensors 希望这有帮助