まえがき
一応、前回のパーセプトロンの記事を読んでいる前提で説明します。
前回の単純パーセプトロンでは以下のように出力yを計算しました。
y = \begin{cases}
0 & (b + w_1w_1 + w_2 x_2 \leq 0)\\
1 & (b + w_1w_1 + w_2 x_2 > 0)
\end{cases}
これを以下の3式に書き換えてみましょう。
$$ y = h(a) $$
$$ a = b + w_1x_1 + w_2x_2 $$
h(a) = \begin{cases}
0 & (a \leq 0)\\
1 & (a > 0)
\end{cases}
3つに分解されただけで、やっていることは現状同じです。
とにかくこういう処理で単層の計算を行っているわけです。
ここで肝心なのはaをyに変換するh(a)です。今は
h(a) = \begin{cases}
0 & (a \leq 0)\\
1 & (a > 0)
\end{cases}
を関数としていますが、これを別の関数に置き換えれば別の出力が得られるはずですね。この種類をいくつか見てみましょう。
どれも$y = ax$みたいな簡単な式(線形関数)ではありません。そもそも線形関数ならわざわざ重ねなくても一回で計算できるし。
ステップ関数
h(a) = \begin{cases}
0 & (a \leq 0)\\
1 & (a > 0)
\end{cases}
これ。
実装してみよう
単純に条件を入力してみるとこうですね。
def step_function(x):
if x > 0:
return 1
else:
return 0
$h(a)$自体はできているんですが、これではnumpy配列を扱えません。
以下のように書いてみます。
import numpy as np
def step_function(x):
return np.array(x > 0, dtype = int)
これは、$x>0$を満たしていたらTrue,満たしていなかったらFalseの配列を返す…のをさらに、int型に変換することで$x > 0$なら1、満たしていないなら0、という配列に変換しています。
グラフで表してみます。
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype = int)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
シグモイド関数
以下の式で表します。
$$ h(x) = {1\over {1 + \mathrm{exp}(-x)}} $$
特徴として、$\infty > x > -\infty$の範囲で$1 > y > 0$の範囲をとること。$x = 0$で$y = 0.5$。
実装してみよう
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
これでシグモイド関数をあらわすことができます。
グラフにしてみましょう。
import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.arange(-10.0, 10.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.show()
こんな感じです。すごくおおざっぱに言えば、なめらかなステップ関数といったような形をしています。
LeRu関数
以下の式で表します。
h(x) = \begin{cases}
x & (x > 0)\\
0 & (x \leq 0)
\end{cases}
ステップ関数で1だった所がxになってます。
実装してみよう
import numpy as np
def relu(x):
return np.maximum(0, x) # 0かxか大きい方を返す
ではグラフにしてみます。
import numpy as np
import matplotlib.pylab as plt
def relu(x):
return np.maximum(0, x)
x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.show()
こうなりました。
ソフトマックス関数
主に出力層に用いります。
これは複数のaを利用してyを求める関数です。
$$ h(a_ k) ={ \mathrm{exp}(a_k)\over \sum^n _ {i = 1}\mathrm{exp}(a_i)}$$
証明は省略しますが、このように変形できます。
$$ y_ k ={ \mathrm{exp}(a_k + C)\over \sum^n _ {i = 1}\mathrm{exp}(a_i + C) }$$
これは数値範囲をオーバーしないためにする変形です。aに10000とか入っててもオーバーフローしないためにこの変形をする必要がある。
実装してみよう
import numpy as np
def softmax(a):
c = np.max(a)
exp_a = np.exp(a - c) # これで必ずa-c <= 0。オーバーフローしなくなる。
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
出力してみましょう。
a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
print(y) # [0.01821127 0.24519181 0.73659691]
はいここで出力yに注目してください。
[0.01821127 0.24519181 0.73659691]の三つ。合計すると1になります。
つまりこれは与えられた三つの数字の大きさを比較し、「確率」に変換する関数といえます。
恒等関数
h(a) = a
つまりそのまま。これも出力に用います。ソフトマックス関数使わずにこれでもそんなに問題ない。
実装してみよう
いる?
まとめ
多数のxの入力をまとめてaを作り、それを活性化関数$h(a)$で変換する。これを繰り返して目的の出力を得るのがニューラルネットワークです。$h(a)$にはいろいろ種類があります。ここで触れた以外にもたくさんあります。
最後の二つのソフトマックス関数、恒等関数はどちらかというと出力用の関数。ちょっとジャンルが違います。
『ゼロから作るDeepLearning - Pythonで学ぶディープラーニングの理論と実装』を参考にしました。
前回:パーセプトロン