kakts-log

programming について調べたことを整理していきます

ソフトマックス関数についてのまとめ

前回の記事でニューラルネットワークの出力に対する活性化関数にシグモイド関数を使うことを紹介しました。 今回では、分類問題をニューラルネットワークをつかって解く際に、活性化関数としてよく使われるソフトマックス関数についてまとめます。

ソフトマックス関数とは

数式で表すと以下のとおりになります。

 {\large y_k = \frac {exp(a_k)} {\sum_{i=0}^n exp(a_i)}}

ソフトマックス関数の利点と特徴

ソフトマックス関数の特徴としては、大きく2つにわけられます。
- 出力の各要素の値域が  0 \leq y_i  \leq 1 となる
- 出力の総和が1となる

この2つのうち、2番目がニューラルネットワークで使う上で重要となります。 なぜならば、出力値の各要素の総和が1になるので、出力層の各ユニットの値を確率として解釈することができます。
これにより、問題に対して確率的アプローチを取れるようになります。

ソフトマックス関数の注意点

上記のように、ニューラルネットワークでソフトマックス関数を使うことで、出力層の各ユニットの値を確率として表現でき、統計的アプローチが取れる様になるのですが、
コンピュータ上でソフトマックス関数を使う際に注意点があります。
注意点は、ソフトマックス関数で  exp(a_i)を使うのですが、指数関数は入力値が増えると、出力の増加率も大きいので、値によっては桁数がたりずにオーバーフローしてしまいます。

どう解決するか

上記のオーバーフローの問題を解決するために、ソフトマックス関数の数式を、logと指数の性質を利用して変形していきます。

 {\large y_k = \frac {exp(a_k)} {\sum_{i=0}^n exp(a_i)}} (1)

分母と分子に定数Cをかけて
 {\large = \frac {Cexp(a_k)} {C \sum_{i=0}^n exp(a_i)}} (2)

 Cexp(x) = exp(x + logC) なので

 {\large = \frac {exp(a_k + logC)} {\sum_{i=0}^n exp(a_i + logC)}} (3)

 logC = C1として、さらに別の定数を用いて
 {\large = \frac {exp(a_k + C1)} {\sum_{i=0}^n exp(a_i + C1)}} (4)

と表すことができる。 ニューラルネットワークにおいては、上記の C1は入力信号の中で最大の値を用いるのが一般的です。
正負のどちらでも問題ないので、最大値を取得した上で、それを引くことでオーバーフローを回避できます。

import numpy as np
a = np.array([0.3, 2.9, 4.0])

def softmax_imp(a):
    # 入力値の中で最大値を取得
    c = np.max(a)
    # オーバーフロー対策として、最大値cを引く。こうすることで値が小さくなる
    exp_a = np.exp(a - c);
    sum_exp_a = np.sum(exp_a)

    y = exp_a / sum_exp_a
    return y

print(softmax_imp(a))

numpyを使えば、配列に対して最大値を求めたり、各要素に対して同じ処理を行うことが容易にできるので、かなり楽にかけます。