Softmax-with-Lossレイヤの実装|Excel VBAでMNIST機械学習
本ページは「ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」を参考にニューラルネットワークを作成していきます。参考書籍では「Affineレイヤ」「ReLUレイヤ」「Softmax-with-Lossレイヤ」という3つのレイヤを作成し、それらを組み合わせることでニューラルネットワークを構築していきます。本サイトでも同じように3つのレイヤをVBAで作成し実装していきます。
本ページではその中の「Softmax-with-Lossレイヤ」をVBAで実装していきます。
Softmax-with-Lossレイヤの機能
Softmax-with-Lossレイヤは
「入力された値にSoftmax関数を適用し活性化させる機能」と
「損失関数(交差エントロピー誤差)を求める機能」の2つの機能まとめたレイヤです。
Softmax関数の機能と交差エントロピー誤差を求める機能は「Functions」モジュールで実装しているので、このレイヤの順伝播ではそれら機能を呼び出すだけです。逆伝播も計算だけ見れば非常にシンプルな内容になので簡単に実装することができます。(その計算になる理由は難易度が少し高めです)
機械学習の流れ
今回作成していくニューラルネットワークをレイヤで表すと下図の通りです。
このレイヤの流れは以下のようになっています。(以下では上図の各レイヤを左から「Affine1」「ReLU」「Affine2」「Softmax-with-Loss」として説明していきます)
1. 入力値を「Affine1」に入れ、入力値と重みの総和を計算する。
2.「Affine1」の出力を「ReLU」に入れて活性化させる。
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」のパラメータ(重み/バイアス)が正解に近づくように徐々に更新されていきます。あとは損失関数をもとにループを止めれば学習終了といった流れになってます。
Softmax-with-Lossレイヤレイヤの実装
まずはクラスモジュールで「SoftmaxWithLoss_Layer」というモジュールを作成します。
このレイヤはインスタンスを作るわけでもなければ、値を保存する必要もないのですが、他のレイヤと揃えるためにクラスモジュールで実装していきます。
また「Option Base 1」を使い配列を「1」スタートにします。
これは要素数とインデックスを合わせ配列同士の計算をわかりやすくするためです。
以下はSoftmax-with-Lossレイヤの全コードです。
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 |
'VBA SoftmaxWithLoss_Layer(class) Option Explicit Option Base 1 Dim loss As Double Dim y() As Double Dim t() As Long '―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Public Function forward(ByRef x() As Double, ByRef input_t() As Long) As Double Dim i As Long ReDim t(UBound(input_t)) For i = 1 To UBound(input_t) t(i) = input_t(i) Next i ReDim y(UBound(x)) y = Functions.softmax(x) loss = Functions.cross_entropy_error(y, t) forward = loss End Function '―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Public Function backward(ByRef dout As Double) As Double() Dim dx() As Double Dim i As Long ReDim dx(UBound(t)) For i = 1 To UBound(t) dx(i) = (y(i) - t(i)) Next backward = dx End Function |
Softmax-with-LossレイヤはPythonで書くと以下のようなコードです。(書籍のコード)
引数や関数名などはできる限りは揃えているのでPythonコードが読める方は見比べてみて下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#Python SoftmaxWithLossレイヤ #softmax関数とcross_entropy_error関数のコードは割愛 class SoftmaxWithLoss: def __init__(self): self.loss = None # 損失 self.y = None # softmaxの出力 self.t = None # 教師データ(one-hot vector) def forward(self, x, t): self.t = t self.y = softmax(x) self.loss = cross_entropy_error(self.y, self.t) return self.loss def backward(self, dout=1): batch_size = self.t.shape[0] dx = (self.y - self.t) / batch_size return dx |
Pythonではミニバッチ処理を行っていますがVBAでは行わないため、
「backward」の「batch_size」はVBAでは出てきません。
以下では各関数で何を行っているかを解説していきます。
※この層では標準モジュールで作成した「Functions」の関数をいくつか呼び出して使用するので、「Functions」も合わせて読み進めて下さい。
Function forward
関数「forward」は名前のとおり順伝播を行う関数です。
このレイヤで行う順伝播は、Softmax関数の適用し交差エントロピー誤差を求めるまでです。
Softmax関数は入力される値の割合に応じて合計が「1」になるような値に変換するという関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
'VBA SoftmaxWithLoss_Layer(class) Dim loss As Double Dim y() As Double Dim t() As Long '―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― Public Function forward(ByRef x() As Double, ByRef input_t() As Long) As Double Dim i As Long ReDim t(UBound(input_t)) For i = 1 To UBound(input_t) t(i) = input_t(i) Next i ReDim y(UBound(x)) y = Functions.softmax(x) loss = Functions.cross_entropy_error(y, t) forward = loss End Function |
この関数の引数としては「x()」と「input_t()」があります。
「x()」は前の層(今回の場合はAffine2)の出力値
「input_t()」はone-hot表現に変換した入力画像データの正解ラベルです。
この関数では
①「input_t()」の値はそのままで、このモジュール内の配列「t()」に格納
②「x()」に対してSoftmax関数を適用し、「y()」に格納
③「y()」と「t()」を使って交差エントロピー誤差を求め、その値を返す
という処理を行っています。
交差エントロピー誤差は損失関数、いわゆるLoss値と呼ばれ学習が進むにつれてこの値が0に近づいていきます。学習精度の目安の1つとしてこの値は非常に重要な存在です。
Function backward
関数「backward」は名前のとおり逆伝播を行う関数です。
Softmax-with-Lossレイヤの逆伝播は順伝播時の入力値が0以下の場合は0を、0より大きい場合は前の層から逆伝播してきた「微分の値」をそのまま返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
'VBA SoftmaxWithLoss_Layer(class) Public Function backward(ByRef dout As Double) As Double() Dim dx() As Double Dim i As Long ReDim dx(UBound(t)) For i = 1 To UBound(t) dx(i) = (y(i) - t(i)) Next backward = dx End Function |
この関数の引数としては「dout」がありますが、コードの内容を見るとわかる通り「dout」は使いません。(Pythonコードでも同じように引数として「dout=1」を設定しているためここでも同じように表現しています)
そのため引数については無視して下さい。
このレイヤではSoftmax関数の出力値「y()」と正解ラベル「t()」の差を逆伝播します。
「y()」「t()」の値は順伝播で保存しているので、
そのまま各要素の差を求め1次元配列として返します。
たとえば「y(1,2,3)」「t(0,1,0)」の場合は
dx(1-0 , 2-1 , 3-0) → dx(1 , 1 , 3)が返り値となります。
この返り値が「微分の値」として逆伝播されていきます。
(なぜ引き算をすると「微分の値」になるのかの説明は書籍を参照下さい)
これにより2つのAffineレイヤが持つ重み/バイアスパラメータの値を更新することができます。
まとめ
ここでは「ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装」を参考にVBAでSoftmax-with-Lossレイヤの実装を行いました。
今回実装したSoftmax-with-Lossレイヤの機能をまとめると以下の通りです。
Function forward:順伝播(Softmax関数の適用し損失関数を求める)
Function backward:逆伝播(「微分の値」を求めて逆伝播)
Softmax-with-Lossレイヤは「Functions」モジュールで実装した
「Softmax関数」「交差エントロピー誤差」を呼び出しているだけのレイヤです。
順伝播でいうと1番最後で、逆伝播でいうと1番最初になる学習の折り返し地点となるレイヤです。
また最終的にニューラルネットワークが導き出した値を出力するレイヤでもあるので、どのような計算が行われているのかはしっかりと理解しておきましょう。
ニーラルネットワークはいくつものレイヤが紐づくことで学習が可能になります。
つまり本ページの内容だけではニューラルネットワーク内で何が行われているのか理解できません。
メインページよりいくつかのページと関連付けて見ることでようやく理解ができるようになります。
ニューラルネットワークについてはぜひ、時間をかけてじっくりと学んでください。
最終的なイメージが全くつかない人は、1度コピペでもいいのでニューラルネットワークをすべて構築して実際に機械学習を行ってみることをオススメします。
参考書籍