softmax 的 Tensorflow 问题

Nik*_*Nik 5 python numerical-stability tensorflow softmax

我有一个Tensorflow多类分类器,其产生naninf同时使用计算概率tf.nn.softmax。请参阅以下代码段(logits具有形状batch_size x 6,因为我有 6 个类并且输出是单热编码的)。batch_size是 1024。

logits = tf.debugging.check_numerics(logits, message='bad logits', name=None)
probabilities = tf.nn.softmax(logits=logits, name='Softmax')
probabilities = tf.debugging.check_numerics(probabilities, message='bad probabilities', name=None)
Run Code Online (Sandbox Code Playgroud)

分类器在找到naninf中的最后一条语句时失败probabilitieslogits是干净的,否则第一个语句将失败。

从我读到的内容来看tf.nn.softmax,它可以处理 logits 中非常大和非常小的值。我已经在交互模式下验证了这一点。

>>> with tf.Session() as s:
...   a = tf.constant([[1000, 10], [-100, -200], [3, 4.0]])
...   sm = tf.nn.softmax(logits=a, name='Softmax')
...   print(a.eval())
...   print(sm.eval())
...
[[1000.   10.]
 [-100. -200.]
 [   3.    4.]]
[[1.         0.        ]
 [1.         0.        ]
 [0.26894143 0.7310586 ]]
Run Code Online (Sandbox Code Playgroud)

然后我尝试剪辑这些值,logits现在整个事情都可以工作了。请参阅下面的修改后的片段。

logits = tf.debugging.check_numerics(logits, message='logits', name=None)
safe_logits = tf.clip_by_value(logits, -15.0, 15.0)
probabilities = tf.nn.softmax(logits=safe_logits, name='Softmax')
probabilities = tf.debugging.check_numerics(probabilities, message='bad probabilities', name=None)
Run Code Online (Sandbox Code Playgroud)

在第二个语句中,我将值裁剪logits为 -15 和 15,这以某种方式阻止了nan/inf在 softmax 计算中。所以,我能够解决手头的问题。

但是,我仍然不明白为什么这种剪辑会起作用?(我应该提到 -20 和 20 之间的裁剪不起作用,并且模型在naninfin 中失败probabilities)。

有人能帮我理解为什么会这样吗?

我正在使用 tensorflow 1.15.0,在 64 位实例上运行。

Yao*_*ang 3

首先要查看的是价值观本身,您已经这样做了。第二个要注意的地方是渐变。即使该值看起来合理,如果梯度非常陡,反向传播最终也会导致梯度和值爆炸。

例如,如果 logits 由 log(x) 等生成,则 0.001 的 x 将生成 -6.9。看起来很良善。但梯度是1000!这会在反向传播/前向传播期间迅速爆炸梯度和值。

# Pretend this is the source value that is fed to a function that generates the logit. 
>>> x = tf.Variable(0.001)

# Let's operate on the source value to generate the logit. 
>>> with tf.GradientTape() as tape:
...   y = tf.math.log(x)
... 

# The logit looks okay... -6.9. 
>>> y
<tf.Tensor: shape=(), dtype=float32, numpy=-6.9077554>

# But the gradient is exploding. 
>>> tape.gradient(y,x)
<tf.Tensor: shape=(), dtype=float32, numpy=999.99994>
>>> 
Run Code Online (Sandbox Code Playgroud)

削减 logit 似乎专注于生成较小的值以提供给 softmax,但这可能不是它有帮助的原因。(事实上​​,softmax 可以处理值为 tf.float32.max 的 logit,没有问题,所以 logit 的值不太可能是问题)。真正发生的情况可能是,当您剪辑为 15 时,您还将梯度设置为零,否则 logit 将为 20,并且梯度会爆炸。因此,剪切值也会引入剪切梯度。

# This is same source variable as above. 
>>> x = tf.Variable(0.001)

# Now let's operate with clipping. 
>>> with tf.GradientTape() as tape:
...   y = tf.clip_by_value(tf.math.log(x), -1., 1.)
... 

# The clipped logit still looks okay... 
>>> y
<tf.Tensor: shape=(), dtype=float32, numpy=-1.0>

# What may be more important is that the clipping has also zeroed out the gradient
>>> tape.gradient(y,x)
<tf.Tensor: shape=(), dtype=float32, numpy=0.0>
Run Code Online (Sandbox Code Playgroud)