282

Udacity のディープ ラーニング クラスから、y_i のソフトマックスは単純に指数を Y ベクトル全体の指数の和で割ったものです。

ここに画像の説明を入力

S(y_i)のsoftmax関数はどこにy_iあり、eは指数関数であり、はいjいえです。入力ベクトル Y の列数。

私は次のことを試しました:

import numpy as np

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

scores = [3.0, 1.0, 0.2]
print(softmax(scores))

戻り値:

[ 0.8360188   0.11314284  0.05083836]

しかし、提案された解決策は次のとおりです。

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    return np.exp(x) / np.sum(np.exp(x), axis=0)

最初の実装は各列と最大値の差を明示的に取り、合計で除算しますが、最初の実装と同じ出力を生成します。

誰かが数学的に理由を示すことができますか? 一方が正しく、もう一方が間違っていますか?

コードと時間の複雑さの点で実装は似ていますか? どちらがより効率的ですか?

4

25 に答える 25

165

どちらも正しいですが、数値安定性の観点からはあなたの方が好まれます。

あなたはから始めます

e ^ (x - max(x)) / sum(e^(x - max(x))

a^(b - c) = (a^b)/(a^c) という事実を利用することで、

= e ^ x / (e ^ max(x) * sum(e ^ x / e ^ max(x)))

= e ^ x / sum(e ^ x)

これは、他の答えが言うことです。max(x) を任意の変数に置き換えると、キャンセルされます。

于 2016-01-23T22:00:06.840 に答える
133

(まあ...質問と回答の両方で、ここでは多くの混乱があります...)

まず、2 つの解決策 (つまり、あなたの解決策と提案された解決策) は同等ではありません。それらはたまたま、1 次元スコア配列の特殊なケースでのみ同等です。Udacity クイズで提供された例で 2 次元スコア配列も試していれば、それを発見できたでしょう。

結果に関しては、2 つのソリューションの唯一の実際の違いはaxis=0引数です。your_softmaxこれが事実であることを確認するために、ソリューション ( ) と、唯一の違いがaxis引数であるソリューションを試してみましょう。

import numpy as np

# your solution:
def your_softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

# correct solution:
def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0) # only difference

前述したように、1 次元のスコア配列の場合、結果はまったく同じです。

scores = [3.0, 1.0, 0.2]
print(your_softmax(scores))
# [ 0.8360188   0.11314284  0.05083836]
print(softmax(scores))
# [ 0.8360188   0.11314284  0.05083836]
your_softmax(scores) == softmax(scores)
# array([ True,  True,  True], dtype=bool)

それにもかかわらず、テスト例として Udacity クイズで与えられた 2-D スコア配列の結果は次のとおりです。

scores2D = np.array([[1, 2, 3, 6],
                     [2, 4, 5, 6],
                     [3, 8, 7, 6]])

print(your_softmax(scores2D))
# [[  4.89907947e-04   1.33170787e-03   3.61995731e-03   7.27087861e-02]
#  [  1.33170787e-03   9.84006416e-03   2.67480676e-02   7.27087861e-02]
#  [  3.61995731e-03   5.37249300e-01   1.97642972e-01   7.27087861e-02]]

print(softmax(scores2D))
# [[ 0.09003057  0.00242826  0.01587624  0.33333333]
#  [ 0.24472847  0.01794253  0.11731043  0.33333333]
#  [ 0.66524096  0.97962921  0.86681333  0.33333333]]

結果は異なります。2 番目の結果は、Udacity クイズで予想される結果とまったく同じで、すべての列の合計が実際に 1 になります。これは、最初の (間違った) 結果には当てはまりません。

したがって、すべての騒ぎは実際には実装の詳細、つまりaxis引数に関するものでした。numpy.sum ドキュメントによると:

デフォルトの axis=None は、入力配列のすべての要素を合計します

ここでは行ごとに合計したいので、axis=0. 1次元配列の場合、(唯一の)行の合計とすべての要素の合計はたまたま同じになるため、その場合は同じ結果になります...

問題はaxisさておき、実装 (つまり、最初に最大値を差し引くという選択) は、実際には提案されたソリューションよりも優れています! 実際、これはsoftmax関数を実装するための推奨される方法です-正当化についてはこちらを参照してください(数値安定性、ここの他の回答でも指摘されています)。

于 2016-07-07T15:56:15.927 に答える
65

だから、これは本当にデザートノートの答えへのコメントですが、私の評判のためにまだコメントすることはできません. 彼が指摘したように、あなたのバージョンは、入力が単一のサンプルで構成されている場合にのみ正しいです。入力が複数のサンプルで構成されている場合、それは誤りです。ただし、デザートノートの解決策も間違っています。問題は、一度 1 次元の入力を取り、次に 2 次元の入力を取ることです。これをお見せしましょう。

import numpy as np

# your solution:
def your_softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

# desertnaut solution (copied from his answer): 
def desertnaut_softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0) # only difference

# my (correct) solution:
def softmax(z):
    assert len(z.shape) == 2
    s = np.max(z, axis=1)
    s = s[:, np.newaxis] # necessary step to do broadcasting
    e_x = np.exp(z - s)
    div = np.sum(e_x, axis=1)
    div = div[:, np.newaxis] # dito
    return e_x / div

デザートノートの例を見てみましょう:

x1 = np.array([[1, 2, 3, 6]]) # notice that we put the data into 2 dimensions(!)

これは出力です:

your_softmax(x1)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

desertnaut_softmax(x1)
array([[ 1.,  1.,  1.,  1.]])

softmax(x1)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

この状況では、desernauts バージョンが失敗することがわかります。(入力が np.array([1, 2, 3, 6] のように 1 次元のみの場合) ではありません)。

これが 2 次元入力を使用する理由なので、3 つのサンプルを使用してみましょう。次の x2 は、desernauts の例のものと同じではありません。

x2 = np.array([[1, 2, 3, 6],  # sample 1
               [2, 4, 5, 6],  # sample 2
               [1, 2, 3, 6]]) # sample 1 again(!)

この入力は、3 つのサンプルを含むバッチで構成されています。しかし、サンプル 1 と 3 は本質的に同じです。これで、3 行目のソフトマックス アクティベーションが期待されます。最初の行は 3 番目の行と同じで、x1 のアクティベーションと同じである必要があります。

your_softmax(x2)
array([[ 0.00183535,  0.00498899,  0.01356148,  0.27238963],
       [ 0.00498899,  0.03686393,  0.10020655,  0.27238963],
       [ 0.00183535,  0.00498899,  0.01356148,  0.27238963]])


desertnaut_softmax(x2)
array([[ 0.21194156,  0.10650698,  0.10650698,  0.33333333],
       [ 0.57611688,  0.78698604,  0.78698604,  0.33333333],
       [ 0.21194156,  0.10650698,  0.10650698,  0.33333333]])

softmax(x2)
array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037047],
       [ 0.01203764,  0.08894682,  0.24178252,  0.65723302],
       [ 0.00626879,  0.01704033,  0.04632042,  0.93037047]])

これが私のソリューションの場合にのみ当てはまることを理解していただければ幸いです。

softmax(x1) == softmax(x2)[0]
array([[ True,  True,  True,  True]], dtype=bool)

softmax(x1) == softmax(x2)[2]
array([[ True,  True,  True,  True]], dtype=bool)

さらに、TensorFlows ソフトマックス実装の結果は次のとおりです。

import tensorflow as tf
import numpy as np
batch = np.asarray([[1,2,3,6],[2,4,5,6],[1,2,3,6]])
x = tf.placeholder(tf.float32, shape=[None, 4])
y = tf.nn.softmax(x)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(y, feed_dict={x: batch})

そして結果:

array([[ 0.00626879,  0.01704033,  0.04632042,  0.93037045],
       [ 0.01203764,  0.08894681,  0.24178252,  0.657233  ],
       [ 0.00626879,  0.01704033,  0.04632042,  0.93037045]], dtype=float32)
于 2016-09-18T13:44:53.457 に答える
41

どちらも数学的には正しいが、実装に関しては、最初の方が優れていると言えます。ソフトマックスを計算すると、中間値が非常に大きくなる場合があります。2 つの大きな数の除算は、数値的に不安定になる可能性があります。これらのメモ(スタンフォードから) は、本質的にあなたがしていることである正規化のトリックについて言及しています。

于 2016-02-08T18:13:54.223 に答える
17

数学的な観点からは、両辺は等しいです。

そして、これを簡単に証明できます。しましょうm=max(x)。これで、関数softmaxは i 番目の座標が等しいベクトルを返します

ここに画像の説明を入力

mすべての(複素数であっても)数値であるため、これは任意の に対して機能することに注意してくださいe^m != 0

  • 計算の複雑さの観点からは、それらは同等であり、両方ともO(n)時間内に実行されます。ここnで、 はベクトルのサイズです。

  • 数値安定性の観点からは、最初の解が推奨されます。これは、e^xが非常に速く成長し、かなり小さい値でxもオーバーフローするためです。最大値を減算すると、このオーバーフローを取り除くことができます。私が話していたことを実際に体験するには、x = np.array([1000, 5])両方の機能に取り入れてみてください。1つは正しい確率を返し、2つ目はオーバーフローしますnan

  • ソリューションはベクトルに対してのみ機能します (Udacity クイズでは、行列に対しても計算する必要があります)。それを修正するには、使用する必要がありますsum(axis=0)

于 2016-11-23T05:51:30.207 に答える
10

ここで、彼らが を使用した理由を知ることができます- max

そこから:

「実際に Softmax 関数を計算するためのコードを作成している場合、指数関数のために中間項が非常に大きくなる可能性があります。大きな数の除算は数値的に不安定になる可能性があるため、正規化のトリックを使用することが重要です。」

于 2016-06-29T19:09:17.497 に答える
3

私はこれを提案します:

def softmax(z):
    z_norm=np.exp(z-np.max(z,axis=0,keepdims=True))
    return(np.divide(z_norm,np.sum(z_norm,axis=0,keepdims=True)))

バッチだけでなくストキャスティクスでも機能します。
詳細については、 https ://medium.com/@ravish1729/analysis-of-softmax-function-ad058d6a564d を参照してください。

于 2018-08-18T09:44:16.347 に答える
1

数値の安定性を維持するために、max(x) を減算する必要があります。以下は softmax 関数のコードです。

デフソフトマックス(x):

if len(x.shape) > 1:
    tmp = np.max(x, axis = 1)
    x -= tmp.reshape((x.shape[0], 1))
    x = np.exp(x)
    tmp = np.sum(x, axis = 1)
    x /= tmp.reshape((x.shape[0], 1))
else:
    tmp = np.max(x)
    x -= tmp
    x = np.exp(x)
    tmp = np.sum(x)
    x /= tmp


return x
于 2016-11-06T15:52:37.300 に答える
1

誰もが解決策を投稿しているように見えるので、私のものを投稿します:

def softmax(x):
    e_x = np.exp(x.T - np.max(x, axis = -1))
    return (e_x / e_x.sum(axis=0)).T

sklearn からインポートしたものとまったく同じ結果が得られます。

from sklearn.utils.extmath import softmax
于 2018-10-17T04:25:11.630 に答える