web-dev-qa-db-ja.com

数値的に安定したソフトマックス

以下のソフトマックス関数を計算する数値的に安定した方法はありますか?ニューラルネットワークコードでナンになる値を取得しています。

np.exp(x)/np.sum(np.exp(y))
12
Abhishek Bhatia

Softmax exp(x)/ sum(exp(x))は、実際には数値的に適切に動作します。正の項しかないため、有意性が失われることを心配する必要はありません。分母は少なくとも分子と同じ大きさなので、結果は0と1の間になることが保証されます。

起こり得る唯一の事故は、指数関数のオーバーフローまたはアンダーフローです。 xのすべての要素の単一またはオーバーフローのオーバーフローは、出力をだいたい役に立たなくします。

しかし、任意のスカラーcに対して保持される恒等式softmax(x)= softmax(x + c)を使用することで、これを防ぐのは簡単です:max(x)from xは、正でないエントリのみを持つベクトルを残し、オーバーフローを排除し、ゼロである要素を少なくとも1つ削除して、分母の消失(一部ではなく一部のエントリでアンダーフロー)を排除します。無害です)。

脚注:理論的には、合計で壊滅的な事故が発生する可能性がありますが、ばかげた数の用語が必要になります。たとえば、小数点以下3桁しか解決できない16ビット浮動小数点を使用する場合でも、「通常の」64ビット浮動小数点の15桁と比較すると、2 ^ 1431(〜6 x 10 ^ 431)と2の間が必要になります。 ^ 1432 2の係数でオフ の合計を取得します。

33
Paul Panzer

Softmax関数には2つの問題が発生しやすい: overflow および underflow

オーバーフロー:非常に大きな数値がapproximatedas infinityの場合に発生します

Underflow :非常に小さい数(数直線のゼロに近い)がapproximated(つまり、丸められて)zeroである場合に発生します

Softmax計算を行うときにこれらの問題に対処するための一般的なトリックは、すべての要素からその中の最大要素を減算することによって入力ベクトルをシフトすることです-。入力ベクトルxについては、次のようにzを定義します。

z = x-max(x)

次に、新しい(安定した)ベクトルzのソフトマックスを取得します


例:

In [266]: def stable_softmax(x):
     ...:     z = x - max(x)
     ...:     numerator = np.exp(z)
     ...:     denominator = np.sum(numerator)
     ...:     softmax = numerator/denominator
     ...:     return softmax
     ...: 

In [267]: vec = np.array([1, 2, 3, 4, 5])

In [268]: stable_softmax(vec)
Out[268]: array([ 0.01165623,  0.03168492,  0.08612854,  0.23412166,  0.63640865])

In [269]: vec = np.array([12345, 67890, 99999999])

In [270]: stable_softmax(vec)
Out[270]: array([ 0.,  0.,  1.])

詳細については、deep learningの章のNumerical Computationの章を参照してください。

9
kmario23

Paul Panzer's の説明に感謝しますが、なぜmax(x)を引く必要があるのか​​と思います。なので、もっと詳しい情報を見つけて、同じ質問をしてくださる方のお役に立てれば幸いです。次のリンクの記事にある「その最大減算の結果は?」のセクションを参照してください。

https://nolanbconaway.github.io/blog/2017/softmax-numpy

1
Alumi Lu

@ kmario23の回答を拡張して、1次元または2次元のnumpy配列またはリストをサポートします(softmax関数を介して結果のバッチを渡す場合は一般的です)。

import numpy as np


def stable_softmax(x):
    z = x - np.max(x, axis=-1, keepdims=True)
    numerator = np.exp(z)
    denominator = np.sum(numerator, axis=-1, keepdims=True)
    softmax = numerator / denominator
    return softmax


test1 = np.array([12345, 67890, 99999999])  # 1D
test2 = np.array([[12345, 67890, 99999999], [123, 678, 88888888]])  # 2D
test3 = [12345, 67890, 999999999]
test4 = [[12345, 67890, 999999999]]

print(stable_softmax(test1))
print(stable_softmax(test2))
print(stable_softmax(test3))
print(stable_softmax(test4))

 [0. 0. 1.]

[[0. 0. 1.]
 [0. 0. 1.]]

 [0. 0. 1.]

[[0. 0. 1.]]
0
David Parks

あなたの場合のように、ソフトマックス関数を計算することには何の問題もありません。問題は爆発の勾配またはトレーニング方法のこの種の問題に起因するようです。 「値をクリッピング」または「重みの適切な初期分布を選択」のいずれかでこれらの問題に焦点を当てます。

0