概要
ニューラルネットワークで使われる活性化関数の役割を、ステップ関数とシグモイド関数を中心に整理する。
活性化関数は、入力信号の合計をそのまま次へ渡すのではなく、出力の形を変えるための関数となる。
ここでは、パーセプトロンとのつながりを確認しながら、なぜ活性化関数が必要になるのかを実装例で見る。
この記事の構成
- パーセプトロンと活性化関数
パーセプトロンと活性化関数の仕組みと要点を具体例から整理。 - ニューラルネットワークと活性化関数(シグモイド関数)
ニューラルネットワークでシグモイド関数を使う考え方を整理。 - ステップ関数の実装サンプル
ステップ関数の実装サンプルをコードや具体例で確認。 - シグモイド関数の実装サンプル
シグモイド関数の実装サンプルをコードや具体例で確認。 - ステップ関数とシグモイド関数の違い
ステップ関数とシグモイド関数の違いを対比し、それぞれの特徴を整理。
概念の説明と実装サンプル
パーセプトロンと活性化関数
- 活性化関数の役割
活性化関数(activation function)は、伝達関数(transfer function)とも呼ばれ、入力値の総和を出力に変換する関数のことを言い、入力値がどのように発火するか(活性化するか・伝達されるか)を決定する役割を持つ。
前の記事 > 多層パーセプトロンの概念と実装サンプル で触れた入力値が2つあるパーセプトロン\((A)\)は、ステップ関数を活性化関数として用いるモデルである。一般的な活性化関数の表現に書き換えると右辺を \(a = b + x_{1}w_{1} + x_{2}w_{2} \) と置き、\((B)\) 、\(y\) を \(h(a)\) で表現した\((C)\)のように表せる。
\[ {\small y = \begin{cases} 0 \hspace{5pt}\text{if}\hspace{5pt}b + x_{1}w_{1} + x_{2}w_{2} \leqq 0 \\ 1 \hspace{5pt}\text{if}\hspace{5pt}b + x_{1}w_{1} + x_{2}w_{2} > 0 \end{cases}\hspace{5mm}・・・(A) } \]\(a = b + x_{1}w_{1} + x_{2}w_{2} \) と置き
\[ {\small y = \begin{cases} 0 \hspace{5pt}\text{if}\hspace{5pt}a \leqq 0 \\ 1 \hspace{5pt}\text{if}\hspace{5pt}a > 0 \end{cases}\hspace{5mm}・・・(B) } \]\(y\) = \(h(a)\) とすると
\[ {\small y = h(b + x_{1}w_{1} + x_{2}w_{2}) } \]\[ {\small h(a) = \begin{cases} 0 \hspace{5pt}\text{if}\hspace{5pt}a \leqq 0 \\ 1 \hspace{5pt}\text{if}\hspace{5pt}a > 0 \end{cases}\hspace{5mm}・・・(C) } \]となり、入力値の総和を \(h(a)\) によって変換し、出力値である \(y\) となることを表わしている。
※ \((C)\) のイメージ
図の丸は処理単位となるニューロン(またはノード)を表し、\(a\)、\(h(a)\)、\(y\) はその途中の値や関数を表す。
上記のとおり、\((C)\)で表される活性化関数は、バイアスを境に出力が切り替わる関数となっているが、このような関数をステップ関数または階段関数という。
従って、このパーセプトロンは活性化関数にステップ関数を用いて表現される。
パーセプトロンにも重みを更新する学習則はあるが、ステップ関数はほとんどの点で微分が\(0\)となるため、勾配を逆伝播して多層ネットワークを学習する方法には適さない。シグモイド関数のように微分可能な活性化関数を使うと、損失の勾配に基づいて各層の重みを更新できる。
ニューラルネットワークと活性化関数(シグモイド関数)
- シグモイド関数を使う考え方
ニューラルネットワークで使用される活性化関数の一つにシグモイド関数(sigmoid function)がある。
パーセプトロンもニューラルネットワークも入力値を関数で変換して出力するが、両者の違いは活性化関数だけではない。層の構成、学習方法、損失関数なども含めてモデルが決まる。ここでは、そのうち活性化関数の違いに注目する。
ここで深く掘り下げないが一般的なシグモイド関数は、下記\((D)\)で表される実関数を指している。
\[ h(x) = \frac{1}{1+e^{-ax}}\hspace{5mm}・・・(D) \]\(a\) は、ゲイン(増幅値)と呼ばれ、\(a = 1\) とした下記\((E)\)は、神経細胞が持つ性質をモデル化したものとして用いられており、標準シグモイド関数という。
\[ h(x) = \frac{1}{1+e^{-x}}\hspace{5mm}・・・(E) \]※ \(e=2.718281828…\) は、自然対数の底となる無理数で、ネイピア数という。
以降では、ステップ関数とシグモイド関数の実装サンプルを比較しながら、それぞれの違いを確認する。
ステップ関数の実装サンプル
- ステップ関数の定義と確認
下記(*1)は、\(x>0\) を基準にそれが真であれば、\(1\)、そうでなければ \(0\) を出力するステップ関数の実装サンプル。
$ python >>> import numpy as np >>> def step_func(x): # (*1) ステップ関数の定義 ... y = x > 0 ... return y.astype(int) ...上記引数の \(x\) を(*2)のNumPy配列で定義する。
$ python >>> x = np.array([-0.5, 0.5, 1.5]) # (*2) NumPy配列の定義 >>> x # (*3)NumPy配列の確認 array([-0.5, 0.5, 1.5])ここで(*4)のとおり、\(x > 0\)を評価するとbool値のNumPy配列が返される。
$ python >>> x > 0 # (*4) NumPy配列をbool値で表示 array([False, True, True])さらに(*5)でこの \(x > 0\) をastypeでint変換している。
$ python >>> (x > 0).astype(int) # (*5) NumPy配列のbool値を 0:false、1:true で表示 array([0, 1, 1]) >>>よって、(*2)の引数 \(x = [-0.5, 0.5, 1.5]\) が戻り値となる(*5)のNumPy配列が \(x = [0, 1, 1]\) で出力されている。
さらに、(*1)を numpy.array の表現に書き換えると(*6)のように \(1\) STEPで表現することができる。$ python >>> import numpy as np >>> import matplotlib.pylab as plt >>> def step_func(x): # (*6) ステップ関数の定義 ... return np.array(x > 0, dtype=int) ...上記(*6)のグラフ出力
>>> x = np.arange(-5.0, 5.0, 0.1) # 区間を-5~5まで、描画間隔を0.1刻みに設定 >>> y = step_func(x) # (*6) ステップ関数をコール >>> plt.title("step_func\n# arange:-5.0, 5.0, 0.1, xlabel:x, ylabel:y") # グラフタイトルを設定 Text(0.5, 1.0, 'step_func\n# arange:-5.0, 5.0, 0.1, xlabel:x, ylabel:y') >>> plt.ylim(-0.1, 1.1) # y軸の範囲を設定 (-0.1, 1.1) >>> plt.xlabel("x") # x軸のラベルを設定 Text(0.5, 0, 'x') >>> plt.ylabel("y") # y軸のラベルを設定 Text(0, 0.5, 'y') >>> plt.plot(x, y) # グラフの描画 [<matplotlib.lines.Line2D object at 0x7f7e0a48e2b0>] >>> plt.savefig('pid17_2.png') # グラフの出力
上記グラフの通り \(x = 0\) を境に \(y\) が \(0\) から \(1\) に切り替わっており、階段状になっていることからステップ関数は、階段関数とも呼ばれている。
シグモイド関数の実装サンプル
- シグモイド関数の定義と確認
上記、シグモイド関数\((E)\)の \(e^{-x}\) をNumPyで書き換えるだけで良く、下記 \(1\) STEPのみ。
$ python >>> import numpy as np >>> import matplotlib.pylab as plt >>> def sigmoid_func(x): # (*7) シグモイド関数の定義 ... return 1 / (1 + np.exp(-x)) # 自然対数の底 (e) の -x 乗 ...引数に関しては、ステップ関数と同様にNumPy配列を渡すとNumPy配列で出力される。
>>> x = np.array([-3.0, -2.0, -1.0, 0, 1.0, 2.0, 3.0]) >>> sigmoid_func(x) array([0.04742587, 0.11920292, 0.26894142, 0.5, 0.73105858, 0.88079708, 0.95257413]) >>>上記(*7)のグラフ出力
>>> x = np.arange(-5.0, 5.0, 0.1) # 区間を-5~5まで、描画間隔を0.1刻みに設定 >>> y = sigmoid_func(x) # (*7) シグモイド関数をコール >>> plt.title("sigmoid_func\n# arange:-5.0, 5.0, 0.1, xlabel:x, ylabel:y") # グラフタイトルを設定 Text(0.5, 1.0, 'sigmoid_func\n# arange:-5.0, 5.0, 0.1, xlabel:x, ylabel:y') >>> plt.ylim(-0.1, 1.1) # y軸の範囲を設定 (-0.1, 1.1) >>> plt.xlabel("x") # x軸のラベルを設定 Text(0.5, 0, 'x') >>> plt.ylabel("y") # y軸のラベルを設定 Text(0, 0.5, 'y') >>> plt.plot(x, y) [<matplotlib.lines.Line2D object at 0x7ff62b64fcf8>] >>> plt.savefig('pid17_3.png') # グラフの出力
ステップ関数とシグモイド関数の違い
- 出力特性の比較
ステップ関数は、\(y = 0\) or \(y = 1\) のみの出力なのに対してシグモイド関数の出力 \(y\) は、連続的な実数を出力しており、滑らかな曲線を描いている。
これは、ニューラルネットワークのシグモイド関数がパーセプトロンのステップ関数では表現できない柔軟性や複雑な処理を表現できることを意味し、自動学習においても重要な役割を果たしている。
まとめ
- 活性化関数はニューロンの出力を変換し、損失関数は予測の悪さを測るため、役割が異なる。
- ステップ関数は0と1が不連続に切り替わり、シグモイド関数は滑らかな出力を返す。
- 線形変換だけを重ねても全体は線形のままだが、非線形な活性化関数を使うと複雑な関係を表現しやすくなる。