【问题标题】:GradientTape with Keras returns 0带有 Keras 的 GradientTape 返回 0
【发布时间】:2020-05-13 09:46:33
【问题描述】:

我已尝试将GradientTape 与 Keras 模型(简化)一起使用,如下所示:

import tensorflow as tf
tf.enable_eager_execution()

input_ = tf.keras.layers.Input(shape=(28, 28))
flat = tf.keras.layers.Flatten()(input_)
output = tf.keras.layers.Dense(10, activation='softmax')(flat)
model = tf.keras.Model(input_, output)
model.compile(loss='categorical_crossentropy', optimizer='sgd')

import numpy as np
inp = tf.Variable(np.random.random((1,28,28)), dtype=tf.float32, name='input')
target = tf.constant([[1,0,0,0,0,0,0,0,0,0]], dtype=tf.float32)
with tf.GradientTape(persistent=True) as g:
    g.watch(inp)
    result = model(inp, training=False)

print(tf.reduce_max(tf.abs(g.gradient(result, inp))))

但是对于inp的一些随机值,梯度处处为零,其余的梯度幅度非常小(

我也用 MNIST 训练的 3 层 MLP 进行了尝试,结果是一样的,但是用没有激活的 1 层线性模型进行尝试。

这是怎么回事?

【问题讨论】:

  • 我不认为“这里发生了什么”真的是一个有效的问题,你期望什么梯度值以及为什么你认为有问题?
  • 好吧,给定一个随机输入,它的前馈阶段给出了错误的分类,那么梯度是否应该足够大,以至于我们可以用合理的步长进行 SGD?这就是我所期待的结果。编辑:我曾经在那里有一个损失层,在调试过程中我正在寻找反向传播链中的中间值。所以我的期望不是太小。

标签: python tensorflow keras gradienttape


【解决方案1】:

您正在计算 softmax 输出层的梯度——因为 softmax 总是总和为 1,所以梯度(在多输入情况下,在维度 AFAIK 上求和/平均)必须为 0 是有意义的—— - 层的整体输出不能改变。我猜你得到的小值 > 0 的情况是数字打嗝。
当您移除激活函数时,此限制不再成立,激活可能会变得更大(意味着幅度 > 0 的梯度)。

您是否尝试使用梯度下降来构造导致某个类的概率非常大的输入(如果不是,请忽略这个...)? @jdehesa 已经包含了一种通过损失函数执行此操作的方法。请注意,您也可以通过 softmax 来实现,如下所示:

import tensorflow as tf
tf.enable_eager_execution()

input_ = tf.keras.layers.Input(shape=(28, 28))
flat = tf.keras.layers.Flatten()(input_)
output = tf.keras.layers.Dense(10, activation='softmax')(flat)
model = tf.keras.Model(input_, output)
model.compile(loss='categorical_crossentropy', optimizer='sgd')

import numpy as np
inp = tf.Variable(np.random.random((1,28,28)), dtype=tf.float32, name='input')   
with tf.GradientTape(persistent=True) as g:
    g.watch(inp)
    result = model(inp, training=False)[:,0]

print(tf.reduce_max(tf.abs(g.gradient(result, inp))))

请注意,我只抓取第 0 列中的结果,对应于第一类(我删除了target,因为它没有被使用)。这将只计算这个类的 softmax 值的梯度,这是有意义的。

一些注意事项:

  • 在渐变磁带上下文管理器中进行索引很重要!如果您在外面进行(例如,在您调用 g.gradient 的行中,这将不起作用(无渐变)
  • 您也可以改用 logits 的梯度(pre-softmax 值)。这是不同的,因为 softmax 概率可以通过降低其他类的可能性来增加,而 logits 只能通过增加相关类的“分数”来增加。

【讨论】:

  • 感谢您提供有关 TF 渐变的提示!这个问题对我来说是相当错误的,因为我尝试简化我遇到的问题并且失败了。如果愿意,请查看我对@jdehesa 答案的评论。
【解决方案2】:

根据模型的输出计算梯度通常不是很有意义,通常您根据损失计算梯度,这就是告诉模型变量应该去哪里才能达到您的目标。在这种情况下,您将优化输入而不是模型参数,但它是相同的。

import tensorflow as tf
import numpy as np
tf.enable_eager_execution()  # Not necessary in TF 2.x

tf.random.set_random_seed(0)  # tf.random.set_seed in TF 2.x
np.random.seed(0)
input_ = tf.keras.layers.Input(shape=(28, 28))
flat = tf.keras.layers.Flatten()(input_)
output = tf.keras.layers.Dense(10, activation='softmax')(flat)
model = tf.keras.Model(input_, output)
model.compile(loss='categorical_crossentropy', optimizer='sgd')

inp = tf.Variable(np.random.random((1, 28, 28)), dtype=tf.float32, name='input')
target = tf.constant([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=tf.float32)
with tf.GradientTape(persistent=True) as g:
    g.watch(inp)
    result = model(inp, training=False)
    # Get the loss for the example
    loss = tf.keras.losses.categorical_crossentropy(target, result)

print(tf.reduce_max(tf.abs(g.gradient(loss, inp))))
# tf.Tensor(0.118953675, shape=(), dtype=float32)

【讨论】:

  • 谢谢,我正在简化我的代码以获得一个最小的例子,忘记重试损失的梯度。但是,当我使用 3 层 MLP(784 -> 300 -> 100 -> 10,除了最后的 softmax 之外的所有 ReLU)尝试此操作时,问题仍然存在。我想知道这是否值得自己提出一个问题。
  • @kwkt 我刚刚在上面的脚本中尝试了那个配置(784个输入层,300个relu层,100个relu层,10个softmax输出层),打印的值为0.10540651
  • 未经训练,是的。但是,使用我在 MNIST 训练 100 个 epoch 后得到的 this 特定模型保存,大多数时候它给出了 0。
  • @kwkt 这很可能是因为你处于局部最小值或接近它,这通常是在训练一段时间后发生的。这意味着在任何方向改变变量都不会降低损失值,它对应于损失图变得平坦的点。你可以想到vanishing gradient problem,但这不应该发生在带有 ReLU 激活的浅层模型中。
  • 但在我的情况下,我正在尝试进行梯度下降以创建一个对抗性数据点,这意味着必须有某个方向可以减少损失,除非梯度表面真的很平坦在某个 epsilon 球中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-24
  • 2018-09-04
  • 2017-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多