前記事では活性化関数について学びましたが、本記事では非線形関数やニューラルネットワークで最近活性化関数として使用されているReLU関数、そして多次元配列についてご紹介していきたいと思います。
・本サイトは以下の書籍を参考にしています。
文字だけで理解するのではなく、実際に手を動かしながら学ぶことで理解を深めることができると思います。
目次
非線形関数について
ステップ関数とシグモイド関数の共通点としては、どちらも非線形関数である点です。
ニューラルネットワークでは多層のネットワークを使っていきますが、ここで線形関数を使ってしまうと、結局隠れ層のないネットワークで実現できてしまうという点から、必ずニューラルネットワークでは活性化関数として非線形関数を使用します。
例えば、線形関数としてこのような数式があります。cは定数です。
h(x) = cx
ここで、今回はこの線形関数を4層にしてみたいと思います。
y(x) = h(h(h(h(x))))
これは、結局
y(x) = c * c * c *c* x
と同じになってしまい以下の数式1回で済んでしまします。
a = c^4
y(x) = ax
なので、ニューラルネットワークでは非線形関数を使用します。
ReLU関数について
活性化関数としてステップ関数とシグモイド関数を紹介しましたが、ニューラルネットワークの代表的な関数として、最近ではReLU( Rectified Linear Unit)関数が使われています。
ReLu関数は、入力が0の場合は0を出力し、0を超えていればそのまま値を出力していく関数になります。
ReLu関数を数式で表すと以下のようになります。
そして、pythonで実装すると以下のようなプログラムになります。
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(-1.0, 5.5) plt.show()
実行結果は下のグラフのようになり、0を超えた場合そのまま右肩上がりに伸びているのが分かると思います。
また、relu関数内で使用しているmaximum関数ですが、これは入力された値から大きい方の値を選択して出力する関数となっています。
入力値として0とxを引数としているので0を超えた場合は、常にxが出力されることになります。
多次元配列の計算
NumPyの多次元配列を使った計算ができるようになれば、今後ニューラルネットワークを学んでいく中で効率的に実装することができるようになります。
多次元配列の計算方法は割愛しますが、分からない方は以下のサイトを参考にするといいでしょう。
多次元配列を扱う理由は、単純にプログラムが書きやすくなるというメリットです。
ニューラルネットワークのように多層に渡って入力と出力が繰り返される場合、多次元配列の計算方法が分かっていればプログラムに応用させることが可能です。
また、多層とは言っても必ずしも最後には何らかの形で出力される値があります。
今までは何か重みをつけて活性化関数で内部処理をしてから次の層へ出力をしていました(これを隠れ層という)。
隠れ層では活性化関数を使用していましたが、最後に出力される際は活性化関数は使用せずに恒等関数というものを使用します。
恒等関数は、入力された値をそのまま出力する関数になります。
これは別の言い方をすると活性化関数を介さずそのまま出力するという意味になります。
実際に3層ニューラルネットワークの実装をした場合のプログラムが以下の通りになります。
# coding: utf-8 import numpy as np import matplotlib.pylab as plt def sigmoid(x): return 1 / (1 + np.exp(-x)) def init_network(): network = {} network['W1'] = np.array([[0.2, 0.4, 0.6], [0.3, 0.4, 0.5]]) network['b1'] = np.array([0.2, 0.4, 0.6]) network['W2'] = np.array([[0.4, 0.6], [0.3, 0.4], [0.5, 0.8]]) network['b2'] = np.array([0.6, 0.5]) network['W3'] = np.array([[0.2, 0.6], [0.3, 0.4, ]]) network['b3'] = np.array([0.2, 0.4]) return network def forward(network, x): W1, W2, W3 = network['W1'], network['W2'], network['W3'] b1, b2, b3 = network['b1'], network['b2'], network['b3'] a1 = np.dot(x, W1) + b1 z1 = sigmoid(a1) a2 = np.dot(z1,W2) + b2 z2 = sigmoid(a2) a3 = np.dot(z2,W3) + b3 y = a3 return y network = init_network() x = np.array([1.0, 0.5]) y = forward(network, x) print(y) ''' 出力結果 [ 0.62105554 1.23282936] '''
init_network関数と、forward関数が新しく出てきましたが、init_network関数では、重みとバイアスの初期化を行いそれらをディクショナリ型の変数networkに代入しています。
このディクショナリ型の変数には、それぞれの層で必要な重みやバイアスを格納しています。
forward関数では、入力信号が出力へと変換される部分をまとめて実装したものになります。
まとめ
ニューラルネットワークを効率的に学ぶ上で、多次元配列の計算や最近使われているReLU関数などを理解することはとても重要です。自信がない方は一度踏み止まって理解することに専念しましょう。
次回は恒等関数とソフトマックス関数の違いや手書き数字認識を実装しながら学んでいきましょう。
次回からはコンピュータが自動で手書き数字がどの数字なのか判別する部分にもなりますので、面白くなってくると思います。
斎藤 康毅「ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」(オライリージャパン,2016)
No comments yet.