6

を使用して確率を生成nanまたは計算している Tensorflow マルチクラス分類器があります。次のスニペットを参照してください ( 6 つのクラスがあり、出力がワンホット エンコードされているため、形状はあります)。は 1024 です。inftf.nn.softmaxlogitsbatch_size x 6batch_size

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)

分類子は、nanまたはinfで見つかった最後のステートメントで失敗しますprobabilitieslogitsそうでなければ、最初のステートメントは失敗していたでしょう。

私が読んだことからtf.nn.softmax、ロジットで非常に大きな値と非常に小さな値を処理できます。対話モードでこれを確認しました。

>>> 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 ]]

次に、値をクリップしてみましたが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)

2 番目のステートメントでは、値logitsを -15 と 15 にクリッピングしています。これにより、softmax 計算でnan/が何らかの形で防止されます。infそのため、当面の問題を修正することができました。

しかし、なぜこのクリッピングが機能しているのか、まだわかりませんか? (-20 と 20 の間のクリッピングは機能せず、モデルはnanまたはinfで失敗することに注意してくださいprobabilities)。

なぜこれが当てはまるのか、誰かが理解するのを手伝ってくれますか?

64 ビット インスタンスで実行されている tensorflow 1.15.0 を使用しています。

4

1 に答える 1

3

最初に確認するのは値自体で、既に行っています。2 番目に注目すべきはグラデーションです。値が妥当に見えても、勾配が非常に急である場合、backprop は最終的に勾配と値を爆発させます。

たとえば、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>
>>> 

ロジットをクリッピングすると、ソフトマックスにフィードするためのより小さな値を生成することに焦点が当てられているように見えますが、それが助けになる理由ではないでしょう。(実際、softmax は値 tf.float32.max のロジットを問題なく処理できるため、ロジットの値が問題になる可能性はほとんどありません)。実際に起こっていることは、15 にクリップすると、爆発的な勾配でロジットが 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>
于 2021-09-02T19:23:38.083 に答える