我尝试将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
,梯度到处都是零,而对于其余的,梯度幅度真的很小(<1e-7(。
我也用 MNIST 训练的 3 层 MLP 尝试过这个,结果是一样的,但用没有激活的 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
的行中,这将不起作用(没有渐变( - 您也可以改用对数的梯度(前软最大值(。这是不同的,因为softmax概率可以通过降低其他类的可能性来增加,而logits只能通过增加所讨论类的"分数"来增加。
根据模型的输出计算梯度通常不是很有意义,通常您根据损失计算梯度,这就是告诉模型变量应该去哪里才能达到目标的原因。在这种情况下,您将优化输入而不是模型参数,但它是相同的。
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)