ゼロから作るDeepLearning1


3章【ニューラルネットワーク 〜NNの基礎と活性化関数〜】

Introduction

環境はGoogle Colaboratoryを用いています。

そのためデータセットをGoogleDriveの中のMyDrive配置していることが前提になります。
必要なデータセットはここからダウンロード or Clone してください。

前章までではANDやORなどの簡単な線形関数を組み合わせることで(層を重ねることで)複雑な非線形識別関数(XOR)を実装可能なことを見ました。
しかしこの方法の非効率的なポイントとして重みパラメータを自分でいちいち設定する必要があることが挙げられます。

そこで今回はパラメータ調整を自動で行えるように設定された多重層の線形合成関数(いわゆるニューラルネット)について学習していきます。

ニューラルネットは上記のように入力層、中間層、出力層からなる、線形関数を多層化した関数のことです。
前章でみてきたように単純な関数でも組み合わせるとコンピュータのような複雑な演算が可能なものが完成するのでその威力は期待できそうです。

ニューラルネットは数学的に「入力が与えられたら期待する出力を出すような関数を求める」機械学習の世界において線形関数と活性化関数を多重に合成した(人間の脳を真似て作成した)形の関数のことと認識しています。

パーセプトロンの復習

上記の図は二つの信号を受け取ってyを出力するパーセプトロンです。
これを数式で表現すると以下のようになります。

y = \left\{
\begin{array}{ll}

0 & (b+w_1x_1+w_2x_2 \leq 0) \\
1 & (b+w_1x_1+w_2x_2 > 0)

\end{array}
\right.

bのことをバイアス(人によっては重み)と呼ぶのでした。
これはニューロンの発火(値が1となるか0となるか)しやすさを決定する変数であったので、通常重みとは少しニュアンスが異なる場合が多そうです。

ここでこの式をよりシンプルに

h(x) = \left\{
\begin{array}{ll}
0 & (x \leq 0) \\
1 & (x > 0 )
\end{array}
\right.

を用いて

y = h(b+w_1x_1+w_2x_2)

と表示することができます。このように入力信号$(b+w_1x_1+w_2x_2)$を関数によって変換して出力するものとして捉えることができます。この考え方を進めたものが次に出てくる活性化関数です。

活性化関数

活性化関数として様々なものが種類としてあるそうですが、ここでは代表的なもののみ学習します。

ステップ関数

パーセプトロンの実装で用いた、ガウス関数のような階段状のグラフを描く関数のことです。
以下で実際にPythonを用いて実装してみます。

def step_function(x)
    if x > 0:
        return 1
    else:
        return 0

この実装だとNumpyのような多次元配列を引数に取ることはできません。
そこで以下のように実装を修正しましょう。

def step_function(x):
    y = x > 0
    return y.astype(np.int)

ここでは省略されていますが、二つのことが起こっています。
一つ目はy = x > 0という評価で配列xが全てTrue、Falseのみを引数にもつ配列に変換されています。
二つ目にはastype()というメソッドで引数にとった希望の型に配列を変換しています。デフォルトではTrueは1に、Falseは2に変換されて、結果としては条件を満たして発火した出力ニューロンの部分のみ1になる配列が出力されます。

以下でステップ関数のグラフを作成してみましょう。
通常グラフ作成や描画などはmatplotlibというライブラリを用いて行います。

import numpy as np
import matplotlib.pylab as plt

def step_function(x):
    y = x > 0
    return y.astype(np.int)

x = np.arange(-5.0,5.0,0.1)
y = step_function(x)

plt.plot(x,y)
plt.ylim(-0.1,1.1) #y軸の範囲を指定している
plt.show()

コードの解説をしておくと、np.arange()では-5.0から0.5までの間を0.1区切りにした要素を持つ配列を表示しています。
その他matplotlibのコードについてはある程度の描画のための決まり文句です。
図は以下のように出力されます。

シグモイド関数

続いて少し複雑なシグモイド関数という活性化関数を考えます。
シグモイド関数は以下のように実装されます。

import numpy as np
def sigmoid(x):
    return 1/(1+np.exp(-x))

注意点として、このシグモイド関数も階段関数と同様に配列を引数にとる関数であることに注意しましょう。
つまり配列の各要素にシグモイド関数を作用させたものとして捉えられます。

このコードには後述するNumpyのブロードキャスト機能が関わってきます。
ブロードキャスト機能とは簡単にいうと配列とスカラーの演算結果が、配列の各要素にスカラーを演算させたものと同じになるという機能です。

では今回もシグモイド関数をグラフにしてみましょう。

コードとグラフは以下のようになります。

import numpy as np
import matplotlib.pylab as plt

def sigmoid(x):
    return 1/(1+np.exp(-x))

x = np.arange(-5.0,5.0,0.1)
y = sigmoid(x)

plt.plot(x,y)
plt.ylim(-0.1,1.1) #y軸の範囲を指定している
plt.show()

このシグモイド関数が先ほどの階段関数と大きく異なる点として、返り値が離散的でないこと、グラフが滑らか(数学的には微分可能)ということが挙げられます。この性質が後ほどわかるように大きな意味を持ってきます。

しかし両者の重要な共通点として非線形な関数であることが挙げられます。この非線形性がニューラルネットにとってとても重要である。
なぜなら仮に線形な活性化関数を用いてしまうと、線形関数の合成は線形関数であるので、全体として線形関数になってしまいます。これでは単一の線形関数と変わらず、層を重ねる意味が無くなってしまします。

ReLu関数

近年よく用いられている人気の活性化関数がReLuです。
これはシグモイドや階段関数ににている非線形関数ですが、関数としてはとても単純です。
そのため実装も以下のように簡単に行うことができます。

def relu(x)
    return np.maximum(0,x)

上記コードのnp.maximumは0とxの配列の各要素を比べています。
つまりは配列の各要素に線形にReLu関数を実行していることになります。

またグラフもシンプルで以下のように実装、描画できます。

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.ylim(-0.1,1.1) #y軸の範囲を指定している
plt.show()

おわり

今回はQitaの練習も兼ねてここまでにします!
次回は多次元配列の計算から書いていきます。