AI・機械学習
ソフトマックス関数とその導関数
The Softmax function and its derivative (eli.thegreenplace.net)
要約
ソフトマックス関数は、実数ベクトルを入力として受け取り、合計が1になるような(0, 1)の範囲の確率分布ベクトルを出力する関数です。この記事では、その数学的定義、機械学習における確率的解釈、そしてヤコビアン行列の導出について解説しています。さらに、計算上の数値安定性の問題とその解決策についても触れています。
全文翻訳
ソフトマックス関数は、N次元の任意の実数値ベクトルを受け取り、(0, 1)の範囲で合計が1.0になる別のN次元ベクトルを生成します。これはS(a):ℝᴺ→ℝᴺと定義され、個々の要素の計算式は以下の通りです。
Sⱼ = e^{aⱼ} / Σ_{k=1}^{N} e^{a_k} ∀ j ∈ 1..N
Sⱼは指数関数により常に正であり、分子が分母の総和の一部であるためSⱼ<1となります。例えば、ベクトル[1.0, 2.0, 3.0]は[0.09, 0.24, 0.67]に変換され、要素の相対的な順序は保たれ、合計は1.0になります。この関数は、最大値関数を「ソフト」にしたものと直感的に解釈できます。最大要素を一つだけ選ぶのではなく、ベクトルを全体(1.0)の一部に分割し、入力が最大の要素がより大きな部分を受け取りますが、他の要素もいくらか受け取ります。
確率的解釈
ソフトマックス関数の特性(出力値がすべて(0, 1)の範囲にあり、合計が1.0になる)は、機械学習で非常に役立つ確率的解釈に適しています。特に、多クラス分類タスクでは、入力が複数の出力クラスのいずれかに属する確率を割り当てたいことがよくあります。N個の出力クラスがある場合、合計が1になるN次元の確率ベクトルを探していることになります。ソフトマックス関数は以下のように解釈できます。
Sⱼ = P(y=j|a)
ここで、yは1..Nで番号付けされた出力クラス、aは任意のN次元ベクトルです。最も基本的な例は多クラスロジスティック回帰であり、入力ベクトルxは重み行列Wと乗算され、その内積の結果がソフトマックス関数にフィードされて確率が生成されます。
ベクトル微分の予備知識
ソフトマックス関数の導関数を計算する前に、ベクトル微分の予備知識から始めましょう。ソフトマックス関数は基本的にベクトル関数です。ベクトルを入力として受け取り、ベクトルを出力します。つまり、複数の入力と複数の出力があります。したがって、「ソフトマックス関数の導関数」というだけでは不十分です。具体的に、ソフトマックス関数のどの出力成分の導関数を、どの入力要素に関して計算するかを指定する必要があります。これが複雑に聞こえる場合は心配いりません。ベクトル微分の記法が開発されたのはまさにこのためです。
私たちが探しているのは偏導関数です。
∂Sᵢ / ∂aⱼ
これはi番目の出力のj番目の入力に関する偏導関数です。今後使用する短い表記法はDⱼSᵢです。ソフトマックス関数はℝᴺ→ℝᴺ関数であるため、計算する最も一般的な導関数はヤコビアン行列です。
DS = [D₁S₁ ... D<0xE2><0x82><0x99>S₁; ...; D₁S<0xE2><0x82><0x99> ... D<0xE2><0x82><0x99>S<0xE2><0x82><0x99>]
MLの文献では、「勾配」という用語が導関数の代わりによく使われます。厳密には、勾配はスカラー関数(MLの損失関数など)にのみ定義されます。ソフトマックスのようなベクトル関数について「勾配」を議論するのは不正確です。ヤコビアンはベクトル関数の完全な導関数ですが、ほとんどの場合、単に「導関数」と言うことにします。
ソフトマックス関数の導関数
任意のiとjについてDⱼSᵢを計算しましょう。
DⱼSᵢ = ∂Sᵢ / ∂aⱼ = ∂(e^{aᵢ} / Σ_{k=1}^{N} e^{a_k}) / ∂aⱼ
商の微分法則を使用します。f(x) = g(x)/h(x)の場合:
f'(x) = (g'(x)h(x) - h'(x)g(x)) / [h(x)]²
この場合、以下のようになります。
gᵢ = e^{aᵢ}
hᵢ = Σ_{k=1}^{N} e^{a_k}
hᵢの導関数をどのaⱼについて計算しても、常にe^{aⱼ}になることに注意してください。ただし、これはgᵢには当てはまりません。gᵢのaⱼに関する導関数は、i=jの場合のみe^{aⱼ}となり、それ以外の場合は0です。DⱼSᵢに戻り、まずi=jの場合から始めます。商の微分法則を使用すると、次のようになります。
∂(e^{aᵢ} / Σ_{k=1}^{N} e^{a_k}) / ∂aⱼ = (e^{aᵢ}Σ - e^{aⱼ}e^{aᵢ}) / Σ²
簡単のため、ΣはΣ_{k=1}^{N} e^{a_k}を表します。少し並べ替えると:
∂(e^{aᵢ} / Σ_{k=1}^{N} e^{a_k}) / ∂aⱼ = (e^{aᵢ}Σ - e^{aⱼ}e^{aᵢ}) / Σ²
= (e^{aᵢ} / Σ) * (Σ - e^{aⱼ}) / Σ
= Sᵢ(1 - Sⱼ)
最終的な式は、導関数をSᵢ自体で表現しています。これは指数関数を含む関数でよく見られるトリックです。同様に、i≠jの場合も計算できます。
∂(e^{aᵢ} / Σ_{k=1}^{N} e^{a_k}) / ∂aⱼ = (0 - e^{aⱼ}e^{aᵢ}) / Σ²
= - (e^{aⱼ} / Σ) * (e^{aᵢ} / Σ)
= -SⱼSᵢ
まとめると:
DⱼSᵢ = { Sᵢ(1-Sⱼ) if i=j
{ -SⱼSᵢ if i≠j
この場合分けによる明示的な分解は分かりやすいですが、数学者はより簡潔で巧妙な表現を好みます。そのため、文献では同じ方程式のさまざまな「凝縮」された定式化が見られます。最も一般的なものの一つは、クロネッカーのデルタ関数を使用することです。
δᵢⱼ = { 1 if i=j
{ 0 if i≠j
これを用いて次のように書けます。
DⱼSᵢ = Sᵢ(δᵢⱼ - Sⱼ)
これはもちろん同じことです。文献で見られる他の定式化には、ヤコビアンの行列形式を使用してδをI(単位行列)に置き換えるものや、「1」をクロネッカーデルタの代わりに、次のように使用するものがあります。DⱼSᵢ = Sᵢ(1(i=j) - Sⱼ)。ここで1(i=j)はi=jのときに値1、それ以外のときに値0を意味します。凝縮された表記法は、ソフトマックス導関数に依存するより複雑な導関数を計算したい場合に役立ちます。そうしないと、条件をすべて伝播させる必要があります。
ソフトマックスの計算と数値安定性
与えられたベクトルに対してソフトマックス関数を計算する簡単な方法は、Pythonでは次のようになります。
def softmax(x):
"""Compute the softmax of vector x."""
exps = np.exp(x)
return exps / np.sum(exps)
例として使用した3要素ベクトルで試してみましょう。
In [146]: softmax([1, 2, 3])
Out[146]: array([ 0.09003057, 0.24472847, 0.66524096])
しかし、より大きな数値(または大きな負の数)でこの関数を実行すると問題が発生します。
In [148]: softmax([1000, 2000, 3000])
Out[148]: array([ nan, nan, nan])
Numpyで使用される浮動小数点数の数値範囲には制限があります。float64の場合、表現可能な最大数値は約10³⁰⁸のオーダーです。ソフトマックス関数での指数関数計算は、かなり控えめなサイズの入力であっても、この数値を簡単に超過させる可能性があります。この問題を回避する良い方法は、入力を正規化して大きすぎたり小さすぎたりしないようにすることです。任意の定数Cを使用できることに注目してください。
Sⱼ = e^{aⱼ} / Σ_{k=1}^{N} e^{a_k} = Ce^{aⱼ} / Σ_{k=1}^{N} Ce^{a_k}
そして、定数を指数に押し込むと、次のようになります。
Sⱼ = e^{aⱼ + log(C)} / Σ_{k=1}^{N} e^{a_k + log(C)}
Cは任意の定数なので、代わりに次のように書くことができます。
Sⱼ = e^{aⱼ + D} / Σ_{k=1}^{N} e^{a_k + D}
ここでDも任意の定数です。この式は、元の式と同等です。