首页 > 解决方案 > tf.GradientTape 为渐变返回 None

问题描述

我正在使用 tf.GradientTape().gradient() 来计算表示点,它可用于计算给定训练示例对给定测试示例的“影响”。给定测试示例x_t和训练示例的表示点x_i被计算为它们的特征表示的点积,f_tf_i乘以权重alpha_i

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

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

在此处输入图像描述

在上面的等式中,L 是标准损失函数(多类分类的分类交叉熵),phi 是 pre-softmax 激活输出(所以它的长度是类的数量)。此外alpha_i,还可以进一步分解为alpha_ij,这是针对特定类计算的jphi_j因此,我们只需获得与测试示例的预测类别(最终预测最高的类别)对应的 pre-softmax 输出。

我用 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: 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),它将不再返回 None。不知道为什么会这样?在切片张量上计算梯度是否存在问题?“观察”太多变量是否存在问题?我对渐变胶带的工作不多,所以我不确定修复方法是什么,但希望能得到帮助。

对于任何感兴趣的人,这里有更多详细信息:在此处输入图像描述

标签: tensorflowneural-networkslicetensorflow2.0automatic-differentiation

解决方案


我从来没有看到你watch任何张量。请注意,tf.Variable默认情况下磁带仅跟踪。您的代码中是否缺少此内容?否则我看不出t1.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_ij,实际上没有反向传播路径(我们只能沿着箭头向后)。另一种思考方式:您在计算损失presoftmax_ij 后进行计算。那么损失怎么可能取决于它呢?


推荐阅读