tf。GradientTape 返回 None 表示渐变

tf.GradientTape returns None for gradient

提问人:Jane Sully 提问时间:8/2/2021 最后编辑:Jane Sully 更新时间:8/3/2021 访问量:1368

问:

我正在使用 tf。GradientTape().gradient() 来计算表示器点,该点可用于计算给定训练样本对给定测试样本的“影响”。给定测试示例和训练示例的表示点计算为其特征表示的点积,并且 乘以权重。x_tx_if_tf_ialpha_i

注意:此方法的细节对于理解问题不是必需的,因为主要问题是让渐变磁带工作。话虽如此,我为任何感兴趣的人提供了下面一些细节的屏幕截图。

计算alpha_i需要微分,因为它表示如下:

enter image description here

在上面的等式中,L 是标准损失函数(多类分类的分类交叉熵),phi 是 softmax 激活前的输出(因此它的长度是类数)。此外,还可以进一步分解为 ,它是相对于特定类计算的。因此,我们只得到与测试样本的预测类(具有最高最终预测的类)对应的 pre-softmax 输出。alpha_ialpha_ijjphi_j

我使用MNIST创建了一个简单的设置,并实现了以下内容:

def simple_mnist_cnn(input_shape = (28,28,1)):
  input = Input(shape=input_shape)
  x = layers.Conv2D(32, kernel_size=(3, 3), activation="relu")(input)
  x = layers.MaxPooling2D(pool_size=(2, 2))(x)
  x = layers.Conv2D(64, kernel_size=(3, 3), activation="relu")(x)
  x = layers.MaxPooling2D(pool_size=(2, 2))(x)
  x = layers.Flatten()(x) # feature representation 
  output = layers.Dense(num_classes, activation=None)(x) # presoftmax activation output 
  activation = layers.Activation(activation='softmax')(output) # final output with activation 
  model = tf.keras.Model(input, [x, output, activation], name="mnist_model")
  return model

现在假设模型已经训练完毕,我想计算给定训练示例对给定测试示例预测的影响,可能是为了模型理解/调试目的。

with tf.GradientTape() as t1:
  f_t, _, pred_t = model(x_t) # get features for misclassified example
  f_i, presoftmax_i, pred_i = model(x_i)

  # compute dot product of feature representations for x_t and x_i
  dotps = tf.reduce_sum(
            tf.multiply(f_t, f_i))

  # get presoftmax output corresponding to highest predicted class of x_t
  phi_ij = presoftmax_i[:,np.argmax(pred_t)]

  # y_i is actual label for x_i
  cl_loss_i = tf.keras.losses.categorical_crossentropy(pred_i, y_i)

alpha_ij = t1.gradient(cl_loss_i, phi_ij)
# note: alpha_ij returns None currently
k_ij = tf.reduce_sum(tf.multiply(alpha_i, dotps))

上面的代码给出了以下错误,因为 alpha_ij 是 None: 。但是,如果我更改 -> ,它不再返回 None。不知道为什么会这样?在切片张量上计算梯度是否存在问题?“观察”太多变量有问题吗?我没有使用过梯度胶带,所以我不确定修复方法是什么,但希望得到帮助。ValueError: Attempt to convert a value (None) with an unsupported type (<class 'NoneType'>) to a Tensor.t1.gradient(cl_loss_i, phi_ij)t1.gradient(cl_loss_i, presoftmax_i)

对于任何感兴趣的人,这里有更多细节:enter image description here

TensorFlow 神经网络 切片 TensorFlow2.0 自动微分

评论

0赞 o-90 8/2/2021
为什么要在 tensorflow 梯度中使用 numpy?这几乎可以肯定是问题所在。
0赞 Jane Sully 8/2/2021
我不知道这是否是一个问题?例如,在某些情况下,tensorflow 文档在梯度磁带中使用 numpy 操作:tensorflow.org/guide/autodiff。但可以肯定的是,我切换到了固定索引(例如 0),问题仍然存在。np.argmax(pred_t)

答:

2赞 xdurch0 8/2/2021 #1

我从来没有见过你任何张量。请注意,默认情况下,磁带仅进行跟踪。您的代码中是否缺少此内容?否则我看不出是如何工作的。watchtf.Variablet1.gradient(cl_loss_i, presoftmax_i)

无论哪种方式,我认为解决它的最简单方法是这样做

all_gradients = t1.gradient(cl_loss_i, presoftmax_i)
desired_gradients = all_gradients[[:,np.argmax(pred_t)]]

因此,只需在渐变之后进行索引即可。请注意,这可能会造成浪费(如果有很多类),因为您计算的梯度比您需要的要多。

为什么(我相信)你的版本不起作用的解释最容易在图中显示,但让我试着解释一下:想象一下有向图中的计算。我们有

presoftmax_i -> pred_i -> cl_loss_i

将损失反向传播到 presoftmax 很容易。但后来你又建立了另一个分支,

presoftmax_i -> presoftmax_ij

现在,当您尝试计算损失相对于 的梯度时,实际上没有反向传播路径(我们只能向后跟随箭头)。另一种思考方式是:你在计算损失进行计算。那么损失怎么可能取决于它呢?presoftmax_ijpresoftmax_ij

评论

0赞 Jane Sully 8/2/2021
感谢您的回复!这也是我所关心的。另外,我没有忘记包括一块手表,所以我也不确定它是如何工作的。你知道我如何观察一个直到渐变磁带内部才定义的变量(例如presoftmax_ij)吗?还有一件事需要注意(这可能是显而易见的)是,任何索引似乎都是一个问题。例如,我将 presoftmax_i 更改为 presoftmax_i[:,:],这是等效的,后者返回 None,而前者则不返回。
0赞 Jane Sully 8/2/2021
另外,如果我在计算cl_loss_i之前计算presoftmax_ij,你会期望有什么变化吗?我试图改变它,这似乎也无济于事。
1赞 xdurch0 8/5/2021
1. 您可以在磁带期间的任何时候使用变量,因此在定义张量后(即使在磁带内部)调用它应该没有问题,但我不是 100% 确定。2.关于索引,这会创建张量的副本,所以我想它将被视为“新”结果,并导致与某些索引相同的问题。3. 关于损失前的计算 -- 很难说,因为看起来你编辑了问题,然后就消失了。;)但我认为如果不根据.watch[:,:]i_ij_ij_ij