ReLUレイヤの実装|Excel VBAでIris分類問題
本ページは「ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」を参考にニューラルネットワークを作成していきます。参考書籍では「Affineレイヤ」「ReLUレイヤ」「Softmax-with-Lossレイヤ」という3つのレイヤを作成し、それらを組み合わせることでニューラルネットワークを構築していきます。本サイトでも同じように3つのレイヤをVBAで作成し実装していきます。(ReLUレイヤの代わりとなるSigmoidレイヤというものも実装します)
本ページではその中の「ReLUレイヤ」をVBAで実装していきます。
ReLUレイヤの機能
ReLUレイヤは入力された値にReLU関数を適用し活性化させる機能を持ちます。
本レイヤは「順伝播」と「逆伝播」の両機能をもっているため、このレイヤを呼び出すだけで簡単にニューラルネットワークを構築することができるようになっています。
機械学習の流れ
今回作成していくニューラルネットワークをレイヤで表すと下図の通りです。
このレイヤの流れは以下のようになっています。(以下では上図の各レイヤを左から「Affine1」「ReLU」「Affine2」「Softmax-with-Loss」として説明していきます)
1. 入力値(4つの数字)を「Affine1」に入れ、入力値と重みの総和を計算する。
2.「Affine1」の出力を「ReLU」に入れて活性化させる。(Sigmoidにも変更可能)
3.「ReLU」の出力を「Affine2」に入れ、入力値と重みの総和を計算する。
4.「Affine2」の出力を「Softmax-with-Loss」に入れ、活性化の結果/損失関数を求める。
5.「Softmax-with-Loss」から「Affine2」に逆伝播(Affine2のパラメータを更新)
6.「Affine2」から「ReLU」に逆伝播
7.「ReLU」から「Affine1」に逆伝播(Affine1のパラメータを更新)
上記の1~7を数百回ループすることで「Affine1」と「Affine2」が持つパラメータ(重み/バイアス)が正解に近づくように徐々に更新されていきます。あとは損失関数をもとにループを止めれば学習終了といった流れになってます。
ReLUレイヤの実装
まずはクラスモジュールで「ReLU_Layer」というモジュールを作成します。
ReLUレイヤでは逆伝播時に「順伝播時の入力値」が必要になるため、値を保存することのできるクラスモジュールで作成します。
また「Option Base 1」を使い配列を「1」スタートにします。
これは要素数とインデックスを合わせ配列同士の計算をわかりやすくするためです。
以下はReLUレイヤの全コードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
Option Explicit Option Base 1 Dim mask() As Boolean '―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Public Function forward(ByRef x() As Double) As Double() Dim i As Long Dim out() As Double ReDim out(UBound(x)) ReDim mask(UBound(x)) For i = 1 To UBound(x) If x(i) <= 0 Then out(i) = 0 mask(i) = True Else out(i) = x(i) mask(i) = False End If Next i forward = out End Function '―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Public Function backward(ByRef dout() As Double) As Double() Dim i As Long Dim dx() As Double ReDim dx(UBound(dout)) For i = 1 To UBound(dout) If mask(i) = True Then dx(i) = 0 Else dx(i) = dout(i) End If Next i backward = dx End Function |
以下では各関数で何を行っているかを解説していきます。
Function forward
関数「forward」は名前のとおり順伝播を行う関数です。
順伝播といっても、入力された値に対してReLU関数で活性化させるだけです。
(図でいうと「a1」を「z1」に変換する部分)
ReLU関数は入力値が0以下の場合は0を、0より大きい場合は入力値をそのまま返すという非常にシンプルな関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Dim mask() As Boolean '―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Public Function forward(ByRef x() As Double) As Double() Dim i As Long Dim out() As Double ReDim out(UBound(x)) ReDim mask(UBound(x)) For i = 1 To UBound(x) If x(i) <= 0 Then out(i) = 0 mask(i) = True Else out(i) = x(i) mask(i) = False End If Next i forward = out End Function |
この関数の引数としては「x()」があります。
これはAffineレイヤからの出力値が入った1次元配列です。
(先ほどの図でいう「a1」)
ループを使ってこの引数の配列の要素1つずつにReLU関数を適応させていきます。
また、順伝播時の入力「x()」の大きさによって逆伝播する値が変化します。
そのためここではReLU関数を適応させるとともに「mask()」というBoolean型の配列を使って、「x()」が0以下の場合は「True」、0より大きい場合は「False」として保存しています。たとえば「x()」の中身が、x(1,-1,0,2,3,-5,4,-1,3,6)の場合は「mask()」の中身はmask(F,T,T,F,F,T,F,T,F,F)となります。(T=True,F=False)
返り値は「x()」にReLU関数を適応させた1次元配列です。
「mask()」は逆伝播時に使用します。
Function backward
関数「backward」は名前のとおり逆伝播を行う関数です。
ReLU関数の逆伝播は順伝播時の入力値が0以下の場合は0を、0より大きい場合は前の層から逆伝播してきた「微分の値」をそのまま返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Public Function backward(ByRef dout() As Double) As Double() Dim i As Long Dim dx() As Double ReDim dx(UBound(dout)) For i = 1 To UBound(dout) If mask(i) = True Then dx(i) = 0 Else dx(i) = dout(i) End If Next i backward = dx End Function |
この関数の引数としては「dout()」があります。
これは逆伝播でいう前の層の関数「backward」で求めた「微分の値」が入った配列です。
この「微分の値」をすべての層に逆伝播していくことでAffineレイヤにある重み/バイアスパラメータを徐々に正解に近づけていくことができます。(Affineレイヤではこの逆伝播された「微分の値」を使うことでパラメータを更新していきます)
ReLU関数の逆伝播では、順伝播同様ループを使って「dout()」の要素を1つ1つチェックしていきます。「mask()」がTrueの場合(順伝播での入力値が0以下の場合)は要素の値は0に、「mask()」がFalseの場合(順伝播での入力値が0より大きい場合)は要素の値はそのままにします。
これら逆伝播のReLU関数を適用させた値は1次元配列の「dx()」に格納し、その配列を戻り値として返します。この「微分の値」が入った配列が次の層に渡されることで逆伝播が連鎖していきます。
まとめ
ここでは「ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」を参考にVBAでReLUレイヤの実装を行いました。
今回実装したReLUレイヤの機能をまとめると以下の通りです。
Function forward:順伝播(ReLU関数を適用する)
Function backward:逆伝播(逆伝播でのReLU関数を適用する)
ReLUレイヤは比較的シンプルな内容なので、何をやっているかはすぐに理解できると思います。
活性化関数なので入力された値を変化させるだけなので配列の”かたち”は変化しません。
つまり要素数が10の1次元配列「x()」が入力された場合は、活性化された要素数10の1次元配列が出力されるということです。
今回はReLU関数を使っていますがこの中身をSigmoid関数に変えればSigmoid関数を適用させることもできます。次回はそんなSigmoid関数を適用させるためのSigmoidレイヤの実装です。
【次回】Sigmoidレイヤの実装
【前回】Affineレイヤの実装
メインページ